diff --git a/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java b/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java
index 872849143..34d99e63e 100644
--- a/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java
+++ b/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java
@@ -26,6 +26,7 @@ protected Injector getInjector() {
return new StatechartLanguageStandaloneSetup().createInjectorAndDoEMFRegistration();
}
+ @Override
protected Class extends AbstractAntlrParser> getParserClass() {
return CustomStatechartExpressionLanguageParser.class;
}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.classpath b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.classpath
new file mode 100644
index 000000000..81fe078c2
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.gitignore b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.gitignore
new file mode 100644
index 000000000..ae3c17260
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.project b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.project
new file mode 100644
index 000000000..7f5a510b2
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.project
@@ -0,0 +1,28 @@
+
+
+ hu.bme.mit.gamma.scxml.transformation.commandhandler
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.settings/org.eclipse.jdt.core.prefs b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..d4540a53f
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/META-INF/MANIFEST.MF b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..84827f024
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SCXML To Gamma Transformer Commandhandler
+Bundle-SymbolicName: hu.bme.mit.gamma.scxml.transformation.commandhandler;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: hu.bme.mit.gamma.scxml.transformation.commandhandler
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Require-Bundle: org.eclipse.core.commands,
+ ac.soton.scxml,
+ hu.bme.mit.gamma.scxml.transformation,
+ org.eclipse.core.resources,
+ org.eclipse.emf.common,
+ org.eclipse.emf.ecore,
+ org.eclipse.jface,
+ org.eclipse.ui.workbench,
+ org.eclipse.m2e.maven.runtime,
+ hu.bme.mit.gamma.statechart.model,
+ hu.bme.mit.gamma.statechart.language.ui,
+ org.eclipse.equinox.registry
+Export-Package: hu.bme.mit.gamma.scxml.transformation.commandhandler;uses:="org.eclipse.core.commands,hu.bme.mit.gamma.util,hu.bme.mit.gamma.statechart.util"
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/build.properties b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/build.properties
new file mode 100644
index 000000000..e9863e281
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/icons/gamma-icon-16.png b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/icons/gamma-icon-16.png
new file mode 100644
index 000000000..a880d2b84
Binary files /dev/null and b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/icons/gamma-icon-16.png differ
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/plugin.xml b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/plugin.xml
new file mode 100644
index 000000000..031ac3acd
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/plugin.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/src/hu/bme/mit/gamma/scxml/transformation/commandhandler/CommandHandler.java b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/src/hu/bme/mit/gamma/scxml/transformation/commandhandler/CommandHandler.java
new file mode 100644
index 000000000..0d99f7576
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/src/hu/bme/mit/gamma/scxml/transformation/commandhandler/CommandHandler.java
@@ -0,0 +1,117 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation.commandhandler;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+import ac.soton.scxml.ScxmlScxmlType;
+import hu.bme.mit.gamma.expression.model.ConstantDeclaration;
+import hu.bme.mit.gamma.scxml.transformation.CompositeTraceability;
+import hu.bme.mit.gamma.scxml.transformation.Namings;
+import hu.bme.mit.gamma.scxml.transformation.ScxmlToGammaCompositeTransformer;
+import hu.bme.mit.gamma.statechart.derivedfeatures.StatechartModelDerivedFeatures;
+import hu.bme.mit.gamma.statechart.interface_.Component;
+import hu.bme.mit.gamma.statechart.interface_.Interface;
+import hu.bme.mit.gamma.statechart.interface_.Package;
+import hu.bme.mit.gamma.statechart.language.ui.serializer.StatechartLanguageSerializer;
+import hu.bme.mit.gamma.statechart.util.StatechartUtil;
+import hu.bme.mit.gamma.util.FileUtil;
+import hu.bme.mit.gamma.util.GammaEcoreUtil;
+
+public class CommandHandler extends AbstractHandler {
+
+ protected final FileUtil fileUtil = FileUtil.INSTANCE;
+ protected final GammaEcoreUtil ecoreUtil = GammaEcoreUtil.INSTANCE;
+ protected final StatechartUtil statechartUtil = StatechartUtil.INSTANCE;
+ protected final Logger logger = Logger.getLogger("GammaLogger");
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ try {
+ ISelection sel = HandlerUtil.getActiveMenuSelection(event);
+ if (sel instanceof IStructuredSelection) {
+ IStructuredSelection selection = (IStructuredSelection) sel;
+ Object firstElement = selection.getFirstElement();
+ if (selection.size() == 1) {
+ if (firstElement != null && firstElement instanceof IFile) {
+ IFile file = (IFile) firstElement;
+ IContainer parentFolder = file.getParent();
+ String fileName = file.getName();
+ String extensionlessFileName = fileUtil.getExtensionlessName(fileName);
+ String parentPath = parentFolder.getFullPath().toString();
+ String path = file.getFullPath().toString();
+
+ // Model processing
+ ScxmlToGammaCompositeTransformer compositeTransformer = new ScxmlToGammaCompositeTransformer(path);
+ CompositeTraceability compositeTraceability = compositeTransformer.execute();
+
+ // Interfaces and type declarations have to be explicitly serialized in another package
+ List gammaInterfaces = new ArrayList(
+ compositeTraceability.getInterfaces());
+ gammaInterfaces.removeIf(i -> i == null);
+ ScxmlScxmlType scxmlRoot = compositeTraceability.getScxmlRoot();
+ Package gammaInterfacePackage = statechartUtil.createPackage(
+ Namings.getInterfacePackageName(scxmlRoot));
+ gammaInterfacePackage.getInterfaces().addAll(gammaInterfaces);
+
+ List gammaConstants = new ArrayList(
+ compositeTraceability.getConstantDeclarations());
+ gammaInterfacePackage.getConstantDeclarations().addAll(gammaConstants);
+
+ // Pack and serialize asynchronous component
+ Component rootComponent = compositeTraceability.getRootComponent();
+ Package gammaCompositePackage = statechartUtil.wrapIntoPackage(rootComponent);
+ Collection components = compositeTraceability.getComponents();
+ gammaCompositePackage.getComponents().addAll(components);
+ gammaCompositePackage.getImports()
+ .addAll(StatechartModelDerivedFeatures.getImportablePackages(gammaCompositePackage));
+ gammaCompositePackage.getImports().remove(gammaCompositePackage);
+
+ StatechartLanguageSerializer packageSerializer = new StatechartLanguageSerializer();
+ logger.log(Level.INFO, "Start serializing Gamma packages...");
+
+ // String declarationsPackageFileName = extensionlessFileName + "Declarations.gsm";
+ // ecoreUtil.normalSave(gammaInterfacePackage, parentPath,
+ // declarationsPackageFileName);
+ String declarationsPackageFileName = extensionlessFileName + "Declarations.gcd";
+ packageSerializer.serialize(gammaInterfacePackage, parentPath, declarationsPackageFileName);
+
+ // String compositePackageFileName = extensionlessFileName + ".gsm";
+ // ecoreUtil.normalSave(gammaCompositePackage, parentPath,
+ // compositePackageFileName);
+ String compositePackageFileName = extensionlessFileName + ".gcd";
+ packageSerializer.serialize(gammaCompositePackage, parentPath, compositePackageFileName);
+
+ logger.log(Level.INFO, "The SCXML - Gamma statechart transformation has finished.");
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.classpath b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.classpath
new file mode 100644
index 000000000..5cf859b24
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.classpath
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.gitignore b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.gitignore
new file mode 100644
index 000000000..ae3c17260
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.project b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.project
new file mode 100644
index 000000000..60a849793
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.project
@@ -0,0 +1,34 @@
+
+
+ hu.bme.mit.gamma.scxml.transformation
+
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.settings/org.eclipse.jdt.core.prefs b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..d4540a53f
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/META-INF/MANIFEST.MF b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..d0482add8
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SCXML To Gamma Transformation
+Bundle-SymbolicName: hu.bme.mit.gamma.scxml.transformation;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: hu.bme.mit.gamma.scxml.transformation
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Require-Bundle: ac.soton.scxml,
+ hu.bme.mit.gamma.statechart.model,
+ com.google.guava,
+ org.eclipse.xtext.xbase.lib,
+ org.eclipse.xtend.lib,
+ org.eclipse.xtend.lib.macro,
+ hu.bme.mit.gamma.expression.language,
+ com.google.inject;bundle-version="3.0.0",
+ hu.bme.mit.gamma.expression.model,
+ org.eclipse.xtext
+Export-Package: hu.bme.mit.gamma.scxml.transformation
+Import-Package: hu.bme.mit.gamma.statechart.language.parser,
+ org.antlr.runtime,
+ org.eclipse.xtext,
+ org.eclipse.xtext.nodemodel,
+ org.eclipse.xtext.parser,
+ org.eclipse.xtext.scoping,
+ org.eclipse.xtext.scoping.impl,
+ org.eclipse.xtext.util
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/build.properties b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/build.properties
new file mode 100644
index 000000000..d8e2f0e92
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/build.properties
@@ -0,0 +1,5 @@
+source.. = src/,\
+ xtend-gen/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AbstractTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AbstractTransformer.xtend
new file mode 100644
index 000000000..dbe2a473c
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AbstractTransformer.xtend
@@ -0,0 +1,46 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import hu.bme.mit.gamma.action.model.ActionModelFactory
+import hu.bme.mit.gamma.expression.model.ExpressionModelFactory
+import hu.bme.mit.gamma.expression.util.ExpressionTypeDeterminator2
+import hu.bme.mit.gamma.scxml.transformation.parse.ScxmlGammaExpressionLanguageLinker
+import hu.bme.mit.gamma.scxml.transformation.parse.ScxmlGammaExpressionLanguageParser
+import hu.bme.mit.gamma.statechart.composite.CompositeModelFactory
+import hu.bme.mit.gamma.statechart.interface_.InterfaceModelFactory
+import hu.bme.mit.gamma.statechart.phase.PhaseModelFactory
+import hu.bme.mit.gamma.statechart.statechart.StatechartModelFactory
+import hu.bme.mit.gamma.statechart.util.StatechartUtil
+import hu.bme.mit.gamma.util.GammaEcoreUtil
+import java.util.logging.Logger
+
+abstract class AbstractTransformer {
+
+ protected final extension GammaEcoreUtil ecoreUtil = GammaEcoreUtil.INSTANCE
+ protected final extension StatechartUtil statechartUtil = StatechartUtil.INSTANCE
+// protected final extension ActionUtil actionUtil = ActionUtil.INSTANCE
+// protected final extension ExpressionUtil expressionUtil = ExpressionUtil.INSTANCE
+
+ protected final extension InterfaceModelFactory interfaceModelFactory = InterfaceModelFactory.eINSTANCE
+ protected final extension CompositeModelFactory compositeModelFactory = CompositeModelFactory.eINSTANCE
+ protected final extension StatechartModelFactory statechartModelFactory = StatechartModelFactory.eINSTANCE
+ protected final extension PhaseModelFactory phaseModelFactory = PhaseModelFactory.eINSTANCE
+ protected final extension ActionModelFactory actionModelFactory = ActionModelFactory.eINSTANCE
+ protected final extension ExpressionModelFactory expressionModelFactory = ExpressionModelFactory.eINSTANCE
+
+ protected final ScxmlGammaExpressionLanguageParser expressionLanguageParser = new ScxmlGammaExpressionLanguageParser()
+ protected final ScxmlGammaExpressionLanguageLinker expressionLanguageLinker = new ScxmlGammaExpressionLanguageLinker()
+ protected final ExpressionTypeDeterminator2 expressionTypeDeterminator = ExpressionTypeDeterminator2.INSTANCE
+
+ protected final Logger logger = Logger.getLogger("GammaLogger")
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ActionTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ActionTransformer.xtend
new file mode 100644
index 000000000..d9501efca
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ActionTransformer.xtend
@@ -0,0 +1,432 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import ac.soton.scxml.ScxmlAssignType
+import ac.soton.scxml.ScxmlIfType
+import ac.soton.scxml.ScxmlInvokeType
+import ac.soton.scxml.ScxmlOnentryType
+import ac.soton.scxml.ScxmlOnexitType
+import ac.soton.scxml.ScxmlParamType
+import ac.soton.scxml.ScxmlRaiseType
+import ac.soton.scxml.ScxmlSendType
+import ac.soton.scxml.ScxmlStateType
+import hu.bme.mit.gamma.action.model.Action
+import hu.bme.mit.gamma.expression.model.Expression
+import hu.bme.mit.gamma.expression.model.VariableDeclaration
+import hu.bme.mit.gamma.statechart.composite.ComponentInstance
+import hu.bme.mit.gamma.statechart.interface_.Interface
+import hu.bme.mit.gamma.statechart.interface_.Port
+import hu.bme.mit.gamma.statechart.interface_.RealizationMode
+import hu.bme.mit.gamma.statechart.phase.History
+import java.util.function.Function
+import java.util.logging.Level
+import org.eclipse.emf.ecore.EObject
+
+import static ac.soton.scxml.ScxmlModelDerivedFeatures.*
+
+class ActionTransformer extends AtomicElementTransformer {
+
+ protected final extension PortTransformer portTransformer
+ protected final extension InterfaceTransformer interfaceTransformer
+ protected final extension EventTransformer eventTransformer
+ protected final extension ScxmlGammaExpressionTransformer expressionTransformer
+
+ new(StatechartTraceability traceability) {
+ super(traceability)
+
+ this.portTransformer = new PortTransformer(traceability)
+ this.interfaceTransformer = new InterfaceTransformer(traceability)
+ this.eventTransformer = new EventTransformer(traceability)
+ this.expressionTransformer = new ScxmlGammaExpressionTransformer(traceability)
+ }
+
+ def Action transformOnentry(ScxmlOnentryType scxmlOnentry) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlOnentry + ")")
+
+ val scxmlActions = getOnentryActions(scxmlOnentry)
+ if (!scxmlActions.nullOrEmpty) {
+ val gammaEntryAction = scxmlActions.transformBlock;
+ return gammaEntryAction
+ }
+ return null
+ }
+
+ def Action transformOnexit(ScxmlOnexitType scxmlOnexit) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlOnexit + ")")
+
+ val scxmlActions = getOnexitActions(scxmlOnexit)
+ if (!scxmlActions.nullOrEmpty) {
+ val gammaExitAction = scxmlActions.transformBlock;
+ return gammaExitAction
+ }
+ return null
+ }
+
+ def dispatch Action transformAction(ScxmlAssignType scxmlAssign) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlAssign + ")")
+
+ val varLoc = scxmlAssign.location
+ val variable = traceability.getVariable(varLoc)
+
+ val expr = scxmlAssign.expr
+ if (expr !== null) {
+ val expression = expressionLanguageParser.preprocessAndParse(
+ expr,
+ expressionLanguageLinker.getLinker(traceability)
+ )
+ val gammaAssign = createAssignment(variable as VariableDeclaration, expression)
+ return gammaAssign
+ }
+
+ // TODO Assignment by element's child content if expr is not present.
+ val gammaAssign = createEmptyStatement
+
+ return gammaAssign
+ }
+
+ def dispatch Action transformAction(ScxmlRaiseType scxmlRaise) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlRaise + ")")
+
+ val eventString = scxmlRaise.event
+ val tokens = eventString.split("\\.")
+ if (tokens.size < 1 || tokens.size > 3) {
+ throw new IllegalArgumentException(
+ "Event descriptor " + eventString + " does not contain exactly 1, 2 or 3 dot separated tokens."
+ )
+ }
+
+ var gammaInterface = null as Interface
+ var gammaPort = null as Port
+ var isDefault = false
+
+ if (tokens.size == 1) {
+ isDefault = true
+ gammaInterface = getOrCreateDefaultInterface()
+ gammaPort = getOrCreateDefaultPort()
+ } else {
+ val interfaceName = tokens.get(tokens.size - 2)
+ gammaInterface = getOrTransformInterfaceByName(interfaceName)
+
+ if (tokens.size >= 3) {
+ val portName = tokens.head
+ gammaPort = getOrTransformPortByName(gammaInterface, portName, RealizationMode.PROVIDED)
+ } else {
+ isDefault = true
+ gammaPort = getOrTransformDefaultInterfacePort(gammaInterface)
+ }
+ }
+
+ val eventName = tokens.lastOrNull
+
+ // If a port is specified, the event will be an out event on an interface
+ // realized in provided mode by the port receiving the event.
+ // In the case of default interfaces and ports, realization mode is also provided,
+ // but the trigger events are internal.
+ val gammaEvent = if (isDefault) {
+ getOrTransformInternalEvent(gammaInterface, eventName)
+ } else {
+ getOrTransformOutEvent(gammaInterface, eventName)
+ }
+
+ val gammaRaise = createRaiseEventAction(gammaPort, gammaEvent, newArrayList)
+ return gammaRaise
+ }
+
+ // TODO actions
+ // TODO Is traceability entry needed?
+ def dispatch Action transformAction(ScxmlSendType scxmlSend) {
+ logger.info("Transforming element (" + scxmlSend + ")")
+
+ // TODO Check nulls / empty substrings
+ val eventString = scxmlSend.event
+ val targetInterfacePortString = scxmlSend.targetexpr
+ val eventTargetString = targetInterfacePortString + "." + eventString
+
+ val tokens = eventTargetString.split("\\.")
+ if (tokens.size < 2 || tokens.size > 3) {
+ throw new IllegalArgumentException(
+ "Event descriptor " + eventString + " does not contain exactly 2 or 3 dot separated tokens."
+ )
+ }
+
+ var gammaInterface = null as Interface
+ var gammaPort = null as Port
+
+ val interfaceName = tokens.get(tokens.size - 2)
+ gammaInterface = getOrTransformInterfaceByName(interfaceName)
+
+ if (tokens.size >= 3) {
+ val portName = tokens.head
+ gammaPort = getOrTransformPortByName(gammaInterface, portName, RealizationMode.PROVIDED)
+ } else {
+ gammaPort = getOrTransformDefaultInterfacePort(gammaInterface)
+ }
+
+ val eventName = tokens.lastOrNull
+ val gammaEvent = getOrTransformOutEvent(gammaInterface, eventName)
+
+ // Event elements
+ // TODO Refactor, extract param and argument transformer method
+ // TODO Named parameter support
+ // In the Gamma event, the order of the parameters and arguments matters, they are not named.
+ // Currently, every event parameter of the same event has to be specified, in exact order.
+ val gammaArgumentNames = newArrayList
+ val gammaArgumentExpressions = newArrayList
+ val arguments = scxmlSend.getContentsOfType(ScxmlParamType)
+ for (argument : arguments) {
+ val argName = argument.name
+ val argExpr = argument.expr
+
+ if (argExpr !== null) {
+ val argExpression = expressionLanguageParser.preprocessAndParse(
+ argExpr,
+ expressionLanguageLinker.getLinker(traceability)
+ )
+ gammaArgumentNames += argName
+ gammaArgumentExpressions += argExpression
+
+ val argType = expressionTypeDeterminator.getType(argExpression)
+
+ // TODO Traceability; event+param -> event+param
+ // existsParameter(event, param)
+ // getOrCreateParameter(event, param): creates if not exists, then returns
+ if (!gammaEvent.parameterDeclarations.exists[it.name == argName]) {
+ val gammaEventParameterDeclaration = argType.createParameterDeclaration(argName)
+ gammaEvent.parameterDeclarations += gammaEventParameterDeclaration
+
+ // TODO Refactor into getOrCreate event parameter method
+ if (!traceability.containsEventParameter(argName)) {
+ traceability.putEventParameter(argName, gammaEventParameterDeclaration)
+ }
+ }
+ }
+ }
+
+ // TODO Reorder event arguments according to parameter names in event
+ // TODO Mix named and indexed parameter passing
+ // TODO Name -> Find argument by name
+ // No name -> get index of element in parent's children list
+ // Parametric(parameterized)Element-ekre meg lehet csinálni
+ val orderedGammaArguments = newArrayList
+ for (eventParameter : gammaEvent.parameterDeclarations) {
+ val index = gammaArgumentNames.indexOf(eventParameter.name)
+ /*if (index < 0) {
+ val index = ecoreUtil.getIndex(0)
+ }*/
+ val argumentExpression = gammaArgumentExpressions.get(index)
+ orderedGammaArguments += argumentExpression
+ }
+
+ val gammaRaise = createRaiseEventAction(gammaPort, gammaEvent, orderedGammaArguments)
+ return gammaRaise
+ }
+
+ def dispatch Action transformAction(ScxmlIfType scxmlIf) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlIf + ")")
+
+ val gammaIf = createIfStatement
+ val cond = scxmlIf.cond
+ if (!cond.nullOrEmpty) {
+ val condExpression = expressionLanguageParser.preprocessAndParse(
+ cond,
+ expressionLanguageLinker.getLinker(traceability)
+ )
+
+ val thenStatements = getIfThenActions(scxmlIf);
+ val gammaThenStatements = thenStatements.transformBlock
+
+ val gammaConditional = createBranch
+ gammaConditional.guard = condExpression
+ gammaConditional.action = gammaThenStatements
+ gammaIf.conditionals += gammaConditional
+ }
+ return gammaIf
+ }
+
+ // TODO check return type (Action vs other)
+ // TODO use global section of traceability object to store component types, interfaces etc.
+ def dispatch Action transformAction(ScxmlInvokeType scxmlInvoke) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlInvoke + ")")
+
+ // TODO invoked statechart type transformation should already be done at this point,
+ // or be done lazily by the get call. Either way, ActionTransformer is
+ // not responsible for invoking contained statechart type transformation.
+ val invokedTypeSourceURI = scxmlInvoke.src
+
+ // TODO get invoked type traceability
+ // TODO check component type
+ val invokedTypeTraceability = traceability.compositeTraceability.getTraceability(invokedTypeSourceURI)
+ val gammaSubcomponentType = invokedTypeTraceability.statechart /*adapter as AsynchronousComponent*/
+
+ val gammaSubcomponent = gammaSubcomponentType.instantiateComponent
+ gammaSubcomponent.name = scxmlInvoke.id
+
+ val missionPhaseStateAnnotation = createMissionPhaseStateAnnotation
+
+ val invokeParameters = scxmlInvoke.param
+
+ // TODO Pass arguments by subcomponent parameter names
+ val arguments = invokeParameters.filter[it.name == "_argument"]
+ /* arguments.forEach [ it |
+ * val gammaArgument = it.expr.transformArgument
+ * gammaSubcomponent.arguments += gammaArgument
+ * ] */
+ val parameters = gammaSubcomponentType.parameterDeclarations
+ for (parameter : parameters) {
+ val argument = arguments.findFirst[it.name == parameter.name]
+ val gammaArgument = argument.expr.transformArgument
+ gammaSubcomponent.arguments += gammaArgument
+ }
+
+ missionPhaseStateAnnotation.component = gammaSubcomponent
+
+ val portBindings = invokeParameters.filter[it.name == "_port_binding"]
+ portBindings.forEach [ it |
+ val gammaPortBinding = transformPortBinding(gammaSubcomponent, it.expr)
+ missionPhaseStateAnnotation.portBindings += gammaPortBinding
+ ]
+
+ val variableBindings = invokeParameters.filter[it.name == "_variable_binding"]
+ variableBindings.forEach [ it |
+ val gammaVariableBinding = transformVariableBinding(gammaSubcomponent, it.expr)
+ missionPhaseStateAnnotation.variableBindings += gammaVariableBinding
+ ]
+
+ val history = invokeParameters.findFirst[it.name == "_history"]
+ if (history !== null) {
+ val gammaHistory = switch history.expr {
+ case "shallow": History.SHALLOW_HISTORY
+ case "deep": History.DEEP_HISTORY
+ case "no",
+ default: History.NO_HISTORY
+ }
+ missionPhaseStateAnnotation.history = gammaHistory
+ }
+
+ // TODO State, transition
+ // Put transformed invoke to stable target state
+ // TODO Assignment by child content if expr is not present
+ val parentState = scxmlInvoke.getContainerOfType(ScxmlStateType)
+ val gammaState = traceability.getState(parentState)
+ gammaState.annotations += missionPhaseStateAnnotation
+
+ val gammaEmptyAction = createEmptyStatement
+ return gammaEmptyAction
+ }
+
+ def Action transformBlock(Iterable extends EObject> actions) {
+ if (actions.empty) {
+ return createBlock
+ }
+
+ val gammaActions = actions.map[it.transformAction].toList
+ val gammaBlock = gammaActions.wrap
+
+ return gammaBlock
+ }
+
+ // TODO Connect statechart arguments with statechart parameters by name
+ private def Expression transformArgument(String argumentSpec) {
+ val argumentString = argumentSpec.trim
+ val tokens = argumentString.split("\\.|\\s*\\=\\s*")
+ if (tokens.size != 2) {
+ throw new IllegalArgumentException(
+ "Argument descriptor " + argumentString + " does not contain exactly 2 dot separated tokens."
+ )
+ }
+ // TODO Trim tokens
+ // TODO Use parameter name to determine the order of statechart arguments
+ val targetParameterName = tokens.get(0)
+ val scxmlArgumentExpression = tokens.get(1)
+
+ // TODO Expression parsing
+ val gammaArgumentExpression = expressionLanguageParser.preprocessAndParse(
+ scxmlArgumentExpression,
+ expressionLanguageLinker.getLinker(traceability)
+ )
+
+ return gammaArgumentExpression
+ }
+
+ // TODO Reuse code - port/variable bindings
+ // TODO? Pattern, Matcher, regexes etc.
+ private def transformPortBinding(ComponentInstance instance, String portBindingSpec) {
+ val bindingString = portBindingSpec.trim
+ val tokens = bindingString.split("\\.|\\s*\\-\\s*")
+ if (tokens.size != 3) {
+ throw new IllegalArgumentException(
+ "Binding descriptor " + bindingString + " does not contain exactly 3 dot separated tokens."
+ )
+ }
+ // TODO Trim tokens
+ val sourcePortName = tokens.get(0)
+ val targetInstanceName = tokens.get(1)
+ val targetPortName = tokens.get(2)
+
+ // TODO Throw exception if port does not exist or ambiguous
+ val gammaSourcePort = traceability.getPort(sourcePortName)
+ val gammaInstancePortReference = createInstancePortReference(instance, targetInstanceName, targetPortName)
+
+ val gammaPortBinding = createPortBinding(gammaSourcePort, gammaInstancePortReference)
+ return gammaPortBinding
+ }
+
+ private def createInstancePortReference(ComponentInstance instance, String instanceName, String scxmlPortName) {
+ // val instance = traceability.compositeTraceability.getComponentInstance(instanceName)
+ // TODO Make it more performant to get statechart traceability
+ // by instance invokeId or source URI.
+ val statechartTraceability = traceability.compositeTraceability.getTraceabilityById(instanceName)
+ val port = statechartTraceability.getPort(scxmlPortName)
+
+ val instancePortReference = createInstancePortReference(instance, port)
+ return instancePortReference
+ }
+
+ private def transformVariableBinding(ComponentInstance instance, String variableBindingSpec) {
+ val bindingString = variableBindingSpec.trim
+ val tokens = bindingString.split("\\.|\\s*\\-\\s*")
+ if (tokens.size != 3) {
+ throw new IllegalArgumentException(
+ "Binding descriptor " + bindingString + " does not contain exactly 3 dot separated tokens."
+ )
+ }
+
+ val sourceVariableName = tokens.get(0)
+ val targetInstanceName = tokens.get(1)
+ val targetVariableName = tokens.get(2)
+
+ // TODO Throw exception if port does not exist or ambiguous
+ val gammaSourceVariable = traceability.getVariable(sourceVariableName)
+ val gammaInstanceVariableReference = createInstanceVariableReference(instance, targetInstanceName,
+ targetVariableName)
+
+ val gammaVariableBinding = createVariableBinding
+ gammaVariableBinding.instanceVariableReference = gammaInstanceVariableReference
+ gammaVariableBinding.statechartVariable = gammaSourceVariable
+ return gammaVariableBinding
+ }
+
+ private def createInstanceVariableReference(ComponentInstance instance, String instanceName,
+ String scxmlVariableName) {
+ // val instance = traceability.compositeTraceability.getComponentInstance(instanceName)
+ // TODO Make it more performant to get statechart traceability
+ // by instance invokeId or source URI.
+ val statechartTraceability = traceability.compositeTraceability.getTraceabilityById(instanceName)
+ val variable = statechartTraceability.getVariable(scxmlVariableName)
+
+ val instanceVariableReference = createInstanceVariableReference
+ instanceVariableReference.instance = instance
+ instanceVariableReference.variable = variable
+ return instanceVariableReference
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AtomicElementTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AtomicElementTransformer.xtend
new file mode 100644
index 000000000..2be9e9f9c
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AtomicElementTransformer.xtend
@@ -0,0 +1,20 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+abstract class AtomicElementTransformer extends AbstractTransformer {
+
+ protected final StatechartTraceability traceability
+
+ new(StatechartTraceability traceability) {
+ this.traceability = traceability
+ }
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeElementTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeElementTransformer.xtend
new file mode 100644
index 000000000..54e898a30
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeElementTransformer.xtend
@@ -0,0 +1,20 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+abstract class CompositeElementTransformer extends AbstractTransformer {
+
+ protected final CompositeTraceability compositeTraceability
+
+ new(CompositeTraceability compositeTraceability) {
+ this.compositeTraceability = compositeTraceability
+ }
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeTraceability.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeTraceability.xtend
new file mode 100644
index 000000000..d530b3653
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeTraceability.xtend
@@ -0,0 +1,221 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import ac.soton.scxml.ScxmlInvokeType
+import ac.soton.scxml.ScxmlScxmlType
+import hu.bme.mit.gamma.expression.model.ConstantDeclaration
+import hu.bme.mit.gamma.expression.model.ParameterDeclaration
+import hu.bme.mit.gamma.statechart.composite.AsynchronousComponentInstance
+import hu.bme.mit.gamma.statechart.interface_.Component
+import hu.bme.mit.gamma.statechart.interface_.Event
+import hu.bme.mit.gamma.statechart.interface_.Interface
+import hu.bme.mit.gamma.statechart.interface_.Port
+import java.util.List
+import java.util.Map
+
+import static com.google.common.base.Preconditions.checkNotNull
+
+class CompositeTraceability {
+
+ // URI of the root SCXML statechart model to transform
+ protected final String rootFileURI
+
+ // Resulting root component
+ protected Component rootComponent
+
+ // Default message queue capacity
+ protected ConstantDeclaration queueCapacity
+
+ // TODO Add interfaces, events, ports, bindings and channels
+ // (Needed for the declarations package and serialization of the components package.)
+ protected final Map statecharts = newHashMap
+ protected final Map instances = newHashMap
+ protected final Map ports = newHashMap
+
+ // Global mappings: Interfaces, events, declarations
+ protected final Map interfaces = newHashMap
+ protected final Map, Event> internalEvents = newHashMap
+ protected final Map, Event> inEvents = newHashMap
+ protected final Map, Event> outEvents = newHashMap
+ protected final Map allEvents = newHashMap
+ protected final Map allEventParameters = newHashMap
+
+ // - Asynchronous Component
+ new(String rootFileURI) {
+ this.rootFileURI = rootFileURI
+ }
+
+ // Creates a traceability object for the Gamma transformation of
+ // the SCXML statechart model found at rootFileURI.
+ def createStatechartTraceability(String rootFileURI) {
+ return new StatechartTraceability(
+ this,
+ rootFileURI,
+ interfaces,
+ queueCapacity,
+ internalEvents,
+ inEvents,
+ outEvents,
+ allEvents,
+ allEventParameters
+ )
+ }
+
+ // TODO Get from statecharts or instances map by rootFileURI string, if needed
+ def getScxmlRoot() {
+ val rootTraceability = getTraceability(rootFileURI)
+ val rootScxmlElement = rootTraceability.scxmlRoot
+ checkNotNull(rootScxmlElement)
+ return rootScxmlElement
+ }
+
+ def getRootComponent() {
+ val rootTraceability = getTraceability(rootFileURI)
+
+ // TODO Which kind of root Gamma component should we return? statechart / adapter / other?
+ val rootComponent = rootTraceability.statechart
+ checkNotNull(rootComponent)
+ return rootComponent
+ }
+
+ def setRootComponent(Component rootComponent) {
+ this.rootComponent = rootComponent
+ }
+
+ def getQueueCapacityDeclaration() {
+ return queueCapacity
+ }
+
+ def setQueueCapacity(ConstantDeclaration capacity) {
+ this.queueCapacity = capacity
+ }
+
+ // String URI of the SCXML statechart definition source - Statechart Traceability
+ def putTraceability(String statechartUri, StatechartTraceability statechartTraceability) {
+ checkNotNull(statechartUri)
+ checkNotNull(statechartTraceability)
+ statecharts += statechartUri -> statechartTraceability
+ }
+
+ def getTraceability(String statechartUri) {
+ checkNotNull(statechartUri)
+ val statechartTraceability = statecharts.get(statechartUri)
+ checkNotNull(statechartTraceability)
+ return statechartTraceability
+ }
+
+ // TODO Simplify map structure and getter.
+ def getTraceabilityById(String scxmlInvokeId) {
+ checkNotNull(scxmlInvokeId)
+ val invoke = instances.keySet.findFirst[invoke|invoke.id == scxmlInvokeId]
+ checkNotNull(invoke)
+ val statechartTraceability = statecharts.filter[source, _|source == invoke.src].values.head
+ checkNotNull(statechartTraceability)
+ return statechartTraceability
+ }
+
+ def getTraceability(ScxmlScxmlType scxmlRoot) {
+ checkNotNull(scxmlRoot)
+ val statechartTraceability = statecharts.values.findFirst[it.scxmlRoot === scxmlRoot]
+ checkNotNull(statechartTraceability)
+ return statechartTraceability
+ }
+
+ def containsTraceability(String statechartUri) {
+ checkNotNull(statechartUri)
+ return statecharts.containsKey(statechartUri)
+ }
+
+ // - Asynchronous Component Instance
+ def putComponentInstance(ScxmlInvokeType scxmlInvoke, AsynchronousComponentInstance instance) {
+ checkNotNull(scxmlInvoke)
+ checkNotNull(instance)
+ instances += scxmlInvoke -> instance
+ }
+
+ def getComponentInstance(ScxmlInvokeType scxmlInvoke) {
+ checkNotNull(scxmlInvoke)
+ val instance = instances.get(scxmlInvoke)
+ checkNotNull(instance)
+ return instance
+ }
+
+ def getComponentInstance(String scxmlInvokeId) {
+ checkNotNull(scxmlInvokeId)
+ val instance = instances.filter[invoke, _|invoke.id == scxmlInvokeId].values.head
+ checkNotNull(instance)
+ return instance
+ }
+
+ // Ports by string identifier (for event strings like 'port.interface.event)
+ def putPort(String scxmlPortName, Port gammaPort) {
+ checkNotNull(scxmlPortName)
+ checkNotNull(gammaPort)
+ ports += scxmlPortName -> gammaPort
+ }
+
+ def getPort(String scxmlPortName) {
+ checkNotNull(scxmlPortName)
+ val gammaPort = ports.get(scxmlPortName)
+ checkNotNull(gammaPort)
+ return gammaPort
+ }
+
+ def containsPort(String scxmlPortName) {
+ checkNotNull(scxmlPortName)
+ return ports.containsKey(scxmlPortName)
+ }
+
+ //
+
+ def getInEvents() {
+ return inEvents
+ }
+
+ def getOutEvents() {
+ return outEvents
+ }
+
+ def getInstances() {
+ return instances
+ }
+
+ def getStatecharts() {
+ return statecharts.values.toList
+ }
+
+ //
+
+ def getConstantDeclarations() {
+ return List.of(queueCapacity)
+ }
+
+ def getInterfaces() {
+ val statechartTraceabilities = statecharts.values
+ return (
+ interfaces.values +
+ statechartTraceabilities.map[it.defaultInterface] +
+ statechartTraceabilities.map[it.defaultInterfacePorts.keySet].flatten
+ ).toSet
+ }
+
+ def getComponents() {
+ val statechartTraceabilities = statecharts.values
+ return (
+ instances.values.map[it.getType] +
+ /* TODO Serialize async adapters */
+ /* statechartTraceabilities.map[it.getAdapter] + */
+ statechartTraceabilities.map[it.getStatechart]
+ ).toSet
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/DataTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/DataTransformer.xtend
new file mode 100644
index 000000000..91e0a55ea
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/DataTransformer.xtend
@@ -0,0 +1,55 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import ac.soton.scxml.ScxmlDataType
+import hu.bme.mit.gamma.expression.model.ParameterDeclaration
+import hu.bme.mit.gamma.expression.model.VariableDeclaration
+import java.util.logging.Level
+
+class DataTransformer extends AtomicElementTransformer {
+
+ new(StatechartTraceability traceability) {
+ super(traceability)
+ }
+
+ // Note: the type of the parameter declaration is inferred from the provided expr of the element.
+ def ParameterDeclaration transformParameter(ScxmlDataType scxmlData) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlData + ")")
+
+ val id = scxmlData.id
+ val expr = scxmlData.expr
+ val expression = expressionLanguageParser.parse(expr, traceability.variables)
+ val type = expressionTypeDeterminator.getType(expression)
+
+ val gammaDeclaration = createParameterDeclaration(type, id);
+
+ traceability.put(scxmlData, gammaDeclaration)
+
+ return gammaDeclaration
+ }
+
+ def VariableDeclaration transformVariable(ScxmlDataType scxmlData) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlData + ")")
+
+ val id = scxmlData.id
+ val expr = scxmlData.expr
+ val expression = expressionLanguageParser.parse(expr, traceability.variables)
+ val type = expressionTypeDeterminator.getType(expression)
+
+ val gammaDeclaration = createVariableDeclaration(type, id, expression);
+
+ traceability.put(scxmlData, gammaDeclaration)
+
+ return gammaDeclaration
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/EventTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/EventTransformer.xtend
new file mode 100644
index 000000000..3d823e7ac
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/EventTransformer.xtend
@@ -0,0 +1,83 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import hu.bme.mit.gamma.statechart.interface_.EventDirection
+import hu.bme.mit.gamma.statechart.interface_.Interface
+
+import static hu.bme.mit.gamma.scxml.transformation.Namings.*
+
+class EventTransformer extends AtomicElementTransformer {
+ new(StatechartTraceability traceability) {
+ super(traceability)
+ }
+
+ def getOrTransformInternalEvent(Interface gammaInterface, String eventName) {
+ if (traceability.containsInternalEvent(gammaInterface -> eventName)) {
+ return traceability.getInternalEvent(gammaInterface -> eventName)
+ }
+ else {
+ return transformInternalEvent(gammaInterface, eventName)
+ }
+ }
+
+ protected def transformInternalEvent(Interface gammaInterface, String eventName) {
+ val gammaEvent = transformEvent(gammaInterface, EventDirection.INTERNAL, eventName)
+ gammaEvent.name = getInternalEventName(eventName)
+ traceability.putInternalEvent(gammaInterface -> eventName, gammaEvent)
+ return gammaEvent
+ }
+
+ def getOrTransformInEvent(Interface gammaInterface, String eventName) {
+ if (traceability.containsInEvent(gammaInterface -> eventName)) {
+ return traceability.getInEvent(gammaInterface -> eventName)
+ }
+ else {
+ return transformInEvent(gammaInterface, eventName)
+ }
+ }
+
+ protected def transformInEvent(Interface gammaInterface, String eventName) {
+ val gammaEvent = transformEvent(gammaInterface, EventDirection.IN, eventName)
+ gammaEvent.name = getInEventName(eventName)
+ traceability.putInEvent(gammaInterface -> eventName, gammaEvent)
+ return gammaEvent
+ }
+
+ def getOrTransformOutEvent(Interface gammaInterface, String eventName) {
+ if (traceability.containsOutEvent(gammaInterface -> eventName)) {
+ return traceability.getOutEvent(gammaInterface -> eventName)
+ }
+ else {
+ return transformOutEvent(gammaInterface, eventName)
+ }
+ }
+
+ protected def transformOutEvent(Interface gammaInterface, String eventName) {
+ val gammaEvent = transformEvent(gammaInterface, EventDirection.OUT, eventName)
+ gammaEvent.name = getOutEventName(eventName)
+ traceability.putOutEvent(gammaInterface -> eventName, gammaEvent)
+ return gammaEvent
+ }
+
+ // This method assumes that the transformation of the interface
+ // of the respective event has already happened when we transform the event.
+ private def transformEvent(Interface gammaInterface, EventDirection direction, String eventName) {
+ val gammaEvent = createEvent
+ val gammaEventDeclaration = createEventDeclaration
+ gammaEventDeclaration.event = gammaEvent
+ gammaEventDeclaration.direction = direction
+
+ gammaInterface.events += gammaEventDeclaration
+ return gammaEvent
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/InterfaceTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/InterfaceTransformer.xtend
new file mode 100644
index 000000000..41b325c3c
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/InterfaceTransformer.xtend
@@ -0,0 +1,61 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import static com.google.common.base.Preconditions.checkState
+import static hu.bme.mit.gamma.scxml.transformation.Namings.*
+
+class InterfaceTransformer extends AtomicElementTransformer {
+ new(StatechartTraceability traceability) {
+ super(traceability)
+ }
+
+ def getOrCreateDefaultInterface() {
+ val defaultInterface = traceability.getDefaultInterface
+ if (defaultInterface !== null) {
+ return defaultInterface
+ }
+ else {
+ val gammaInterface = createDefaultInterface
+ traceability.setDefaultInterface(gammaInterface)
+ return gammaInterface
+ }
+ }
+
+ protected def createDefaultInterface() {
+ val defaultInterface = createInterface
+ val defaultInterfaceName = getDefaultInterfaceName(traceability.getTypeName())
+ defaultInterface.name = defaultInterfaceName
+
+ return defaultInterface
+ }
+
+ def getOrTransformInterfaceByName(String interfaceName) {
+ checkState(interfaceName !== null)
+ if (traceability.containsInterface(interfaceName)) {
+ return traceability.getInterface(interfaceName)
+ }
+ else {
+ val gammaInterface = transformInterfaceByName(interfaceName)
+ traceability.putInterface(interfaceName, gammaInterface)
+ return gammaInterface
+ }
+ }
+
+ protected def transformInterfaceByName(String interfaceName) {
+ val gammaInterface = createInterface
+ val gammaInterfaceName = getInterfaceName(interfaceName)
+ gammaInterface.name = gammaInterfaceName
+
+ return gammaInterface
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/Namings.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/Namings.xtend
new file mode 100644
index 000000000..8c418ff9e
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/Namings.xtend
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import ac.soton.scxml.ScxmlFinalType
+import ac.soton.scxml.ScxmlHistoryType
+import ac.soton.scxml.ScxmlParallelType
+import ac.soton.scxml.ScxmlScxmlType
+import ac.soton.scxml.ScxmlStateType
+
+class Namings {
+
+ // TODO check default names differ from user defined interface and port names at the end of the transformation
+ def static String getDefaultPortName(String instanceName) '''«instanceName»_DefaultPort'''
+ def static String getDefaultInterfaceName(String instanceName) '''«instanceName»_DefaultInterface'''
+ def static String getDefaultInterfacePortName(String scxmlInterfaceName) '''«scxmlInterfaceName»_DefaultPort'''
+ def static String getInterfaceName(String scxmlInterfaceName) '''«scxmlInterfaceName»'''
+ def static String getPortName(String scxmlPortName) '''«scxmlPortName»'''
+
+ def static String getInternalEventName(String scxmlEventName) '''«scxmlEventName»'''
+ def static String getInEventName(String scxmlEventName) '''«scxmlEventName»'''
+ def static String getOutEventName(String scxmlEventName) '''«scxmlEventName»'''
+
+ def static String getAdapterName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»Adapter'''
+ def static String getInternalEventQueueName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»InternalEventQueue'''
+ def static String getExternalEventQueueName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»ExternalEventQueue'''
+
+ def static String getInterfacePackageName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name.toLowerCase»_interfaces'''
+ def static String getCompositeStatechartName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»'''
+ def static String getStatechartName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»'''
+ def static String getRegionName(String scxmlElementName) '''«scxmlElementName»Region'''
+
+ def static String getParallelName(ScxmlParallelType scxmlParallel) '''«scxmlParallel.id»'''
+ def static String getStateName(ScxmlStateType scxmlState) '''«scxmlState.id»'''
+ def static String getFinalName(ScxmlFinalType scxmlFinal) '''«scxmlFinal.id»'''
+
+ def static String getInitialName(ScxmlStateType scxmlParentState) '''«scxmlParentState.id»Initial'''
+ def static String getInitialName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»Initial'''
+ def static String getShallowHistoryName(ScxmlHistoryType scxmlHistoryState) '''«scxmlHistoryState.id»ShallowHistory'''
+ def static String getDeepHistoryName(ScxmlHistoryType scxmlHistoryState) '''«scxmlHistoryState.id»DeepHistory'''
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/PortTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/PortTransformer.xtend
new file mode 100644
index 000000000..58414fc48
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/PortTransformer.xtend
@@ -0,0 +1,126 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import hu.bme.mit.gamma.statechart.interface_.Interface
+import hu.bme.mit.gamma.statechart.interface_.RealizationMode
+
+import static com.google.common.base.Preconditions.checkState
+import static hu.bme.mit.gamma.scxml.transformation.Namings.*
+
+class PortTransformer extends AtomicElementTransformer {
+
+ protected final extension InterfaceTransformer interfaceTransformer
+
+ new(StatechartTraceability traceability) {
+ super(traceability)
+
+ this.interfaceTransformer = new InterfaceTransformer(traceability)
+ }
+
+ // Gets or creates an interface and a port realizing the interface in the specified mode.
+ def getOrTransformPort(String scxmlPortName,
+ String scxmlInterfaceName, RealizationMode realizationMode
+ ) {
+ val gammaInterfaceName = getInterfaceName(scxmlInterfaceName)
+ val gammaInterface = interfaceTransformer.getOrTransformInterfaceByName(gammaInterfaceName)
+
+ val gammaPortName = getPortName(scxmlPortName)
+ val gammaPort = getOrTransformPortByName(gammaInterface, gammaPortName, realizationMode)
+
+ return gammaPort
+ }
+
+ def getOrCreateDefaultPort() {
+ val defaultPort = traceability.getDefaultPort
+ if (defaultPort !== null) {
+ return defaultPort
+ }
+ else {
+ val gammaPort = createDefaultPort
+ traceability.setDefaultPort(gammaPort)
+ return gammaPort
+ }
+ }
+
+ // We assume that the statechart's default interface already exists at this point.
+ protected def createDefaultPort() {
+ val defaultInterface = interfaceTransformer.getOrCreateDefaultInterface
+
+ val defaultInterfaceRealization = createInterfaceRealization
+ defaultInterfaceRealization.realizationMode = RealizationMode.PROVIDED
+ defaultInterfaceRealization.interface = defaultInterface
+
+ val defaultPort = createPort
+ defaultPort.name = getDefaultPortName(traceability.getTypeName())
+ defaultPort.interfaceRealization = defaultInterfaceRealization
+
+ return defaultPort
+ }
+
+ def getOrTransformDefaultInterfacePort(Interface gammaInterface) {
+ checkState(gammaInterface !== null)
+ if (traceability.containsDefaultInterfacePort(gammaInterface)) {
+ return traceability.getDefaultInterfacePort(gammaInterface)
+ }
+ else {
+ val gammaPort = transformDefaultInterfacePort(gammaInterface)
+ traceability.putDefaultInterfacePort(gammaInterface, gammaPort)
+ return gammaPort
+ }
+ }
+
+ // For now, all ports realize their interfaces in provided mode.
+ protected def transformDefaultInterfacePort(Interface gammaInterface) {
+ val gammaInterfaceRealization = createInterfaceRealization
+ gammaInterfaceRealization.realizationMode = RealizationMode.PROVIDED
+ gammaInterfaceRealization.interface = gammaInterface
+
+ val gammaPort = createPort
+ val gammaInterfaceName = gammaInterface.name
+ gammaPort.name = getDefaultInterfacePortName(gammaInterfaceName)
+ gammaPort.interfaceRealization = gammaInterfaceRealization
+
+ return gammaPort
+ }
+
+ def getOrTransformPortByName(Interface gammaInterface,
+ String portName, RealizationMode realizationMode
+ ) {
+ checkState(gammaInterface !== null)
+ checkState(portName !== null)
+ checkState(realizationMode !== null)
+
+ if (traceability.containsPort(portName)) {
+ return traceability.getPort(portName)
+ }
+ else {
+ val gammaPort = transformPortByName(gammaInterface, portName, realizationMode)
+ traceability.putPort(portName, gammaPort)
+ return gammaPort
+ }
+ }
+
+ protected def transformPortByName(Interface gammaInterface,
+ String portName, RealizationMode realizationMode
+ ) {
+ val gammaInterfaceRealization = createInterfaceRealization
+ gammaInterfaceRealization.realizationMode = realizationMode
+ gammaInterfaceRealization.interface = gammaInterface
+
+ val gammaPort = createPort
+ gammaPort.name = getPortName(portName)
+ gammaPort.interfaceRealization = gammaInterfaceRealization
+
+ return gammaPort
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlGammaExpressionTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlGammaExpressionTransformer.xtend
new file mode 100644
index 000000000..17faa4494
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlGammaExpressionTransformer.xtend
@@ -0,0 +1,19 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+class ScxmlGammaExpressionTransformer extends AtomicElementTransformer {
+
+ new(StatechartTraceability traceability) {
+ super(traceability)
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaCompositeTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaCompositeTransformer.xtend
new file mode 100644
index 000000000..41dbd8d7a
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaCompositeTransformer.xtend
@@ -0,0 +1,53 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+class ScxmlToGammaCompositeTransformer extends CompositeElementTransformer {
+
+ // Root element of the SCXML statechart model to transform
+ protected final String rootFileURI
+
+ new(String rootFileURI) {
+ this(new CompositeTraceability(rootFileURI), rootFileURI)
+ }
+
+ new(CompositeTraceability compositeTraceability, String rootFileURI) {
+ super(compositeTraceability)
+ this.rootFileURI = rootFileURI
+ }
+
+ // Entry point for the SCXML to Gamma hierarchical statechart transformation
+ def execute() {
+ // Initialize constants
+ initQueueCapacity
+
+ val rootStatechartTraceability = compositeTraceability.createStatechartTraceability(rootFileURI)
+
+ // Execute root statechart transformation.
+ // The object rootStatechartTraceability is populated with data during transformation.
+ val rootStatechartTransformer = new ScxmlToGammaStatechartTransformer(rootStatechartTraceability)
+ rootStatechartTransformer.execute()
+
+ return compositeTraceability
+ }
+
+ private def initQueueCapacity() {
+ // Define default queue capacity
+ val queueCapacityDeclaration = createConstantDeclaration
+ queueCapacityDeclaration.name = "QUEUE_CAPACITY"
+
+ val capacity = toIntegerLiteral(4)
+ queueCapacityDeclaration.expression = capacity
+ queueCapacityDeclaration.type = createIntegerTypeDefinition
+
+ compositeTraceability.setQueueCapacity(queueCapacityDeclaration)
+ }
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaStatechartTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaStatechartTransformer.xtend
new file mode 100644
index 000000000..c4159bc54
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaStatechartTransformer.xtend
@@ -0,0 +1,737 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import ac.soton.scxml.HistoryTypeDatatype
+import ac.soton.scxml.ScxmlDataType
+import ac.soton.scxml.ScxmlFinalType
+import ac.soton.scxml.ScxmlHistoryType
+import ac.soton.scxml.ScxmlInitialType
+import ac.soton.scxml.ScxmlInvokeType
+import ac.soton.scxml.ScxmlParallelType
+import ac.soton.scxml.ScxmlScxmlType
+import ac.soton.scxml.ScxmlStateType
+import ac.soton.scxml.ScxmlTransitionType
+import ac.soton.scxml.TransitionTypeDatatype
+import hu.bme.mit.gamma.expression.model.Expression
+import hu.bme.mit.gamma.statechart.composite.AsynchronousAdapter
+import hu.bme.mit.gamma.statechart.composite.AsynchronousComponent
+import hu.bme.mit.gamma.statechart.composite.ControlFunction
+import hu.bme.mit.gamma.statechart.composite.DiscardStrategy
+import hu.bme.mit.gamma.statechart.derivedfeatures.StatechartModelDerivedFeatures
+import hu.bme.mit.gamma.statechart.interface_.RealizationMode
+import hu.bme.mit.gamma.statechart.statechart.EntryState
+import hu.bme.mit.gamma.statechart.statechart.GuardEvaluation
+import hu.bme.mit.gamma.statechart.statechart.InitialState
+import hu.bme.mit.gamma.statechart.statechart.OrthogonalRegionSchedulingOrder
+import hu.bme.mit.gamma.statechart.statechart.SchedulingOrder
+import hu.bme.mit.gamma.statechart.statechart.State
+import hu.bme.mit.gamma.statechart.statechart.SynchronousStatechartDefinition
+import hu.bme.mit.gamma.statechart.statechart.Transition
+import hu.bme.mit.gamma.statechart.statechart.TransitionPriority
+import java.math.BigInteger
+import java.util.List
+import java.util.function.Function
+import java.util.logging.Level
+import org.eclipse.emf.common.util.URI
+
+import static ac.soton.scxml.ScxmlModelDerivedFeatures.*
+import static hu.bme.mit.gamma.scxml.transformation.Namings.*
+
+// TODO Scoping in variable transformation and assignment
+// TODO History, parallel
+class ScxmlToGammaStatechartTransformer extends AtomicElementTransformer {
+
+ // Reference to the composite transformation traceability model
+ protected final CompositeTraceability compositeTraceability
+
+ protected final extension ActionTransformer actionTransformer
+ protected final extension DataTransformer dataTransformer
+ protected final extension PortTransformer portTransformer
+ protected final extension TriggerTransformer triggerTransformer
+
+ // Root element of the SCXML statechart model to transform
+ protected ScxmlScxmlType scxmlRoot
+
+ // Contained elements invoking SCXML substatecharts
+ protected List invokes
+
+ // Asynchronous wrapper component around the synchronous statechart definition
+ protected AsynchronousAdapter adapter
+
+ // Root element of the Gamma statechart definition as the transformation result
+ protected SynchronousStatechartDefinition gammaStatechart
+
+ new(StatechartTraceability traceability) {
+ super(traceability)
+
+ compositeTraceability = traceability.compositeTraceability
+
+ /* TODO Load and set scxmlRoot, create default StDef
+ * scxmlRoot
+ * this.gammaStatechart = createSynchronousStatechartDefinition => [
+ * it.name = getStatechartName(scxmlRoot)
+ * ]
+ */
+ this.actionTransformer = new ActionTransformer(traceability)
+ this.dataTransformer = new DataTransformer(traceability)
+ this.portTransformer = new PortTransformer(traceability)
+ this.triggerTransformer = new TriggerTransformer(traceability)
+ }
+
+ // TODO This will be the execute() method, combine with former statechart transformer execute()
+ protected def AsynchronousComponent execute() {
+
+ // Put statechart traceability in composite traceability object
+ compositeTraceability.putTraceability(traceability.fileURI, traceability)
+
+ // TODO Check if not already transformed (at caller side or in this transformation method)
+ val fileURI = traceability.fileURI
+ scxmlRoot = loadSubcomponent(fileURI)
+ traceability.scxmlRoot = scxmlRoot
+
+ gammaStatechart = createSynchronousStatechartDefinition => [
+ it.name = getStatechartName(scxmlRoot)
+ ]
+ traceability.statechart = gammaStatechart
+
+ // TODO First, transform all invoked statechart types
+ // (recursively, through transformation calls to types of contained components)
+ // Then transform individual statecharts with invoke actions, referencing invoked types
+ // Note: circular references in invoke chains are not supported.
+ invokes = getAllInvokes(scxmlRoot)
+
+ for (invoke : invokes) {
+ // TODO Check src | srcexpr
+ val invokedSrc = invoke.src
+
+ if (!compositeTraceability.containsTraceability(invokedSrc)) {
+ // Create (and store reference to) new statechart type transformation traceability
+ val invokedStatechartTraceability = compositeTraceability.createStatechartTraceability(invokedSrc)
+ compositeTraceability.putTraceability(invokedSrc, invokedStatechartTraceability)
+
+ // Execute root statechart transformation.
+ // The object rootStatechartTraceability is populated with trace data during transformation.
+ val invokedStatechartTransformer = new ScxmlToGammaStatechartTransformer(invokedStatechartTraceability)
+ invokedStatechartTransformer.execute()
+ }
+ }
+
+ // Transform statechart after invoked statechart type transformations have finished
+ // TODO Transform control, action, data and communication elements
+ return scxmlRoot.transformStatechart
+ }
+
+ private def loadSubcomponent(String path) {
+ val fileURI = URI.createPlatformResourceURI(path, true);
+ val documentRoot = ecoreUtil.normalLoad(fileURI);
+
+ val scxmlRoot = ecoreUtil.getFirstOfAllContentsOfType(documentRoot, ScxmlScxmlType);
+ return scxmlRoot
+ }
+
+ protected def transformStatechart(ScxmlScxmlType scxmlRoot) {
+ val gammaComposite = createScheduledAsynchronousCompositeComponent
+ gammaComposite.name = getCompositeStatechartName(scxmlRoot)
+
+ // Instantiate transformed invoked child statecharts
+ for (invoke : invokes) {
+ val statechartTraceability = compositeTraceability.getTraceability(invoke.src)
+
+ // TODO Extend to deeper composition hierarchy levels, not just one-level adapter children
+ val gammaSubcomponentType = statechartTraceability.adapter as AsynchronousComponent
+
+ val gammaSubcomponent = gammaSubcomponentType.instantiateAsynchronousComponent
+ gammaSubcomponent.name = invoke.id
+ gammaComposite.components += gammaSubcomponent
+
+ compositeTraceability.putComponentInstance(invoke, gammaSubcomponent)
+ }
+
+ // Create ports, port bindings and channels in the composite component
+ val datamodels = scxmlRoot.datamodel
+ if (datamodels !== null) {
+ val datamodel = datamodels.head
+ if (datamodel !== null) {
+ val dataElements = getDataElements(datamodel)
+
+ // TODO Move string literal parts of names to Namings
+ val portDataElements = dataElements.filter [ it |
+ it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("pro_port_") ||
+ it.id.startsWith("req_port_")
+ ]
+ for (portData : portDataElements) {
+ val gammaPort = portData.getOrCreatePort
+ gammaComposite.ports += gammaPort
+ }
+
+ val bindingDataElements = dataElements.filter [ it |
+ it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("binding_")
+ ]
+ for (binding : bindingDataElements) {
+ val gammaPortBinding = binding.createBinding
+ gammaComposite.portBindings += gammaPortBinding
+ }
+
+ val channelDataElements = dataElements.filter [ it |
+ it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("channel_")
+ ]
+ for (channel : channelDataElements) {
+ val gammaChannel = channel.createChannel
+ gammaComposite.channels += gammaChannel
+ }
+ }
+ }
+
+ // TODO Merge methods logically
+ transformStatechartInnerElements
+
+ return gammaComposite
+ }
+
+ // TODO Merge this statechart transformation with 'composite' transformation method
+ // Transformation of the SCXML root element and its contents recursively.
+ // Invoked types are assumed to be already transformed by this point.
+ def transformStatechartInnerElements() {
+ traceability.setStatechart(gammaStatechart)
+
+ logger.log(Level.INFO, "Transforming root element (" + scxmlRoot.name + ")")
+
+ // Configure transition selection and execution
+ // TODO Check settings!
+ gammaStatechart.schedulingOrder = SchedulingOrder.BOTTOM_UP
+ gammaStatechart.orthogonalRegionSchedulingOrder = OrthogonalRegionSchedulingOrder.SEQUENTIAL
+ gammaStatechart.transitionPriority = TransitionPriority.ORDER_BASED
+ gammaStatechart.guardEvaluation = GuardEvaluation.BEGINNING_OF_STEP
+
+ val datamodels = scxmlRoot.datamodel
+ if (datamodels !== null) {
+ val datamodel = datamodels.head
+ if (datamodel !== null) {
+ val dataElements = getDataElements(datamodel)
+
+ // TODO Move string literal parts of names to Namings
+ val portDataElements = dataElements.filter[
+ it.eContainer.eContainer instanceof ScxmlScxmlType &&
+ (it.id.startsWith("pro_port_") || it.id.startsWith("req_port_"))
+ ]
+ for (portData : portDataElements) {
+ val gammaPort = portData.getOrCreatePort
+ gammaStatechart.ports += gammaPort
+ }
+
+ // TODO Transform constants, prefix: "const_"
+ // TODO Extract to method
+ val parameterDataElements = dataElements.filter[
+ it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("param_")
+ ]
+ for (parameterData : parameterDataElements) {
+ val gammaParameterDeclaration = dataTransformer.transformParameter(parameterData)
+ gammaStatechart.parameterDeclarations += gammaParameterDeclaration
+ }
+
+ val variableDataElements = dataElements.filter[
+ !portDataElements.contains(it) && !parameterDataElements.contains(it)
+ ]
+ for (variableData : variableDataElements) {
+ val gammaVariableDeclaration = dataTransformer.transformVariable(variableData)
+ gammaStatechart.variableDeclarations += gammaVariableDeclaration
+ }
+ }
+ }
+
+ val mainRegion = createRegion => [
+ it.name = getRegionName(scxmlRoot.name)
+ ]
+ gammaStatechart.regions += mainRegion
+
+ val scxmlStateNodes = getStateNodes(scxmlRoot)
+ for (scxmlStateNode : scxmlStateNodes) {
+ if (isParallel(scxmlStateNode)) {
+ val parallel = scxmlStateNode as ScxmlParallelType
+ mainRegion.stateNodes += parallel.transformParallel
+ } else if (isState(scxmlStateNode)) {
+ val state = scxmlStateNode as ScxmlStateType
+ mainRegion.stateNodes += state.transformState
+ } else if (isFinal(scxmlStateNode)) {
+ val final = scxmlStateNode as ScxmlFinalType
+ mainRegion.stateNodes += final.transformFinal
+ } else {
+ throw new IllegalArgumentException("Object " + scxmlStateNode + " is of unknown SCXML type.")
+ }
+ }
+
+ val transitions = getAllTransitions(scxmlRoot)
+ for (transition : transitions) {
+ transition.transformTransition
+ }
+
+ // Transform the SCXML root element's initial attribute,
+ // or select its first child as initial state if it is not present.
+ val gammaInitial = createInitialState => [
+ it.name = getInitialName(scxmlRoot)
+ ]
+ mainRegion.stateNodes += gammaInitial
+
+ val scxmlInitialAttribute = scxmlRoot.initial
+ val scxmlFirstInitialAttribute = scxmlInitialAttribute?.head
+ if (scxmlFirstInitialAttribute !== null) {
+ val gammaInitialTarget = traceability.getStateNodeById(scxmlFirstInitialAttribute)
+ gammaInitial.createTransition(gammaInitialTarget)
+ } else {
+ val firstScxmlStateChild = getFirstChildStateNode(scxmlRoot) as ScxmlStateType
+ val gammaInitialTarget = traceability.getState(firstScxmlStateChild)
+ gammaInitial.createTransition(gammaInitialTarget)
+ }
+
+ // Add all transitions from initial states of Gamma compound states
+ // specified by scxml initial attributes or document order
+ gammaStatechart.transitions += traceability.getInitialTransitions
+
+ // Add all ports from traceability
+ if (traceability.getDefaultPort !== null) {
+ gammaStatechart.ports += traceability.getDefaultPort
+ }
+ gammaStatechart.ports += traceability.defaultInterfacePorts.values
+ gammaStatechart.ports += traceability.ports.values
+
+ val adapter = wrapIntoAdapter
+ traceability.adapter = adapter
+
+ return traceability
+ }
+
+ protected def getOrCreatePort(ScxmlDataType portData) {
+ val isProvided = portData.id.startsWith("pro_port_")
+ val realizationMode = isProvided ? RealizationMode.PROVIDED : RealizationMode.REQUIRED
+
+ // Get port name and interface name from port descriptor string
+ val portString = portData.expr.trim
+ val tokens = portString.split("\\.")
+ if (tokens.size < 1 || tokens.size > 2) {
+ throw new IllegalArgumentException(
+ "Port descriptor " + portString + " does not contain exactly 1 or 2 dot separated tokens."
+ )
+ }
+
+ var scxmlInterfaceName = ""
+ var scxmlPortName = ""
+
+ if (tokens.size == 1) {
+ scxmlInterfaceName = portString
+ scxmlPortName = portString
+ } else {
+ scxmlInterfaceName = tokens.get(tokens.size - 1)
+ scxmlPortName = tokens.head
+ }
+ //
+ val gammaPort = portTransformer.getOrTransformPort(
+ scxmlPortName,
+ scxmlInterfaceName,
+ realizationMode
+ )
+
+ // TODO Put port into composite or statechart traceability?
+ compositeTraceability.putPort(scxmlPortName, gammaPort)
+
+ return gammaPort
+ }
+
+ protected def createBinding(ScxmlDataType bindingData) {
+ val bindingString = bindingData.expr.trim
+ val tokens = bindingString.split("\\.|\\s*\\-\\s*")
+ if (tokens.size != 3) {
+ throw new IllegalArgumentException(
+ "Binding descriptor " + bindingString + " does not contain exactly 3 dot separated tokens."
+ )
+ }
+
+ val sourcePortName = tokens.get(0)
+ val targetInstanceName = tokens.get(1)
+ val targetPortName = tokens.get(2)
+
+ //
+ val gammaSourcePort = compositeTraceability.getPort(sourcePortName)
+ val gammaInstancePortReference = createInstancePortReference(targetInstanceName, targetPortName)
+
+ val gammaPortBinding = createPortBinding(gammaSourcePort, gammaInstancePortReference)
+ return gammaPortBinding
+ }
+
+ protected def createChannel(ScxmlDataType channelData) {
+ val channelString = channelData.expr.trim
+ val tokens = channelString.split("\\.|\\s*\\-\\s*")
+ if (tokens.size != 4) {
+ throw new IllegalArgumentException(
+ "Channel descriptor " + channelString + " does not contain exactly 4 dot separated tokens."
+ )
+ }
+
+ val sourceInstanceName = tokens.get(0)
+ val sourcePortName = tokens.get(1)
+ val targetInstanceName = tokens.get(2)
+ val targetPortName = tokens.get(3)
+
+ //
+ val sourceInstancePortReference = createInstancePortReference(sourceInstanceName, sourcePortName)
+ val targetInstancePortReference = createInstancePortReference(targetInstanceName, targetPortName)
+
+ val gammaChannel = createChannel(sourceInstancePortReference, targetInstancePortReference)
+ return gammaChannel
+ }
+
+ private def createInstancePortReference(String instanceName, String scxmlPortName) {
+ val instance = compositeTraceability.getComponentInstance(instanceName)
+
+ // TODO Make it more performant to get statechart traceability
+ // by instance invokeId or source URI.
+ val statechartTraceability = compositeTraceability.getTraceabilityById(instanceName)
+ val port = statechartTraceability.getPort(scxmlPortName)
+
+ val instancePortReference = createInstancePortReference(instance, port)
+ return instancePortReference
+ }
+
+ protected def AsynchronousAdapter wrapIntoAdapter() {
+ adapter = gammaStatechart.wrapIntoAdapter(getAdapterName(scxmlRoot))
+
+ val allStatechartPorts = StatechartModelDerivedFeatures.getAllPortsWithInput(gammaStatechart)
+ val allInternalPorts = allStatechartPorts.filter[it|StatechartModelDerivedFeatures.isInternal(it)]
+ val allExternalPorts = allStatechartPorts.filter[it|!StatechartModelDerivedFeatures.isInternal(it)]
+
+ // Set control specification
+ // TODO Check if internal port event triggers should be added as control specification triggers.
+ for (port : allExternalPorts) {
+ val portEvents = StatechartModelDerivedFeatures.getInputEvents(port)
+ for (event : portEvents) {
+ val controlSpecification = createControlSpecification
+ controlSpecification.controlFunction = ControlFunction.RUN_ONCE
+
+ val eventReference = createPortEventReference
+ eventReference.port = port
+ eventReference.event = event
+
+ val trigger = createEventTrigger
+ trigger.eventReference = eventReference
+ controlSpecification.trigger = trigger
+
+ adapter.controlSpecifications += controlSpecification
+ }
+ }
+
+ // Create internal event queue
+ val internalEventQueue = createMessageQueue
+ internalEventQueue.name = getInternalEventQueueName(scxmlRoot)
+ internalEventQueue.eventDiscardStrategy = DiscardStrategy.INCOMING
+ internalEventQueue.priority = BigInteger.TWO
+
+ val internalCapacityReference = createDirectReferenceExpression
+ internalCapacityReference.declaration = traceability.queueCapacityDeclaration
+ internalEventQueue.capacity = internalCapacityReference
+
+ for (port : allInternalPorts) {
+ val portEvents = StatechartModelDerivedFeatures.getInputEvents(port)
+ for (event : portEvents) {
+ val reference = createPortEventReference
+ reference.port = port
+ reference.event = event
+
+ val eventPassing = createEventPassing(reference)
+ internalEventQueue.eventPassings += eventPassing
+ }
+ }
+
+ if (!internalEventQueue.eventPassings.empty) {
+ adapter.messageQueues += internalEventQueue
+ }
+
+ // Create external event queue
+ val externalEventQueue = createMessageQueue
+ externalEventQueue.name = getExternalEventQueueName(scxmlRoot)
+ externalEventQueue.eventDiscardStrategy = DiscardStrategy.INCOMING
+ externalEventQueue.priority = BigInteger.ONE
+
+ val externalCapacityReference = createDirectReferenceExpression
+ externalCapacityReference.declaration = traceability.queueCapacityDeclaration
+ externalEventQueue.capacity = externalCapacityReference
+
+ for (port : allExternalPorts) {
+ val portEvents = StatechartModelDerivedFeatures.getInputEvents(port)
+ for (event : portEvents) {
+ val reference = createPortEventReference
+ reference.port = port
+ reference.event = event
+
+ val eventPassing = createEventPassing(reference)
+ externalEventQueue.eventPassings += eventPassing
+ }
+ }
+
+ if (!externalEventQueue.eventPassings.empty) {
+ adapter.messageQueues += externalEventQueue
+ }
+
+ return adapter
+ }
+
+ protected def State transformParallel(ScxmlParallelType parallelNode) {
+ logger.log(Level.INFO, "Transforming element (" + parallelNode.id + ")")
+
+ val gammaParallel = createState => [
+ it.name = getParallelName(parallelNode)
+ ]
+
+ val scxmlStateNodes = getStateNodes(parallelNode)
+ for (scxmlStateNode : scxmlStateNodes) {
+ val region = createRegion => [
+ it.name = getRegionName(gammaParallel.name)
+ ]
+ gammaParallel.regions += region
+
+ if (isParallel(scxmlStateNode)) {
+ val parallel = scxmlStateNode as ScxmlParallelType
+ region.stateNodes += parallel.transformParallel
+ } else if (isState(scxmlStateNode)) {
+ val state = scxmlStateNode as ScxmlStateType
+ region.stateNodes += state.transformState
+ } else if (isFinal(scxmlStateNode)) {
+ val final = scxmlStateNode as ScxmlFinalType
+ region.stateNodes += final.transformFinal
+ } else {
+ throw new IllegalArgumentException("Object " + scxmlStateNode + " is of unknown SCXML type.")
+ }
+ }
+
+ // TODO Transform history pseudo-states (by adding a wrapper compound state perhaps)
+ // Initial-ok kicserélése a gyerek state-ekben, amely parallelekben találok historyt
+ // Vagy history node hozzáadása minden gyerek régióhoz, ha van kívül history?
+ /*val scxmlHistoryStates = parallelNode.history
+ * for (scxmlHistoryState : scxmlHistoryStates) {
+ *
+ }*/
+ // Transform onentry and onexit handlers
+ val onentryActions = parallelNode.onentry
+ for (onentryAction : onentryActions) {
+ if (onentryAction !== null) {
+ gammaParallel.entryActions += onentryAction.transformOnentry
+ }
+ }
+
+ val onexitActions = parallelNode.onexit
+ for (onexitAction : onexitActions) {
+ if (onexitAction !== null) {
+ gammaParallel.exitActions += onexitAction.transformOnexit
+ }
+ }
+
+ traceability.put(parallelNode, gammaParallel)
+
+ return gammaParallel
+ }
+
+ protected def State transformState(ScxmlStateType scxmlState) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlState.id + ")")
+
+ val gammaState = createState => [
+ it.name = getStateName(scxmlState)
+ ]
+
+ traceability.put(scxmlState, gammaState)
+
+ if (isCompoundState(scxmlState)) {
+ val region = createRegion => [
+ it.name = getRegionName(gammaState.name)
+ ]
+ gammaState.regions += region
+
+ val scxmlStateNodes = getStateNodes(scxmlState)
+ for (scxmlStateNode : scxmlStateNodes) {
+ if (isParallel(scxmlStateNode)) {
+ val parallel = scxmlStateNode as ScxmlParallelType
+ region.stateNodes += parallel.transformParallel
+ } else if (isState(scxmlStateNode)) {
+ val state = scxmlStateNode as ScxmlStateType
+ region.stateNodes += state.transformState
+ } else if (isFinal(scxmlStateNode)) {
+ val final = scxmlStateNode as ScxmlFinalType
+ region.stateNodes += final.transformFinal
+ } else {
+ throw new IllegalArgumentException(
+ "Object " + scxmlStateNode + " is of unknown SCXML type.")
+ }
+ }
+
+ // Transform history pseudo-states
+ val scxmlHistoryStates = scxmlState.history
+ for (scxmlHistoryState : scxmlHistoryStates) {
+ region.stateNodes += scxmlHistoryState.transform
+ }
+
+ // Transform the state's initial element or attribute,
+ // or select its first child as initial state if neither one above is present.
+ val scxmlInitialElement = scxmlState.initial.head
+ if (scxmlInitialElement !== null) {
+ region.stateNodes += scxmlInitialElement.transform
+ } else {
+ val gammaInitial = createInitialState => [
+ it.name = getInitialName(scxmlState)
+ ]
+ region.stateNodes += gammaInitial
+
+ val scxmlInitialAttribute = scxmlState.initial1.head
+ if (scxmlInitialAttribute !== null) {
+ val gammaInitialTarget = traceability.getStateNodeById(scxmlInitialAttribute)
+ val initialTransition = gammaInitial.createTransition(gammaInitialTarget)
+ traceability.putInitialTransition(initialTransition)
+ } else {
+ val firstScxmlStateChild = getFirstChildStateNode(scxmlState) as ScxmlStateType
+ val gammaInitialTarget = traceability.getState(firstScxmlStateChild)
+ val initialTransition = gammaInitial.createTransition(gammaInitialTarget)
+ traceability.putInitialTransition(initialTransition)
+ }
+ }
+ }
+
+ // Transform onentry and onexit handlers
+ val onentryActions = scxmlState.onentry
+ for (onentryAction : onentryActions) {
+ if (onentryAction !== null) {
+ val gammaOnEntryAction = onentryAction.transformOnentry
+ if (gammaOnEntryAction !== null) {
+ gammaState.entryActions += gammaOnEntryAction
+ }
+ }
+ }
+
+ val onexitActions = scxmlState.onexit
+ for (onexitAction : onexitActions) {
+ if (onexitAction !== null) {
+ val gammaOnExitAction = onexitAction.transformOnexit
+ if (gammaOnExitAction !== null) {
+ gammaState.exitActions += gammaOnExitAction
+ }
+ }
+ }
+
+ // Transform invoke actions
+ val invokeActions = scxmlState.invoke
+ for (invokeAction : invokeActions) {
+ invokeAction?.transformAction
+ }
+
+ return gammaState
+ }
+
+ protected def State transformFinal(ScxmlFinalType scxmlFinal) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlFinal.id + ")")
+
+ val gammaFinal = createState => [
+ it.name = getFinalName(scxmlFinal)
+ ]
+
+ // Transform onentry and onexit handlers
+ val onentryActions = scxmlFinal.onentry
+ for (onentryAction : onentryActions) {
+ if (onentryAction !== null) {
+ gammaFinal.entryActions += onentryAction.transformOnentry
+ }
+ }
+
+ val onexitActions = scxmlFinal.onexit
+ for (onexitAction : onexitActions) {
+ if (onexitAction !== null) {
+ gammaFinal.exitActions += onexitAction.transformOnexit
+ }
+ }
+
+ traceability.put(scxmlFinal, gammaFinal)
+
+ return gammaFinal
+ }
+
+ protected def InitialState transform(ScxmlInitialType scxmlInitial) {
+ logger.log(Level.INFO, "Transforming element")
+
+ val parentState = getParentState(scxmlInitial)
+ val gammaInitial = createInitialState => [
+ it.name = getInitialName(parentState)
+ ]
+
+ traceability.put(scxmlInitial, gammaInitial)
+
+ return gammaInitial
+ }
+
+ protected def EntryState transform(ScxmlHistoryType scxmlHistory) {
+ logger.log(Level.INFO, "Transforming element (" + scxmlHistory.id + ")")
+
+ if (scxmlHistory.type == HistoryTypeDatatype.SHALLOW) {
+ val gammaShallowHistory = createShallowHistoryState => [
+ it.name = getShallowHistoryName(scxmlHistory)
+ ]
+
+ traceability.put(scxmlHistory, gammaShallowHistory)
+ return gammaShallowHistory
+ } else {
+ val gammaDeepHistory = createDeepHistoryState => [
+ it.name = getDeepHistoryName(scxmlHistory)
+ ]
+
+ traceability.put(scxmlHistory, gammaDeepHistory)
+ return gammaDeepHistory
+ }
+ }
+
+ // Internal transitions are not supported by the transformer.
+ protected def Transition transformTransition(ScxmlTransitionType transition) {
+ if (transition.type == TransitionTypeDatatype.INTERNAL) {
+ throw new IllegalArgumentException("Transforming internal transition " + transition + " is not supported.")
+ }
+
+ val sourceState = getTransitionSource(transition)
+ val targetId = transition.target.head
+
+ val gammaSource = traceability.getStateNode(sourceState)
+ val gammaTarget = traceability.getStateNodeById(targetId)
+
+ logger.log(Level.INFO, "Transforming transition" + gammaSource.name + " -> " + gammaTarget.name)
+
+ val gammaTransition = gammaSource.createTransition(gammaTarget)
+
+ // Transform event trigger if present
+ val eventName = transition.event
+ if (!eventName.nullOrEmpty) {
+ val eventTrigger = transformTrigger(eventName)
+ gammaTransition.trigger = eventTrigger
+ }
+
+ // Transform guard if present
+ val guardStr = transition.cond
+ if (!guardStr.nullOrEmpty) {
+ val gammaGuardExpression = expressionLanguageParser.preprocessAndParse(
+ guardStr,
+ expressionLanguageLinker.getLinker(traceability)
+ )
+ gammaTransition.guard = gammaGuardExpression
+ }
+
+ // TODO for all types of actions
+ val effects = transition.assign + transition.raise
+ if (!effects.nullOrEmpty) {
+ gammaTransition.effects += effects.transformBlock
+ }
+ //
+ traceability.put(transition, gammaTransition)
+
+ return gammaTransition
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/StatechartTraceability.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/StatechartTraceability.xtend
new file mode 100644
index 000000000..68a394b51
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/StatechartTraceability.xtend
@@ -0,0 +1,611 @@
+/********************************************************************************
+ * Copyright (c) 2023-2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import ac.soton.scxml.ScxmlDataType
+import ac.soton.scxml.ScxmlFinalType
+import ac.soton.scxml.ScxmlHistoryType
+import ac.soton.scxml.ScxmlInitialType
+import ac.soton.scxml.ScxmlParallelType
+import ac.soton.scxml.ScxmlScxmlType
+import ac.soton.scxml.ScxmlStateType
+import ac.soton.scxml.ScxmlTransitionType
+import hu.bme.mit.gamma.expression.model.ConstantDeclaration
+import hu.bme.mit.gamma.expression.model.Declaration
+import hu.bme.mit.gamma.expression.model.ParameterDeclaration
+import hu.bme.mit.gamma.expression.model.VariableDeclaration
+import hu.bme.mit.gamma.statechart.composite.AsynchronousAdapter
+import hu.bme.mit.gamma.statechart.interface_.Event
+import hu.bme.mit.gamma.statechart.interface_.Interface
+import hu.bme.mit.gamma.statechart.interface_.Port
+import hu.bme.mit.gamma.statechart.statechart.EntryState
+import hu.bme.mit.gamma.statechart.statechart.InitialState
+import hu.bme.mit.gamma.statechart.statechart.State
+import hu.bme.mit.gamma.statechart.statechart.StateNode
+import hu.bme.mit.gamma.statechart.statechart.SynchronousStatechartDefinition
+import hu.bme.mit.gamma.statechart.statechart.Transition
+import hu.bme.mit.gamma.util.GammaEcoreUtil
+import java.util.Map
+import java.util.Set
+import org.eclipse.emf.ecore.EObject
+
+import static com.google.common.base.Preconditions.checkNotNull
+
+class StatechartTraceability {
+
+ // Reference to the composite transformation traceability model
+ protected final CompositeTraceability compositeTraceability
+
+ // URI of the root SCXML statechart model to transform
+ protected final String fileURI
+
+ // Root element of the atomic SCXML statechart model to transform.
+ protected ScxmlScxmlType scxmlRoot
+ protected SynchronousStatechartDefinition statechart
+ protected AsynchronousAdapter adapter
+ protected ConstantDeclaration queueCapacity
+
+ protected final Map parallels = newHashMap
+ protected final Map states = newHashMap
+ protected final Map finals = newHashMap
+ protected final Map initials = newHashMap
+ protected final Map historyStates = newHashMap
+ protected final Map transitions = newHashMap
+ protected final Set initialTransitions = newHashSet
+
+ protected final Map dataElements = newHashMap
+ protected final Map parameters = newHashMap
+ // The variables map works only if variables are globally unique and have a global scope
+ protected final Map variables = newHashMap
+
+ // Internal mappings (internal to the statechart)
+ protected Port defaultPort
+ protected Interface defaultInterface
+ protected final Map defaultInterfacePorts = newHashMap
+ protected final Map ports = newHashMap
+
+ // Global mappings (from composite traceability)
+ protected final Map interfaces
+ protected final Map, Event> internalEvents
+ protected final Map, Event> inEvents
+ protected final Map, Event> outEvents
+ protected final Map allEvents
+ protected final Map allEventParameters
+
+ protected final extension GammaEcoreUtil ecoreUtil = GammaEcoreUtil.INSTANCE
+
+ // - Synchronous Statechart Definition
+ new(
+ CompositeTraceability compositeTraceability,
+ String rootFileURI,
+ Map interfaces,
+ ConstantDeclaration queueCapacity,
+ Map, Event> internalEvents,
+ Map, Event> inEvents,
+ Map, Event> outEvents,
+ Map allEvents,
+ Map allEventParameters
+ ) {
+ this.compositeTraceability = compositeTraceability
+ this.fileURI = rootFileURI
+ this.interfaces = interfaces
+ this.queueCapacity = queueCapacity
+ this.internalEvents = internalEvents
+ this.inEvents = inEvents
+ this.outEvents = outEvents
+ this.allEvents = allEvents
+ this.allEventParameters = allEventParameters
+ }
+
+ def getScxmlRoot() {
+ return scxmlRoot
+ }
+
+ def getTypeName() {
+ return scxmlRoot.name
+ }
+
+ def setAdapter(AsynchronousAdapter adapter) {
+ this.adapter = adapter
+ }
+
+ def getAdapter() {
+ return adapter
+ }
+
+ def getQueueCapacityDeclaration() {
+ checkNotNull(queueCapacity)
+ return queueCapacity
+ }
+
+ def setStatechart(SynchronousStatechartDefinition gammaStatechart) {
+ checkNotNull(gammaStatechart)
+ statechart = gammaStatechart
+ }
+
+ def getStatechart() {
+ return statechart
+ }
+
+ // - State (with orthogonal Region children)
+ def put(ScxmlParallelType scxmlParallel, State gammaParallel) {
+ checkNotNull(scxmlParallel)
+ checkNotNull(gammaParallel)
+ parallels += scxmlParallel -> gammaParallel
+ }
+
+ def getParallel(ScxmlParallelType scxmlParallel) {
+ checkNotNull(scxmlParallel)
+ val gammaParallel = parallels.get(scxmlParallel)
+ checkNotNull(gammaParallel)
+ return gammaParallel
+ }
+
+ def getParallelById(String scxmlParallelId) {
+ checkNotNull(scxmlParallelId)
+ val keySet = states.keySet
+ val scxmlParallel = keySet.findFirst[parallel|parallel.id == scxmlParallelId]
+ checkNotNull(scxmlParallel)
+ return getState(scxmlParallel)
+ }
+
+ // - State (with a Region child if it is compound)
+ def put(ScxmlStateType scxmlState, State gammaState) {
+ checkNotNull(scxmlState)
+ checkNotNull(gammaState)
+ states += scxmlState -> gammaState
+ }
+
+ def getState(ScxmlStateType scxmlState) {
+ checkNotNull(scxmlState)
+ val gammaState = states.get(scxmlState)
+ checkNotNull(gammaState)
+ return gammaState
+ }
+
+ def getStateById(String scxmlStateId) {
+ checkNotNull(scxmlStateId)
+ val keySet = states.keySet
+ val scxmlState = keySet.findFirst[state|state.id == scxmlStateId]
+ checkNotNull(scxmlState)
+ return getState(scxmlState)
+ }
+
+ // - State
+ def put(ScxmlFinalType scxmlFinal, State gammaFinal) {
+ checkNotNull(scxmlFinal)
+ checkNotNull(gammaFinal)
+ finals += scxmlFinal -> gammaFinal
+ }
+
+ def getFinalState(ScxmlFinalType scxmlFinal) {
+ checkNotNull(scxmlFinal)
+ val gammaFinal = finals.get(scxmlFinal)
+ checkNotNull(gammaFinal)
+ return gammaFinal
+ }
+
+ def getFinalById(String scxmlFinalId) {
+ checkNotNull(scxmlFinalId)
+ val keySet = finals.keySet
+ val scxmlFinal = keySet.findFirst[final|final.id == scxmlFinalId]
+ checkNotNull(scxmlFinal)
+ return getFinalState(scxmlFinal)
+ }
+
+ // - InitialState
+ def put(ScxmlInitialType scxmlInitial, InitialState gammaInitial) {
+ checkNotNull(scxmlInitial)
+ checkNotNull(gammaInitial)
+ initials += scxmlInitial -> gammaInitial
+ }
+
+ def getInitialState(ScxmlInitialType scxmlInitial) {
+ checkNotNull(scxmlInitial)
+ val gammaInitial = initials.get(scxmlInitial)
+ checkNotNull(gammaInitial)
+ return gammaInitial
+ }
+
+ // - ShallowHistoryState or DeepHistoryState (stored as an EntryState)
+ def put(ScxmlHistoryType scxmlHistory, EntryState gammaHistory) {
+ checkNotNull(scxmlHistory)
+ checkNotNull(gammaHistory)
+ historyStates += scxmlHistory -> gammaHistory
+ }
+
+ def getHistoryState(ScxmlHistoryType scxmlHistory) {
+ checkNotNull(scxmlHistory)
+ val gammaHistory = historyStates.get(scxmlHistory)
+ checkNotNull(gammaHistory)
+ return gammaHistory
+ }
+
+ def getHistoryStateById(String scxmlHistoryId) {
+ checkNotNull(scxmlHistoryId)
+ val keySet = historyStates.keySet
+ val scxmlHistory = keySet.findFirst[history|history.id == scxmlHistoryId]
+ checkNotNull(scxmlHistory)
+ return getHistoryState(scxmlHistory)
+ }
+
+ // General functions returning a mapped Gamma StateNode
+ // Retrieves the mapped Gamma StateNode for an arbitrary transition source state
+ def getStateNode(EObject scxmlStateNode) {
+ val gammaState = states.get(scxmlStateNode)
+ if (gammaState !== null) {
+ return gammaState as StateNode
+ }
+ val gammaParallel = parallels.get(scxmlStateNode)
+ if (gammaParallel !== null) {
+ return gammaParallel as StateNode
+ }
+ val gammaInitial = initials.get(scxmlStateNode)
+ if (gammaInitial !== null) {
+ return gammaInitial as StateNode
+ }
+ val gammaHistory = historyStates.get(scxmlStateNode)
+ checkNotNull(gammaHistory)
+ return gammaHistory as StateNode
+ }
+
+ // Retrieves the mapped Gamma StateNode for an arbitrary transition target state id
+ def getStateNodeById(String scxmlStateNodeId) {
+ val gammaState = states.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value
+ if (gammaState !== null) {
+ return gammaState
+ }
+ val gammaParallel = parallels.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value
+ if (gammaParallel !== null) {
+ return gammaParallel
+ }
+ val gammaFinal = finals.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value
+ if (gammaFinal !== null) {
+ return gammaFinal
+ }
+ val gammaHistory = historyStates.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value
+ checkNotNull(gammaHistory)
+ return gammaHistory
+
+ }
+
+ // - Transition
+ def put(ScxmlTransitionType scxmlTransition, Transition gammaTransition) {
+ checkNotNull(scxmlTransition)
+ checkNotNull(gammaTransition)
+ transitions += scxmlTransition -> gammaTransition
+ }
+
+ def getTransition(ScxmlTransitionType scxmlTransition) {
+ checkNotNull(scxmlTransition)
+ val gammaTransition = transitions.get(scxmlTransition)
+ checkNotNull(gammaTransition)
+ return gammaTransition
+ }
+
+ // Transitions from initial states of Gamma compound states
+ // specified by scxml initial attributes or document order
+ def putInitialTransition(Transition gammaTransition) {
+ checkNotNull(gammaTransition)
+ initialTransitions += gammaTransition
+ }
+
+ def getInitialTransitions() {
+ return initialTransitions
+ }
+
+ // - ParameterDeclaration
+ def put(ScxmlDataType scxmlData, ParameterDeclaration gammaDeclaration) {
+ checkNotNull(scxmlData)
+ checkNotNull(gammaDeclaration)
+ dataElements += scxmlData -> gammaDeclaration
+
+ put(scxmlData.id, gammaDeclaration)
+ }
+
+ def getParameter(ScxmlDataType scxmlData) {
+ checkNotNull(scxmlData)
+ val gammaDeclaration = dataElements.get(scxmlData)
+ checkNotNull(gammaDeclaration)
+ return gammaDeclaration
+ }
+
+ // - VariableDeclaration
+ def put(ScxmlDataType scxmlData, VariableDeclaration gammaDeclaration) {
+ checkNotNull(scxmlData)
+ checkNotNull(gammaDeclaration)
+ dataElements += scxmlData -> gammaDeclaration
+
+ put(scxmlData.id, gammaDeclaration)
+ }
+
+ def getVariable(ScxmlDataType scxmlData) {
+ checkNotNull(scxmlData)
+ val gammaDeclaration = dataElements.get(scxmlData)
+ checkNotNull(gammaDeclaration)
+ return gammaDeclaration
+ }
+
+ // Parameter Declarations by String identifier
+ private def put(String scxmlParameterName, ParameterDeclaration gammaDeclaration) {
+ checkNotNull(scxmlParameterName)
+ checkNotNull(gammaDeclaration)
+ parameters += scxmlParameterName -> gammaDeclaration
+ }
+
+ def containsParameter(String scxmlParameterName) {
+ checkNotNull(scxmlParameterName)
+ return parameters.containsKey(scxmlParameterName)
+ }
+
+ def getParameter(String scxmlParameterName) {
+ checkNotNull(scxmlParameterName)
+ val gammaDeclaration = parameters.get(scxmlParameterName)
+ checkNotNull(gammaDeclaration)
+ return gammaDeclaration
+ }
+
+ // Variable Declarations by String identifier
+ private def put(String scxmlVariableName, VariableDeclaration gammaDeclaration) {
+ checkNotNull(scxmlVariableName)
+ checkNotNull(gammaDeclaration)
+ variables += scxmlVariableName -> gammaDeclaration
+ }
+
+ def containsVariable(String scxmlVariableName) {
+ checkNotNull(scxmlVariableName)
+ return variables.containsKey(scxmlVariableName)
+ }
+
+ def getVariable(String scxmlVariableName) {
+ checkNotNull(scxmlVariableName)
+ val gammaDeclaration = variables.get(scxmlVariableName)
+ checkNotNull(gammaDeclaration)
+ return gammaDeclaration
+ }
+
+ // Default port and interface (for event strings like 'event')
+ def getDefaultInterface() {
+ return defaultInterface
+ }
+
+ def setDefaultInterface(Interface defaultInterface) {
+ this.defaultInterface = defaultInterface
+ }
+
+ def getDefaultPort() {
+ return defaultPort
+ }
+
+ def setDefaultPort(Port defaultPort) {
+ this.defaultPort = defaultPort
+ }
+
+ // Default ports of interfaces (for event strings like 'interface.event')
+ def putDefaultInterfacePort(Interface gammaInterface, Port gammaPort) {
+ checkNotNull(gammaInterface)
+ checkNotNull(gammaPort)
+ defaultInterfacePorts += gammaInterface -> gammaPort
+ }
+
+ def getDefaultInterfacePort(Interface gammaInterface) {
+ checkNotNull(gammaInterface)
+ val gammaPort = defaultInterfacePorts.get(gammaInterface)
+ checkNotNull(gammaPort)
+ return gammaPort
+ }
+
+ def containsDefaultInterfacePort(Interface gammaInterface) {
+ checkNotNull(gammaInterface)
+ return defaultInterfacePorts.containsKey(gammaInterface)
+ }
+
+ // Interfaces by string identifier (for event strings like '[port.]interface.event)
+ def putInterface(String scxmlInterfaceName, Interface gammaInterface) {
+ checkNotNull(scxmlInterfaceName)
+ checkNotNull(gammaInterface)
+ interfaces += scxmlInterfaceName -> gammaInterface
+ }
+
+ def getInterface(String scxmlInterfaceName) {
+ checkNotNull(scxmlInterfaceName)
+ val gammaInterface = interfaces.get(scxmlInterfaceName)
+ checkNotNull(gammaInterface)
+ return gammaInterface
+ }
+
+ def containsInterface(String scxmlInterfaceName) {
+ checkNotNull(scxmlInterfaceName)
+ return interfaces.containsKey(scxmlInterfaceName)
+ }
+
+ def getInterfaces() {
+ return interfaces;
+ }
+
+ def getAllInterfaces() {
+ var allInterfaces = interfaces.values.toList
+ allInterfaces += defaultInterfacePorts.keySet.toList
+ if (getDefaultInterface !== null) {
+ allInterfaces += getDefaultInterface
+ }
+ return allInterfaces
+ }
+
+ // Ports by string identifier (for event strings like 'port.interface.event)
+ def putPort(String scxmlPortName, Port gammaPort) {
+ checkNotNull(scxmlPortName)
+ checkNotNull(gammaPort)
+ ports += scxmlPortName -> gammaPort
+ }
+
+ def getPort(String scxmlPortName) {
+ checkNotNull(scxmlPortName)
+ val gammaPort = ports.get(scxmlPortName)
+ checkNotNull(gammaPort)
+ return gammaPort
+ }
+
+ def containsPort(String scxmlPortName) {
+ checkNotNull(scxmlPortName)
+ return ports.containsKey(scxmlPortName)
+ }
+
+ // Internal events by pairs of {Gamma interface; event name}
+ def putInternalEvent(Pair interfaceEvent, Event gammaEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+ checkNotNull(gammaEvent)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ internalEvents += (interface -> eventName) -> gammaEvent
+
+ putEvent(eventName, gammaEvent)
+ }
+
+ def getInternalEvent(Pair interfaceEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ val gammaEvent = internalEvents.get(interface -> eventName)
+
+ checkNotNull(gammaEvent)
+ return gammaEvent
+ }
+
+ def containsInternalEvent(Pair interfaceEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ return internalEvents.containsKey(interface -> eventName)
+ }
+
+ // Input events by pairs of {Gamma interface; event name}
+ def putInEvent(Pair interfaceEvent, Event gammaEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+ checkNotNull(gammaEvent)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ inEvents += (interface -> eventName) -> gammaEvent
+
+ putEvent(eventName, gammaEvent)
+ }
+
+ def getInEvent(Pair interfaceEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ val gammaEvent = inEvents.get(interface -> eventName)
+
+ checkNotNull(gammaEvent)
+ return gammaEvent
+ }
+
+ def containsInEvent(Pair interfaceEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ return inEvents.containsKey(interface -> eventName)
+ }
+
+ // Output events by pairs of {port name; event name}
+ def putOutEvent(Pair interfaceEvent, Event gammaEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+ checkNotNull(gammaEvent)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ outEvents += (interface -> eventName) -> gammaEvent
+
+ putEvent(eventName, gammaEvent)
+ }
+
+ def getOutEvent(Pair interfaceEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ val gammaEvent = outEvents.get(interface -> eventName)
+
+ checkNotNull(gammaEvent)
+ return gammaEvent
+ }
+
+ def containsOutEvent(Pair interfaceEvent) {
+ checkNotNull(interfaceEvent)
+ checkNotNull(interfaceEvent.key)
+ checkNotNull(interfaceEvent.value)
+
+ val interface = interfaceEvent.key
+ val eventName = interfaceEvent.value
+ return outEvents.containsKey(interface -> eventName)
+ }
+
+ // Events by string identifier
+ def putEvent(String scxmlEventName, Event gammaEvent) {
+ checkNotNull(scxmlEventName)
+ checkNotNull(gammaEvent)
+ allEvents += scxmlEventName -> gammaEvent
+ }
+
+ def getEvent(String scxmlEventName) {
+ checkNotNull(scxmlEventName)
+ val gammaEvent = allEvents.get(scxmlEventName)
+ checkNotNull(gammaEvent)
+ return gammaEvent
+ }
+
+ def containsEvent(String scxmlEventName) {
+ checkNotNull(scxmlEventName)
+ return allEvents.containsKey(scxmlEventName)
+ }
+
+ // Event parameters by string identifier
+ def putEventParameter(String scxmlEventParameterName, ParameterDeclaration gammaEventParameter) {
+ checkNotNull(scxmlEventParameterName)
+ checkNotNull(gammaEventParameter)
+ allEventParameters += scxmlEventParameterName -> gammaEventParameter
+ }
+
+ def getEventParameter(String scxmlEventParameterName) {
+ checkNotNull(scxmlEventParameterName)
+ val gammaEventParameter = allEventParameters.get(scxmlEventParameterName)
+ checkNotNull(gammaEventParameter)
+ return gammaEventParameter
+ }
+
+ def containsEventParameter(String scxmlEventParameterName) {
+ checkNotNull(scxmlEventParameterName)
+ return allEventParameters.containsKey(scxmlEventParameterName)
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/TriggerTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/TriggerTransformer.xtend
new file mode 100644
index 000000000..264cc24ed
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/TriggerTransformer.xtend
@@ -0,0 +1,82 @@
+/********************************************************************************
+ * Copyright (c) 2023 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation
+
+import hu.bme.mit.gamma.statechart.interface_.Interface
+import hu.bme.mit.gamma.statechart.interface_.Port
+import hu.bme.mit.gamma.statechart.interface_.RealizationMode
+
+class TriggerTransformer extends AtomicElementTransformer {
+
+ protected final extension PortTransformer portTransformer
+ protected final extension InterfaceTransformer interfaceTransformer
+ protected final extension EventTransformer eventTransformer
+
+ new(StatechartTraceability traceability) {
+ super(traceability)
+
+ this.portTransformer = new PortTransformer(traceability)
+ this.interfaceTransformer = new InterfaceTransformer(traceability)
+ this.eventTransformer = new EventTransformer(traceability)
+ }
+
+ // TODO sanitize and check eventString
+ def transformTrigger(String eventString) {
+ val tokens = eventString.split("\\.")
+ if (tokens.size < 1 || tokens.size > 3) {
+ throw new IllegalArgumentException(
+ "Event descriptor " + eventString + " does not contain exactly 1, 2 or 3 dot separated tokens."
+ )
+ }
+
+ var gammaInterface = null as Interface
+ var gammaPort = null as Port
+ var isDefault = false
+
+ if (tokens.size == 1) {
+ isDefault = true
+ gammaInterface = getOrCreateDefaultInterface()
+ gammaPort = getOrCreateDefaultPort()
+ } else {
+ val interfaceName = tokens.get(tokens.size - 2)
+ gammaInterface = getOrTransformInterfaceByName(interfaceName)
+
+ if (tokens.size >= 3) {
+ val portName = tokens.head
+ gammaPort = getOrTransformPortByName(gammaInterface, portName, RealizationMode.REQUIRED)
+ } else {
+ isDefault = true
+ gammaPort = getOrTransformDefaultInterfacePort(gammaInterface)
+ }
+ }
+
+ val eventName = tokens.lastOrNull
+
+ // If a port is specified, the event will be an out event on an interface
+ // realized in required mode by the port receiving the event.
+ // In the case of default interfaces and ports, realization mode is provided
+ // and trigger events are internal.
+ val gammaEvent = if (isDefault) {
+ getOrTransformInternalEvent(gammaInterface, eventName)
+ } else {
+ getOrTransformOutEvent(gammaInterface, eventName)
+ }
+
+ val gammaEventReference = createPortEventReference
+ gammaEventReference.port = gammaPort
+ gammaEventReference.event = gammaEvent
+
+ val gammaEventTrigger = createEventTrigger
+ gammaEventTrigger.eventReference = gammaEventReference
+ return gammaEventTrigger
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageLinker.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageLinker.xtend
new file mode 100644
index 000000000..52be970fc
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageLinker.xtend
@@ -0,0 +1,106 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+
+package hu.bme.mit.gamma.scxml.transformation.parse;
+
+import hu.bme.mit.gamma.expression.model.ExpressionModelFactory
+import hu.bme.mit.gamma.scxml.transformation.StatechartTraceability
+import hu.bme.mit.gamma.statechart.util.StatechartUtil
+import java.util.List
+import java.util.function.Function
+import org.eclipse.emf.ecore.EObject
+import org.eclipse.xtext.nodemodel.ILeafNode
+
+class ScxmlGammaExpressionLanguageLinker {
+
+ protected final extension StatechartUtil statechartUtil = StatechartUtil.INSTANCE
+ protected final extension ExpressionModelFactory expressionModelFactory = ExpressionModelFactory.eINSTANCE
+
+ def Function getLinker(StatechartTraceability traceability) {
+ return new Function {
+ List tokenList = newArrayList
+
+// TODO Goal: set references
+// TODO Resolve with reflective API, EObject, EClass
+// Get eStructuralFeature name from CrossReference
+// Get eStructuralFeature from EClass
+// semanticElement.setEStructuralFeature(object)
+
+// TODO ILeafNode parameter
+// Get type (gamma type), id (text, scxml id)
+// scxml elements with this id
+// filter gamma type matches
+// OR: filter by type, then filter by id
+// 0 elements -> IllegalArgumentException, user scxml model error
+// 1 element -> OK, return EObject
+// 2+ element -> random selection from options: throw exception
+// or post prcessing step after full model transformation: resolve scoping globally
+// heuristic: getLevel in tree -> select safer options: global"er" reference
+// or properly implement scoping (direct reference expressions)
+ override apply(ILeafNode node) {
+ val id = node.text
+ val grammarElement = node.getGrammarElement
+ val reference = node.getSemanticElement
+
+ tokenList += id
+
+ // statechart parameter reference
+ if (traceability.containsParameter(id)) {
+ val parameterDeclaration = traceability.getParameter(id)
+ return statechartUtil.createReferenceExpression(parameterDeclaration)
+ }
+
+ // variable reference
+ if (traceability.containsVariable(id)) {
+ val variableDeclaration = traceability.getVariable(id)
+ return statechartUtil.createReferenceExpression(variableDeclaration)
+ }
+
+ // port
+ if (traceability.containsPort(id)) {
+ val port = traceability.getPort(id)
+ return port
+ }
+
+ // event
+ if (traceability.containsEvent(id)) {
+ val event = traceability.getEvent(id)
+ return event
+ }
+
+ // event parameter
+ if (traceability.containsEventParameter(id)) {
+ val eventParameter = traceability.getEventParameter(id)
+ return eventParameter
+ }
+
+ /* TODO? Alternative resolution method: get port event parameter reference by token list
+ *
+ * if (traceability.containsPort(tokenList.get(0))) {
+ * val port = traceability.getPort(tokenList.get(0))
+ * val interface = port.interfaceRealization.interface
+ * if (interface !== null && tokenList.length >= 2) {
+ * val event = interface.events.findFirst[it.event.name == tokenList.get(1)].event
+ * if (event !== null && tokenList.length >= 3) {
+ * val parameter = event.parameterDeclarations.findFirst[it.name == tokenList.get(2)]
+ * if (parameter !== null) {
+ * return createEventParameterReference(port, parameter)
+ * }
+ * }
+ * }
+ }*/
+
+ return null
+ }
+ }
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageParser.java b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageParser.java
new file mode 100644
index 000000000..342324862
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageParser.java
@@ -0,0 +1,146 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Gamma project
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * SPDX-License-Identifier: EPL-1.0
+ ********************************************************************************/
+package hu.bme.mit.gamma.scxml.transformation.parse;
+
+import static java.util.Map.entry;
+
+import java.io.StringReader;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.CrossReference;
+import org.eclipse.xtext.nodemodel.ICompositeNode;
+import org.eclipse.xtext.nodemodel.ILeafNode;
+import org.eclipse.xtext.parser.IParseResult;
+
+import hu.bme.mit.gamma.expression.model.DirectReferenceExpression;
+import hu.bme.mit.gamma.expression.model.EnumerationLiteralExpression;
+import hu.bme.mit.gamma.expression.model.Expression;
+import hu.bme.mit.gamma.expression.model.OpaqueExpression;
+import hu.bme.mit.gamma.expression.model.ParameterDeclaration;
+import hu.bme.mit.gamma.expression.model.TypeReference;
+import hu.bme.mit.gamma.statechart.interface_.Event;
+import hu.bme.mit.gamma.statechart.interface_.EventParameterReferenceExpression;
+import hu.bme.mit.gamma.statechart.interface_.Port;
+import hu.bme.mit.gamma.statechart.language.parser.StatechartExpressionLanguageParserAndLinker;
+
+public class ScxmlGammaExpressionLanguageParser extends StatechartExpressionLanguageParserAndLinker {
+
+ private static final Map defaultScxmlPreprocessRules = Map.ofEntries(
+ entry("!", "not"),
+ entry("&&", "and"),
+ entry("||", "or"));
+
+ public Expression preprocessAndParse(String expression, Function scope) {
+ return super.preprocessAndParse2(expression, scope, defaultScxmlPreprocessRules);
+ }
+
+ @Override
+ public Expression parse2(String expression, Function scope) {
+ String trimmedExpression = javaUtil.deparenthesize(expression);
+ StringReader reader = new StringReader(trimmedExpression);
+
+ IParseResult result = parser.parse(reader);
+
+ if (result.hasSyntaxErrors()) {
+ return util.createOpaqueExpression(expression);
+ }
+
+ try {
+ String typeReferenceId = null;
+
+ ICompositeNode rootNode = result.getRootNode();
+ for (ILeafNode node : rootNode.getLeafNodes()) {
+ EObject grammarElement = node.getGrammarElement();
+ System.out.println("ScxmlGammaExpressionParser: " + node.getText());
+ if (grammarElement instanceof CrossReference) {
+ EObject reference = node.getSemanticElement();
+
+ String text = node.getText();
+ // TODO grammarElement.type.classifier
+ EObject parsedReference = scope.apply(node);
+ if (parsedReference == null) {
+ parsedReference = util.createOpaqueExpression(text);
+ }
+
+ // TODO
+ // grammarelement.eContainer().getFeature() // e.g. "port"
+ // parsedReference.eClass().getEStructuralFeature(featureName);
+
+ EObject container = reference.eContainer();
+
+ /*
+ * TODO Save EObject (scope) list, define matchable sequences e.g.
+ * port.event::param, port.event, port.any, variable, parameter etc. If match,
+ * then resolve with correct types
+ *
+ * TODO Implement scoping, inject scope provider
+ */
+
+ /// Direct reference expressions (variable, parameter)
+ if (reference instanceof DirectReferenceExpression ref) {
+ if (parsedReference instanceof DirectReferenceExpression ref2) {
+ ref.setDeclaration(ref2.getDeclaration());
+ }
+ }
+ ///
+
+ /// Event parameter reference expressions
+ if (reference instanceof EventParameterReferenceExpression ref) {
+ if (parsedReference instanceof Port port) {
+ ref.setPort(port);
+ }
+ if (parsedReference instanceof Event event) {
+ ref.setEvent(event);
+ }
+ if (parsedReference instanceof ParameterDeclaration eventParameter) {
+ ref.setParameter(eventParameter);
+ }
+ }
+ ///
+
+ /* TODO Fix commented section if needed
+ * if (container == null) { // Replace would not work as it is a single element
+ * return (Expression) parsedReference; }
+ */
+
+ /// Unparsable enum literals (e.g., state references)
+ if (parsedReference instanceof OpaqueExpression) { // I.e., 'parsedReference' was 'null'
+ if (reference instanceof TypeReference && container instanceof EnumerationLiteralExpression) {
+ typeReferenceId = text;
+ continue; // Next node is the literal id
+ } else if (reference instanceof EnumerationLiteralExpression) {
+ String enumId = typeReferenceId + "::" + text;
+ parsedReference = util.createOpaqueExpression(enumId);
+
+ if (container == null) {
+ // Replace would not work as it is a single element
+ return (Expression) parsedReference;
+ } else {
+ ecoreUtil.replace(parsedReference, reference);
+ }
+ }
+ }
+ ///
+
+ // TODO Fix commented section if needed
+ // ecoreUtil.replace(parsedReference, reference);
+ }
+ }
+
+ return (Expression) result.getRootASTElement();
+ } catch (Exception e) {
+ return util.createOpaqueExpression(expression);
+ }
+ }
+
+}
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/.gitignore b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/.gitignore
new file mode 100644
index 000000000..86d0cb272
--- /dev/null
+++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
\ No newline at end of file
diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/hu/bme/mit/gamma/scxml/transformation/.ScxmlToGammaTransformer.java._trace b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/hu/bme/mit/gamma/scxml/transformation/.ScxmlToGammaTransformer.java._trace
new file mode 100644
index 000000000..7cbe5193e
Binary files /dev/null and b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/hu/bme/mit/gamma/scxml/transformation/.ScxmlToGammaTransformer.java._trace differ