diff --git a/plugins/de.cau.cs.kieler.language.server/src/de/cau/cs/kieler/language/server/LSCreator.xtend b/plugins/de.cau.cs.kieler.language.server/src/de/cau/cs/kieler/language/server/LSCreator.xtend index 497aac4575..428d24a2b5 100644 --- a/plugins/de.cau.cs.kieler.language.server/src/de/cau/cs/kieler/language/server/LSCreator.xtend +++ b/plugins/de.cau.cs.kieler.language.server/src/de/cau/cs/kieler/language/server/LSCreator.xtend @@ -19,6 +19,7 @@ import de.cau.cs.kieler.klighd.lsp.KGraphLanguageClient import de.cau.cs.kieler.klighd.lsp.interactive.layered.LayeredInteractiveLanguageServerExtension import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingInteractiveLanguageServerExtension import de.cau.cs.kieler.klighd.lsp.launch.AbstractLsCreator +import de.cau.cs.kieler.klighd.lsp.structuredProgramming.IStructuredProgrammingLanguageServerContribution import java.util.List import org.eclipse.xtext.Constants import org.eclipse.xtext.IGrammarAccess @@ -37,6 +38,7 @@ class LSCreator extends AbstractLsCreator { RectpackingInteractiveLanguageServerExtension rectPack + List diagramHighlighters List iLanguageServerExtensions @@ -54,10 +56,14 @@ class LSCreator extends AbstractLsCreator { override getLanguageServerExtensions() { constraints = injector.getInstance(LayeredInteractiveLanguageServerExtension) rectPack = injector.getInstance(RectpackingInteractiveLanguageServerExtension) + iLanguageServerExtensions = newArrayList(constraints, rectPack) for (lse : KielerServiceLoader.load(ILanguageServerContribution)) { iLanguageServerExtensions.add(lse.getLanguageServerExtension(injector)) } + for (lse : KielerServiceLoader.load(IStructuredProgrammingLanguageServerContribution)){ + iLanguageServerExtensions.add(lse.getLanguageServerExtension(injector)) + } return iLanguageServerExtensions } @@ -75,6 +81,9 @@ class LSCreator extends AbstractLsCreator { } constraints.client = languageClient as KGraphLanguageClient rectPack.client = languageClient as KGraphLanguageClient + for (lse : KielerServiceLoader.load(IStructuredProgrammingLanguageServerContribution)){ + lse.setClient(injector, languageClient) + } diagramHighlighters = newArrayList for (iLSdhc : KielerServiceLoader.load(ILSDiagramHighlighterContribution)) { var highlighter = iLSdhc.getHighlighter(injector) diff --git a/plugins/de.cau.cs.kieler.sccharts.ide/.classpath b/plugins/de.cau.cs.kieler.sccharts.ide/.classpath index 616862fb33..a1410aa45c 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ide/.classpath +++ b/plugins/de.cau.cs.kieler.sccharts.ide/.classpath @@ -1,13 +1,13 @@ + + + - - - diff --git a/plugins/de.cau.cs.kieler.sccharts.ide/META-INF/MANIFEST.MF b/plugins/de.cau.cs.kieler.sccharts.ide/META-INF/MANIFEST.MF index 702cc9f577..5d24b11ec4 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ide/META-INF/MANIFEST.MF +++ b/plugins/de.cau.cs.kieler.sccharts.ide/META-INF/MANIFEST.MF @@ -25,5 +25,4 @@ Require-Bundle: org.antlr.runtime;bundle-version="[3.2.0,3.2.1)", Export-Package: de.cau.cs.kieler.sccharts.ide.simulation, de.cau.cs.kieler.sccharts.ide.synthesis, de.cau.cs.kieler.sccharts.ide.text.contentassist.antlr, - de.cau.cs.kieler.sccharts.ide.text.contentassist.antlr.internal, - de.cau.cs.kieler.sccharts.ide.text.highlighting + de.cau.cs.kieler.sccharts.ide.text.contentassist.antlr.internal diff --git a/plugins/de.cau.cs.kieler.sccharts.ide/build.properties b/plugins/de.cau.cs.kieler.sccharts.ide/build.properties index d17f6b2413..b29b2d7dff 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ide/build.properties +++ b/plugins/de.cau.cs.kieler.sccharts.ide/build.properties @@ -6,3 +6,4 @@ bin.includes = META-INF/,\ .,\ about.html src.includes = about.html +jars.compile.order = . diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/MANIFEST.MF b/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/MANIFEST.MF index dc6759dd03..a9bdb50ecc 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/MANIFEST.MF +++ b/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/MANIFEST.MF @@ -29,7 +29,7 @@ Require-Bundle: de.cau.cs.kieler.klighd, org.eclipse.elk.alg.force, org.eclipse.elk.alg.rectpacking;bundle-version="0.6.1", de.cau.cs.kieler.klighd.kgraph, - de.cau.cs.kieler.sccharts.ide, + de.cau.cs.kieler.sccharts.ide;bundle-version="1.4.0", de.cau.cs.kieler.simulation.ui, com.google.gson;bundle-version="2.7.0", org.eclipse.ui.ide;bundle-version="3.14.0", @@ -41,7 +41,12 @@ Require-Bundle: de.cau.cs.kieler.klighd, org.eclipse.jdt.core;bundle-version="3.19.0", de.cau.cs.kieler.kicool.ide, de.cau.cs.kieler.annotations.ide, - org.eclipse.elk.core.service + org.eclipse.elk.core.service, + de.cau.cs.kieler.klighd.lsp;bundle-version="2.2.1", + de.cau.cs.kieler.scl.ide;bundle-version="1.4.0", + org.eclipse.sprotty;bundle-version="0.9.0", + org.eclipse.lsp4j;bundle-version="0.14.0", + de.cau.cs.kieler.language.server;bundle-version="1.4.0" Bundle-Vendor: Kiel University Bundle-ActivationPolicy: lazy Export-Package: de.cau.cs.kieler.sccharts.ui, diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/services/de.cau.cs.kieler.sccharts.ui.synthesis.hooks.SynthesisHook b/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/services/de.cau.cs.kieler.sccharts.ui.synthesis.hooks.SynthesisHook index b78459e206..deff30712a 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/services/de.cau.cs.kieler.sccharts.ui.synthesis.hooks.SynthesisHook +++ b/plugins/de.cau.cs.kieler.sccharts.ui/META-INF/services/de.cau.cs.kieler.sccharts.ui.synthesis.hooks.SynthesisHook @@ -15,4 +15,5 @@ de.cau.cs.kieler.sccharts.ui.synthesis.hooks.InducedDataflowHook de.cau.cs.kieler.sccharts.ui.synthesis.hooks.ShowStateDependencyHook de.cau.cs.kieler.sccharts.ui.synthesis.hooks.ActionsAsDataflowHook de.cau.cs.kieler.sccharts.ui.debug.hooks.SetBreakpointActionHook -de.cau.cs.kieler.sccharts.ui.synthesis.hooks.ModelOrderHook \ No newline at end of file +de.cau.cs.kieler.sccharts.ui.synthesis.hooks.ModelOrderHook +de.cau.cs.kieler.sccharts.ui.synthesis.hooks.StructuralEditingHook \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/EdgeActions.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/EdgeActions.xtend new file mode 100644 index 0000000000..3d30105279 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/EdgeActions.xtend @@ -0,0 +1,299 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import java.util.function.Consumer +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend.lib.annotations.EqualsHashCode +import org.eclipse.xtend.lib.annotations.ToString +import de.cau.cs.kieler.klighd.structuredEditMsg.InputType +import de.cau.cs.kieler.klighd.structuredEditMsg.StructuredEditMsg +import org.eclipse.sprotty.Action + +/** + * Action received from client when a change of target is requested. + * The transmitted information is the new target state id and the id of the edge. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangeTargetStateAction implements Action { + public static val LABEL = "Change target state" + public static val KIND = 'SCChart_graph_changeTargetState' + String kind = KIND + + public String id + public String new_target + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("new_target", "SelectTarget", "New target state"); + return #[input1]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangeTargetStateAction.LABEL, + ChangeTargetStateAction.KIND, + false, + ChangeTargetStateAction.getInputs() + ) + } +} + +/** + * Action received from client when a change of source is requested. + * The transmitted information is the new source state id and the id of the edge. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangeSourceStateAction implements Action { + public static val LABEL = "Change source state" + public static val KIND = 'SCChart_graph_changeSourceState' + String kind = KIND + + public String id + public String new_source + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("new_source", "SelectSource", "New source state"); + return #[input1]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangeSourceStateAction.LABEL, + ChangeSourceStateAction.KIND, + false, + ChangeSourceStateAction.getInputs() + ) + } +} + +/** + * Action received from client if a change of trigger or effect is requested. + * The given information is the id of the edge and the new trigger and effects as string representations. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangeTriggerEffectAction implements Action { + public static val LABEL = "Change trigger and effect" + public static val KIND = 'SCChart_graph_changeTriggerAndEffect' + String kind = KIND + + public String id + public String trigger + public String effect + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("trigger", "String", "New trigger"); + val input2 = new InputType("effect", "String", "New effect"); + return #[input1, input2]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangeTriggerEffectAction.LABEL, + ChangeTriggerEffectAction.KIND, + false, + ChangeTriggerEffectAction.getInputs() + ) + } +} + +/** + * Action received from the client if a edge priority should change. + * The given information is the new priority and the edge id. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangePriorityAction implements Action { + public static val LABEL = "Change priority" + public static val KIND = 'SCChart_graph_changePriorityOfEdge' + String kind = KIND + + public String id + public String priority + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("priority", "String", "Change Priority"); + return #[input1]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangePriorityAction.LABEL, + ChangePriorityAction.KIND, + false, + ChangePriorityAction.getInputs() + ) + } +} + +/** + * Action received from the client if a transition should be weak + * The given information is the id of the edge + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangeToWeakTransitionAction implements Action { + public static val LABEL = "Change to weak transition" + public static val KIND = 'SCChart_graph_changeToWeakTransition' + String kind = KIND + + public String id + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangeToWeakTransitionAction.LABEL, + ChangeToWeakTransitionAction.KIND, + false, + ChangeToWeakTransitionAction.getInputs() + ) + } +} + +/** + * Action received from the client if a transition should be aborting + * The given information is the id of the edge + * + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangeToAbortingTransitionAction implements Action { + public static val LABEL = "Change to aborting transition" + public static val KIND = 'SCChart_graph_changeToAbortTransition' + String kind = KIND + + public String id + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangeToAbortingTransitionAction.LABEL, + ChangeToAbortingTransitionAction.KIND, + false, + ChangeToAbortingTransitionAction.getInputs() + ) + } +} + +/** + * Action received from the client if a transition should be terminating + * The given information is the id of the edge + * + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ChangeToTerminatingTransitionAction implements Action { + public static val LABEL = "Change to terminating transition" + public static val KIND = 'SCChart_graph_changeToTerminatingTransition' + String kind = KIND + + public String id + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ChangeToTerminatingTransitionAction.LABEL, + ChangeToTerminatingTransitionAction.KIND, + false, + ChangeToTerminatingTransitionAction.getInputs() + ) + } +} diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/MultipurposeActions.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/MultipurposeActions.xtend new file mode 100644 index 0000000000..93fe1653da --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/MultipurposeActions.xtend @@ -0,0 +1,60 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import java.util.function.Consumer +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend.lib.annotations.EqualsHashCode +import org.eclipse.xtend.lib.annotations.ToString +import de.cau.cs.kieler.klighd.structuredEditMsg.InputType +import de.cau.cs.kieler.klighd.structuredEditMsg.StructuredEditMsg +import org.eclipse.sprotty.Action + +/** + * The delete action is supported by all selectable elements. + * The given information are one or many nodes ids seperated by : + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class DeleteAction implements Action { + public static val LABEL = "Delete" + public static val KIND = 'SCChart_graph_Delete' + String kind = KIND + + public String id + public Boolean mergable = true + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + DeleteAction.LABEL, + DeleteAction.KIND, + true, + DeleteAction.getInputs() + ) + } +} \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/RegionActions.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/RegionActions.xtend new file mode 100644 index 0000000000..088f70a131 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/RegionActions.xtend @@ -0,0 +1,103 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import java.util.function.Consumer +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend.lib.annotations.EqualsHashCode +import org.eclipse.xtend.lib.annotations.ToString +import de.cau.cs.kieler.klighd.structuredEditMsg.InputType +import de.cau.cs.kieler.klighd.structuredEditMsg.StructuredEditMsg +import org.eclipse.sprotty.Action + +/** + * Action received from client to rename a region. + * Given information is the regions id and the new name of the region. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class RenameRegionAction implements Action { + public static val LABEL = "Rename region" + public static val KIND = 'SCChart_graph_RenameRegion' + String kind = KIND + + public String id + public String region_name + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("region_name", "String", "New Name"); + return #[input1]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + RenameRegionAction.LABEL, + RenameRegionAction.KIND, + false, + RenameRegionAction.getInputs() + ) + } +} + +/** + * Action received from client to add concurrent behavior. + * Given information is the new concurrent regions name and the name of the initial state in it. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class AddConcurrentRegionAction implements Action { + public static val LABEL = "Add concurrent region" + public static val KIND = 'SCChart_graph_AddConcurrentRegion' + String kind = KIND + + public String id + public String region_name + public String state_name + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("state_name", "String", "New initial state name"); + val input2 = new InputType("region_name", "String", "New Region Name"); + return #[input1, input2]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + AddConcurrentRegionAction.LABEL, + AddConcurrentRegionAction.KIND, + false, + AddConcurrentRegionAction.getInputs() + ) + } +} \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructProgLanguageServerContribution.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructProgLanguageServerContribution.xtend new file mode 100644 index 0000000000..a694db2866 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructProgLanguageServerContribution.xtend @@ -0,0 +1,41 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import com.google.inject.Injector +import de.cau.cs.kieler.klighd.lsp.KGraphDiagramState +import de.cau.cs.kieler.klighd.lsp.KGraphLanguageClient +import de.cau.cs.kieler.klighd.lsp.KGraphLanguageServerExtension +import de.cau.cs.kieler.klighd.lsp.structuredProgramming.IStructuredProgrammingLanguageServerContribution + +/** + * Used during creation of a language server and to set the client acordingly + * @author felixj + * + */ +class ScchartsStructProgLanguageServerContribution implements IStructuredProgrammingLanguageServerContribution{ + override getLanguageServerExtension(Injector injector) { + + + val serverExt = injector.getInstance(ScchartsStructuredProgrammingServerExtension) + injector.getInstance(ScchartsStructuredActionHandler).ext = serverExt + serverExt.KGraphDiagramState = injector.getInstance(KGraphDiagramState) + serverExt.KGraphLanguageServerExtension = injector.getInstance(KGraphLanguageServerExtension) + return serverExt + } + + override setClient(Injector injector, KGraphLanguageClient client) { + injector.getInstance(ScchartsStructuredProgrammingServerExtension).KGraphLanguageClient = client + } + +} diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredActionHandler.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredActionHandler.xtend new file mode 100644 index 0000000000..5f6ace6fbd --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredActionHandler.xtend @@ -0,0 +1,130 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import de.cau.cs.kieler.klighd.lsp.AbstractActionHandler +import de.cau.cs.kieler.klighd.lsp.IActionHandler +import de.cau.cs.kieler.klighd.lsp.KGraphDiagramServer +import org.eclipse.sprotty.Action + +class ScchartsStructuredActionHandler extends AbstractActionHandler implements IActionHandler{ + + //idealy injection should work however this for some reason gives back null + //also every time this class is used in the KGraphDiagramServer a new instance is created meaning even after + //changing the extension to the correct one its not persistent unless its static + //@inject + //ScchartsStructuredProgrammingServerExtension extens + static ScchartsStructuredProgrammingServerExtension extens = new ScchartsStructuredProgrammingServerExtension() + + new() { + this.supportedMessages = newHashMap( + DeleteAction.KIND -> DeleteAction, + RenameStateAction.KIND -> RenameStateAction, + AddSuccessorStateAction.KIND -> AddSuccessorStateAction, + AddHierarchicalStateAction.KIND -> AddHierarchicalStateAction, + ChangeTargetStateAction.KIND -> ChangeTargetStateAction, + ChangeSourceStateAction.KIND -> ChangeSourceStateAction, + ChangeTriggerEffectAction.KIND -> ChangeTriggerEffectAction, + RenameRegionAction.KIND -> RenameRegionAction, + AddConcurrentRegionAction.KIND -> AddConcurrentRegionAction, + ChangeToAbortingTransitionAction.KIND -> ChangeToAbortingTransitionAction, + ChangeToTerminatingTransitionAction.KIND -> ChangeToTerminatingTransitionAction, + ChangeToWeakTransitionAction.KIND -> ChangeToWeakTransitionAction, + AddTransitionAction.KIND -> AddTransitionAction, + ToggleFinalStateAction.KIND -> ToggleFinalStateAction, + MakeInitialStateAction.KIND -> MakeInitialStateAction, + EditSemanticDeclarationAction.KIND -> EditSemanticDeclarationAction, + ChangePriorityAction.KIND -> ChangePriorityAction + ) + } + + override handle(Action action, String clientId, KGraphDiagramServer server) { + + if (action.kind == DeleteAction.KIND) { + synchronized (server.modelLock) { + extens.delete(action as DeleteAction, clientId, server) + } + } else if (action.kind == RenameStateAction.KIND) { + synchronized (server.modelLock) { + extens.rename(action, clientId, server) + } + } else if (action.kind == AddSuccessorStateAction.KIND) { + synchronized (server.modelLock) { + extens.addSuccessorState(action as AddSuccessorStateAction, clientId, server) + } + } else if (action.kind == AddHierarchicalStateAction.KIND) { + synchronized (server.modelLock) { + extens.addHirachicalNode(action as AddHierarchicalStateAction, clientId, server) + } + } else if (action.kind == ChangeTargetStateAction.KIND) { + synchronized (server.modelLock) { + extens.changeDestination(action as ChangeTargetStateAction, clientId, server) + } + } else if (action.kind == ChangeSourceStateAction.KIND) { + synchronized (server.modelLock) { + extens.changeSource(action as ChangeSourceStateAction, clientId, server) + } + } else if (action.kind == ChangeTriggerEffectAction.KIND) { + synchronized (server.modelLock) { + extens.changeIO(action as ChangeTriggerEffectAction, clientId, server) + } + } else if (action.kind == RenameRegionAction.KIND) { + synchronized (server.modelLock) { + extens.rename(action, clientId, server) + } + } else if (action.kind == AddConcurrentRegionAction.KIND) { + synchronized (server.modelLock) { + extens.addConcurrentRegion(action as AddConcurrentRegionAction, clientId, server) + } + } else if (action.kind == ChangeToAbortingTransitionAction.KIND) { + synchronized (server.modelLock) { + extens.changeToAbort(action as ChangeToAbortingTransitionAction, clientId, server) + } + } else if (action.kind == ChangeToTerminatingTransitionAction.KIND) { + synchronized (server.modelLock) { + extens.changeToTerminating(action as ChangeToTerminatingTransitionAction, clientId, server) + } + } else if (action.kind == ChangeToWeakTransitionAction.KIND) { + synchronized (server.modelLock) { + extens.changeToWeak(action as ChangeToWeakTransitionAction, clientId, server) + } + } else if (action.kind == AddTransitionAction.KIND) { + synchronized (server.modelLock) { + extens.addNewTransition(action as AddTransitionAction, clientId, server) + } + } else if (action.kind == ToggleFinalStateAction.KIND) { + synchronized (server.modelLock) { + extens.toggleFinalState(action as ToggleFinalStateAction, clientId, server) + } + } else if (action.kind == MakeInitialStateAction.KIND) { + synchronized (server.modelLock) { + extens.makeInitialState(action as MakeInitialStateAction, clientId, server) + } + } else if (action.kind == EditSemanticDeclarationAction.KIND) { + synchronized (server.modelLock) { + extens.editSemanticDeclaration(action as EditSemanticDeclarationAction, clientId, server) + } + } else if (action.kind == ChangePriorityAction.KIND) { + synchronized (server.modelLock) { + extens.changeEdgePriority(action as ChangePriorityAction, clientId, server) + } + } else { + throw new IllegalArgumentException("Action " + action.kind + " not supported by handler " + + this.class.simpleName) + } + } + + def setExt(ScchartsStructuredProgrammingServerExtension ext) { + extens = ext + } +} \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredActions.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredActions.xtend new file mode 100644 index 0000000000..a42633c23b --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredActions.xtend @@ -0,0 +1,44 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import de.cau.cs.kieler.klighd.lsp.structuredProgramming.IStructuredActions + +/** + * @author felixj + * + */ +class ScchartsStructuredActions implements IStructuredActions{ + + override getKindAndActions() { + return newHashMap( + DeleteAction.KIND -> DeleteAction, + RenameStateAction.KIND -> RenameStateAction, + AddSuccessorStateAction.KIND -> AddSuccessorStateAction, + AddHierarchicalStateAction.KIND -> AddHierarchicalStateAction, + ChangeTargetStateAction.KIND -> ChangeTargetStateAction, + ChangeSourceStateAction.KIND -> ChangeSourceStateAction, + ChangeTriggerEffectAction.KIND -> ChangeTriggerEffectAction, + RenameRegionAction.KIND -> RenameRegionAction, + AddConcurrentRegionAction.KIND -> AddConcurrentRegionAction, + ChangeToAbortingTransitionAction.KIND -> ChangeToAbortingTransitionAction, + ChangeToTerminatingTransitionAction.KIND -> ChangeToTerminatingTransitionAction, + ChangeToWeakTransitionAction.KIND -> ChangeToWeakTransitionAction, + AddTransitionAction.KIND -> AddTransitionAction, + ToggleFinalStateAction.KIND -> ToggleFinalStateAction, + MakeInitialStateAction.KIND -> MakeInitialStateAction, + EditSemanticDeclarationAction.KIND -> EditSemanticDeclarationAction, + ChangePriorityAction.KIND -> ChangePriorityAction + ) + } +} \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredProgrammingServerExtension.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredProgrammingServerExtension.xtend new file mode 100644 index 0000000000..f80deaef03 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/ScchartsStructuredProgrammingServerExtension.xtend @@ -0,0 +1,761 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import com.google.inject.Inject +import de.cau.cs.kieler.kexpressions.impl.OperatorExpressionImpl +import de.cau.cs.kieler.kexpressions.impl.ValuedObjectReferenceImpl +import de.cau.cs.kieler.kexpressions.keffects.impl.AssignmentImpl +import de.cau.cs.kieler.kexpressions.kext.KExtStandaloneParser +import de.cau.cs.kieler.klighd.internal.util.KlighdInternalProperties +import de.cau.cs.kieler.klighd.kgraph.KEdge +import de.cau.cs.kieler.klighd.kgraph.KNode +import de.cau.cs.kieler.klighd.lsp.KGraphDiagramServer +import de.cau.cs.kieler.klighd.lsp.KGraphDiagramState +import de.cau.cs.kieler.klighd.lsp.KGraphLanguageClient +import de.cau.cs.kieler.klighd.lsp.KGraphLanguageServerExtension +import de.cau.cs.kieler.klighd.lsp.LSPUtil +import de.cau.cs.kieler.klighd.lsp.ValuedObjectNotFoundException +import de.cau.cs.kieler.sccharts.ControlflowRegion +import de.cau.cs.kieler.sccharts.PreemptionType +import de.cau.cs.kieler.sccharts.Region +import de.cau.cs.kieler.sccharts.State +import de.cau.cs.kieler.sccharts.Transition +import de.cau.cs.kieler.sccharts.extensions.SCChartsTransitionExtensions +import de.cau.cs.kieler.sccharts.impl.SCChartsFactoryImpl +import java.io.ByteArrayOutputStream +import java.util.List +import java.util.Map +import javax.inject.Singleton +import org.eclipse.lsp4j.Position +import org.eclipse.lsp4j.Range +import org.eclipse.lsp4j.TextEdit +import org.eclipse.sprotty.Action +import org.eclipse.xtext.ide.server.ILanguageServerAccess +import org.eclipse.xtext.ide.server.ILanguageServerExtension + +/** + * @author felixj + * + */ +@Singleton +class ScchartsStructuredProgrammingServerExtension implements ILanguageServerExtension{ + //only injection that works as intendet + // used to change transitions + @Inject extension SCChartsTransitionExtensions + + //all injections are problematic since they return null! + //used to communicate with the frontend eg sending error messages + //@inject + static KGraphLanguageClient client; + + // used to get the client uri + //@inject + static KGraphDiagramState diagramState + + // used to get the model from a given uri + //@inject + static KGraphLanguageServerExtension languageServer + + // used to create new states transitions and regions + SCChartsFactoryImpl factory = new SCChartsFactoryImpl + + + + // used to store the old file size since updating text content replaces old content for now. + Position pre_range + + override initialize(ILanguageServerAccess access) { + factory = new SCChartsFactoryImpl() + } + + /*simply sets the range to be the files size. */ + def set_pre(String clientId) { + val uri = diagramState.getURIString(clientId) + val resource = languageServer.getResource(uri); + val outputStream = new ByteArrayOutputStream + resource.save(outputStream, emptyMap) + val codeBefor = outputStream.toString().trim() + val lines = codeBefor.split("\r\n|\r|\n") + // The range is the length of the previous file. + val last_line = lines.get(lines.length - 1) + pre_range = new Position(lines.length, last_line.length) + } + + /* Called to add a new transition to a state. */ + def addNewTransition(AddTransitionAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + // get the node corresponding to the selected state on the client. Since the contextmenu was opened for a specific + // state we know the id exists and is a state so we can omit a try catch block + val uri = diagramState.getURIString(clientId) + val kNode = LSPUtil.getKNode(diagramState, uri, action.id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + + if (node.parentRegion === null){ + this.client.sendMessage("The root may not have a successor.", "error") + return + } + val new_transition = factory.createTransition() + + // Since trigger and effect are given as strings there may be variables that are not inputs / outputs + // also we only allow assignments in effects. + try { + changeTrigger(new_transition, action.trigger, uri) + changeEffect(new_transition, action.effect, uri) + } catch (ValuedObjectNotFoundException ex) { + client.sendMessage("During the parsing of the expression " + action.trigger + " the object: " + ex.message + + " could not be found.", "error") + return + } catch (ExpressionParseException ex) { + client.sendMessage( + "During the parsing of the expression " + action.effect + " the expression: " + ex.message + + " could not be converted to an assignment expression.", "error") + return + } catch (NullPointerException ex) { + client.sendMessage("The expression could not be parsed.", "error") + return + } + + // Since the destination was selected it may be a region or a state outside the parent region of the selected node + try { + val kDest = LSPUtil.getKNode(diagramState, uri, action.destination) + val dest = kDest.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + // need to check if the destination is in the same region as the source + if (dest.parentRegion !== node.parentRegion) { + client.sendMessage("The selected state is not part of the same region.", "error") + return + } + new_transition.sourceState = node + new_transition.targetState = dest + } catch (ClassCastException|NullPointerException ex) { + // Catching the moment when not a state is selected as destination + client.sendMessage("The selected element is not a targetable state.", "error") + return + } + + updateDocument(uri) + } + + /* changes a given transition to a weak transition */ + def changeToWeak(ChangeToWeakTransitionAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as Transition + + edge.preemption = PreemptionType.WEAK + + updateDocument(uri) + } + + /*changes a given transition to a terminating transition */ + def changeToTerminating(ChangeToTerminatingTransitionAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as Transition + + edge.preemption = PreemptionType.TERMINATION + + updateDocument(uri) + } + + /*changes a given transition to a aborting transition */ + def changeToAbort(ChangeToAbortingTransitionAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as Transition + + edge.preemption = PreemptionType.STRONG + + updateDocument(uri) + } + + /*changes the trigger and effect of a transition */ + def changeIO(ChangeTriggerEffectAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + var edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as Transition + + // currently as workaround we delete the edge and add it again since adjusting trigger and effect directly + // introduces some weird spacing in the updateDocument step + val prio = edge.priority + val target = edge.targetState + val source = edge.sourceState + + val newedge = createTransitionTo(source, target) + + // Since trigger and effect are given as strings there may be variables that are not inputs / outputs + // also we only allow assignments in effects. + try { + changeTrigger(newedge, action.trigger, uri) + changeEffect(newedge, action.effect, uri) + setSpecificPriority(newedge, prio) + } catch (ValuedObjectNotFoundException ex) { + client.sendMessage("During the parsing of the expression " + action.trigger + " the object: " + ex.message + + " could not be found.", "error") + return + } catch (ExpressionParseException ex) { + client.sendMessage( + "During the parsing of the expression " + action.effect + " the expression: " + ex.message + + " could not be converted to an assignment expression.", "error") + return + } catch (NullPointerException ex) { + client.sendMessage("The expression could not be parsed.", "error") + return + } + deleteEdge(kEdge) + + updateDocument(uri) + } + + /*method to change the destination of a given transition */ + def changeDestination(ChangeTargetStateAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as Transition + + // since the destination is selected by the user it may be inside another region or be no state atall + try { + val kNode = LSPUtil.getKNode(diagramState, uri, action.new_target) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + // checking if the selected state is inside the same region as the source + if (node.parentRegion !== edge.targetState.parentRegion) { + client.sendMessage("The selected state is not part of the same region.", "error") + return + } + + edge.targetState.incomingTransitions.remove(edge) + edge.targetState = node + + node.incomingTransitions.add(edge) + } catch (ClassCastException|NullPointerException ex) { + // catching the cases where the selected element is not a state + client.sendMessage("The selected element is not a targetable state.", "error") + return + } + + updateDocument(uri) + } + + /*Method to change the source of a given transition */ + def changeSource(ChangeSourceStateAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as Transition + + // we need to check if the selected element is a state and if the selected element is inside the same region + try { + val kNode = LSPUtil.getKNode(diagramState, uri, action.new_source) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + // checking the the target and source are in the same region + if (node.parentRegion !== edge.targetState.parentRegion) { + client.sendMessage("The selected state is not part of the same region.", "error") + return + } + + edge.sourceState.outgoingTransitions.remove(edge) + edge.sourceState = node + + node.outgoingTransitions.add(edge) + } catch (ClassCastException|NullPointerException ex) { + // catching all cases where not a state was selected. + client.sendMessage("The selected element is not a targetable state.", "error") + return + } + + updateDocument(uri) + } + + /*Method to add hirarchical behavior to a node */ + def addHirachicalNode(AddHierarchicalStateAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + // we need to make sure that the new states name is given + if (action.state_name.equals("")) { + this.client.sendMessage("You must provide a state id.", "error") + return + } + val newState = factory.createState() + val state_id = getId(action.state_name) + // we need to make sure the new name obeys the rules for state names. + if (state_id.equals("_") || state_id.equals("")) { + this.client.sendMessage("The state id needs to have atleast one number or letter in it.", "error") + return + } + // we want to display the desired name as label in the graph + if (!state_id.equals(action.state_name)) { + newState.label = action.state_name + } + newState.name = state_id + newState.initial = true + + // Regions follow a laxer definition for id's there may be regions that have no name etc. + val newRegion = factory.createControlflowRegion() + if (!action.region_name.equals("")) { + val region_id = getId(action.region_name) + + if (!region_id.equals(action.region_name)) + newRegion.label = action.region_name + + newRegion.name = region_id + } + + newRegion.states.add(newState) + + val uri = diagramState.getURIString(clientId) + val kNode = LSPUtil.getKNode(diagramState, uri, action.id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + + node.regions.add(newRegion) + + updateDocument(uri) + } + + /*Method to add a concurrent behavior. */ + def addConcurrentRegion(AddConcurrentRegionAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + // To make sure there is a name given for the state. + if (action.state_name.equals("")) { + this.client.sendMessage("You must provide a state id.", "error") + return + } + val newState = factory.createState() + val state_id = getId(action.state_name) + // we need to make sure the new name obeys the rules for state names. + if (state_id.equals("_") || state_id.equals("")) { + this.client.sendMessage("The state id needs to have atleast one number or letter in it.", "error") + return + } + // we want to display the desired name as label in the graph + if (!state_id.equals(action.state_name)) { + newState.label = action.state_name + } + newState.name = state_id + newState.initial = true + + // Regions follow a laxer definition for id's there may be regions that have no name etc. + val newRegion = factory.createControlflowRegion() + if (!action.region_name.equals("")) { + val region_id = getId(action.region_name) + + if (!region_id.equals(action.region_name)) + newRegion.label = action.region_name + + if (!(region_id.equals("") || region_id.equals("_"))) + newRegion.name = region_id + } + + newRegion.states.add(newState) + + val uri = diagramState.getURIString(clientId) + val kNode = LSPUtil.getKNode(diagramState, uri, action.id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) + + if (node instanceof ControlflowRegion) { + (node as ControlflowRegion).parentState.regions.add(newRegion) + } + + updateDocument(uri) + } + + /*Method to rename states and regions */ + def rename(Action action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + // We need to differentiate between states and regions + if (action.kind === RenameStateAction.KIND) { + val kNode = LSPUtil.getKNode(diagramState, uri, (action as RenameStateAction).id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) + // we need to make sure the new name obeys the rules for state ids + if ((action as RenameStateAction).state_name.equals("")) { + this.client.sendMessage("You must provide a state id.", "error") + return + } + val state_id = getId((action as RenameStateAction).state_name) + if (state_id.equals("_") || state_id.equals("")) { + this.client.sendMessage("The state id needs to have atleast one number or letter in it.", "error") + return + } + if (!state_id.equals((action as RenameStateAction).state_name)) { + (node as State).label = (action as RenameStateAction).state_name + } + (node as State).name = state_id + + } else if (action.kind === RenameRegionAction.KIND) { + val kNode = LSPUtil.getKNode(diagramState, uri, (action as RenameRegionAction).id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) + // we need to make sure the new name obeys the rules for region names. + if((action as RenameRegionAction).region_name.equals("")) return + + val region_id = getId((action as RenameRegionAction).region_name) + + if (!region_id.equals((action as RenameRegionAction).region_name)) { + (node as Region).label = (action as RenameRegionAction).region_name + } + + (node as Region).name = region_id + + } + + updateDocument(uri) + } + + /*Method to delete elements ie. Regions, States and Transitions */ + def delete(DeleteAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + try { + // for multiple slected elements + val nodes = action.id.split(":"); + for (x : nodes) { + this.deleteSingleElem(x, clientId, server) + } + } catch (NullPointerException ex) { + // single element was send and should be deleted + this.deleteSingleElem(action.id, clientId, server) + } + + val uri = diagramState.getURIString(clientId) + + updateDocument(uri) + } + + /*Method to add a successor state to a given state */ + def addSuccessorState(AddSuccessorStateAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kNode = LSPUtil.getKNode(diagramState, uri, action.id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + if (node.parentRegion === null){ + this.client.sendMessage("The root may not have a successor.", "error") + return + } + + // we need to make sure a name for the new state is given + if (action.state_name.equals("")) { + this.client.sendMessage("You must provide a state id.", "error") + return + } + val newState = factory.createState() + val state_id = getId(action.state_name) + // we need to make sure the new name obeys the rules for state id's + if (state_id.equals("_") || state_id.equals("")) { + this.client.sendMessage("The state id needs to have atleast one number or letter in it.", "error") + return + } + // we want to display the given name in the graph and have the id only as id + if (!state_id.equals(action.state_name)) { + newState.label = action.state_name + } + newState.name = state_id + + val new_transition = factory.createTransition() + + // the trigger and effect are given as strings and can therefore can be false. + // The Variables may not be initialised or the effect may not be an expression that assigns somthing. + try { + changeTrigger(new_transition, action.trigger, uri) + changeEffect(new_transition, action.effect, uri) + } catch (ValuedObjectNotFoundException ex) { + client.sendMessage("During the parsing of the expression " + action.trigger + " the object: " + ex.message + + " could not be found.", "error") + return + } catch (ExpressionParseException ex) { + client.sendMessage( + "During the parsing of the expression " + action.effect + " the expression: " + ex.message + + " could not be converted to an assignment expression.", "error") + return + } catch (NullPointerException ex) { + client.sendMessage("The expression could not be parsed.", "error") + return + } + + new_transition.sourceState = node + new_transition.targetState = newState + + node.parentRegion.states.add(newState) + + updateDocument(uri) + } + + /*Method to toggle a state to be final or not */ + def toggleFinalState(ToggleFinalStateAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + + val uri = diagramState.getURIString(clientId) + val kNode = LSPUtil.getKNode(diagramState, uri, action.id) + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + if (node.parentRegion === null){ + this.client.sendMessage("The root may not be final.", "error") + return + } + node.final = !node.final + + updateDocument(uri) + } + + /*Send from client to server to indicate that the focused tab should be different. */ + def editSemanticDeclaration(EditSemanticDeclarationAction action, String clientId, + KGraphDiagramServer server) { + val uri = diagramState.getURIString(clientId) + this.client.sendMessage(uri, "switchEditor") + } + + /*Makes the desired state initial and changes the old initial state to be normal. */ + def makeInitialState(MakeInitialStateAction action, String clientId, + KGraphDiagramServer server) { + set_pre(clientId) + + // since the action is triggered with the contextmenu for states the action id is the id of a state and thus we can omit the try catch's + val uri = diagramState.getURIString(clientId) + val kNode = LSPUtil.getKNode(diagramState, uri, action.id) + val new_init = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + if (new_init.parentRegion === null){ + this.client.sendMessage("The root may not be an initial node.", "error") + return + } + // changes the old initial state to be normal + for (node : new_init.parentRegion.states) { + if(node.initial) node.initial = false + } + + new_init.initial = true + + updateDocument(uri) + } + + /*Method to change a transitions priority. The edge moves up/down the hirachy and the others are moved up or down by one */ + def changeEdgePriority(ChangePriorityAction action, String clientId, KGraphDiagramServer server) { + set_pre(clientId) + val uri = diagramState.getURIString(clientId) + val kEdge = LSPUtil.getKEdge(diagramState, uri, action.id) + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) + val i = Integer.parseInt(action.priority) + setSpecificPriority(edge as Transition, i) + updateDocument(uri) + } + + /*Helper method to delete a single element */ + def deleteSingleElem(String id, String clientId, KGraphDiagramServer server) { + val uri = diagramState.getURIString(clientId) + // kNode may be states or regions. + val kNode = LSPUtil.getKNode(diagramState, uri, id) + + if (kNode !== null && kNode.parent !== null) { + deleteNode(kNode); + } + // edges are transitions + val kEdge = LSPUtil.getKEdge(diagramState, uri, id) + if (kEdge !== null) { + deleteEdge(kEdge); + } + } + + /*Helper method to delete edges by removing the edge from source and target */ + def deleteEdge(KEdge kEdge) { + val edge = kEdge.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) + val source = kEdge.source.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + val target = kEdge.target.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + + source.outgoingTransitions.remove(edge) + target.incomingTransitions.remove(edge) + } + + /*Helper method to delete nodes or regions */ + def void deleteNode(KNode kNode) { + val node = kNode.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) + + if (node instanceof State) { + // if we delete the last state in a region we instead want to delete the region itself + if (node.parentRegion.states.length === 1) { + node.parentRegion.parentState.regions.remove(node.parentRegion) + return + } + // for all incomming edges we need to delete edge and remove it from the source + for (incommingEdge : kNode.incomingEdges) { + + val source = incommingEdge.source.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + + val transitions = source.getOutgoingTransitions() + val to_del = newArrayList + + for (transition : transitions) { + if (transition.getTargetState() === node) { + to_del.add(transition) + } + } + for (t : to_del) { + source.outgoingTransitions.remove(t) + } + + } + // for all outgoing edges we need to delete the edge and remove it from the target + for (outgoingEdge : kNode.outgoingEdges) { + val target = outgoingEdge.target.getProperty(KlighdInternalProperties.MODEL_ELEMEMT) as State + + val transitions = target.getIncomingTransitions() + val to_del = newArrayList + + for (transition : transitions) { + if (transition.getSourceState() === node) { + to_del.add(transition) + } + } + for (t : to_del) { + target.incomingTransitions.remove(t) + } + } + // if the node is initial we want to make any other node initial + if (node.initial) { + if (node.parentRegion.states.get(0) !== node) { + node.parentRegion.states.get(0).initial = true + } else { + node.parentRegion.states.get(1).initial = true + } + + } + // finaly we want to remove the node from the region. + node.parentRegion.states.remove(node) + } else { + // in case of regions we want to delete them from the parent state + (node as ControlflowRegion).parentState.regions.remove(node) + } + + } + + /*Helper method to change the trigger of an transition. Similar to change effect*/ + def changeTrigger(Transition transition, String trigger, String uri) { + if (trigger != "") { + // may yield nothing and thus throw a nullpointer exception (Handeled when the mehtod is used) + val new_trigger = KExtStandaloneParser.parseExpression(trigger) + + // The expression may consist of multiple operators + if ((new_trigger instanceof OperatorExpressionImpl)) { + _changeTriggerSubExpressions(transition, new_trigger, uri) + + } else if (new_trigger instanceof ValuedObjectReferenceImpl) { + // If there is a valued objectreverence we need to change the reference to the one in the model + (new_trigger as ValuedObjectReferenceImpl).valuedObject = LSPUtil.getValuedObjectReference(diagramState, + uri, (new_trigger as ValuedObjectReferenceImpl).valuedObject.name) + } + + transition.trigger = new_trigger + } + } + + /*Helper method to change the valued object references recursively for all valued objects */ + def void _changeTriggerSubExpressions(Transition transition, OperatorExpressionImpl trigger, String uri) { + for (exp : trigger.subExpressions) { + // If we have an operator we want to change the subexcpressions + if (exp instanceof OperatorExpressionImpl) { + _changeTriggerSubExpressions(transition, exp, uri) + + } else if (exp instanceof ValuedObjectReferenceImpl) { + // if we have a object reference we want to change it to the one in the model + exp.valuedObject = LSPUtil.getValuedObjectReference(diagramState, uri, exp.valuedObject.name) + } + } + } + + /*Helper function to change the effect of a transition. Similar to change Trigger*/ + def changeEffect(Transition new_transition, String total_effect_String, String uri) { + // we want to delete any old effects + new_transition.effects.removeAll(new_transition.effects) + + if (total_effect_String != "") { + // since there may be multiple assignments in a effect we need to split those up before parsing. + val effects_String = total_effect_String.split(";") + for (effect_string : effects_String) { + try { + // may be null if no expression could be generated + val effect = KExtStandaloneParser.parseEffect(effect_string); + // need to update the valued object since the parser generates dummys + // cast can throw a class cast exception since the user input may be no assingment + (effect as AssignmentImpl).reference.valuedObject = LSPUtil. + getValuedObjectReference(diagramState, uri, + (effect as AssignmentImpl).reference.valuedObject.name) + new_transition.effects.add(effect) + } catch (ClassCastException ex) { + // simply rethrown for readability in the catch cases outside of method + throw new ExpressionParseException(effect_string) + } + + } + } + } + + /*updates the textual representation on the client. + * for now everything is replaced.*/ + def updateDocument(String uri) { + val Map> changes = newHashMap + + val resource = languageServer.getResource(uri); + + val outputStream = new ByteArrayOutputStream + resource.save(outputStream, emptyMap) + val codeAfter = outputStream.toString().trim() + + // The range is the length of the previous file. + val Range range = new Range(new Position(0, 0), pre_range) + + val TextEdit textEdit = new TextEdit(range, codeAfter) + changes.put(uri, #[textEdit]); + + this.client.replaceContentInFile(uri, codeAfter, range) + } + + /*removes all charactes that can not be in an id from a string and returns the resulting string */ + def getId(String name) { + return name.replaceAll("[^a-zA-Z0-9_]", "") + } + + def setKGraphDiagramState(KGraphDiagramState diagramState) { + this.diagramState = diagramState + } + + def setKGraphLanguageClient(KGraphLanguageClient client) { + this.client = client + } + + def setKGraphLanguageServerExtension(KGraphLanguageServerExtension serverExt) { + this.languageServer = serverExt + } + +} + +/** + * Simple exception for readability when error arises during the parsing of a expression + * @author fjo + */ +class ExpressionParseException extends Exception { + + new(String expression) { + super(expression) + } + +} + diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/StateActions.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/StateActions.xtend new file mode 100644 index 0000000000..9d7dc3c291 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/structuredProgramming/StateActions.xtend @@ -0,0 +1,306 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.structuredProgramming + +import java.util.function.Consumer +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend.lib.annotations.EqualsHashCode +import org.eclipse.xtend.lib.annotations.ToString +import de.cau.cs.kieler.klighd.structuredEditMsg.InputType +import de.cau.cs.kieler.klighd.structuredEditMsg.StructuredEditMsg +import org.eclipse.sprotty.Action + +/** + * Action received from the client if the window should switch to the code base. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class EditSemanticDeclarationAction implements Action { + public static val LABEL = "Edit sematic declarations" + public static val KIND = 'SCChart_EditSemanticDeclarations' + String kind = KIND + public String id + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + EditSemanticDeclarationAction.LABEL, + EditSemanticDeclarationAction.KIND, + false, + EditSemanticDeclarationAction.getInputs() + ) + } +} + +/** + * Action received from the client if a state should be renamed. + * The given information is the state id and the new name. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class RenameStateAction implements Action { + public static val LABEL = "Rename state" + public static val KIND = 'SCChart_graph_RenameState' + String kind = KIND + + public String id + public String state_name + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("state_name", "String", "New Name"); + return #[input1]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + RenameStateAction.LABEL, + RenameStateAction.KIND, + false, + RenameStateAction.getInputs() + ) + } +} + +/** + * Action received from client if a new transition should be added. + * Given from client are the id of the source node the new destination + * as well as trigger and effect as strings. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class AddTransitionAction implements Action { + public static val LABEL = "Add new transition" + public static val KIND = 'SCChart_graph_AddTransition' + String kind = KIND + + public String id + public String destination + public String trigger + public String effect + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("destination", "SelectTarget", "Destination"); + val input2 = new InputType("trigger", "String", "Trigger"); + val input3 = new InputType("effect", "String", "Effect") + return #[input1, input2, input3]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + AddTransitionAction.LABEL, + AddTransitionAction.KIND, + false, + AddTransitionAction.getInputs() + ) + } +} + +/** + * Action received from client for adding a new successor state. + * the given information is the predecessor node the new nodes name as well as + * trigger and effect + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class AddSuccessorStateAction implements Action { + public static val LABEL = "Add successor state" + public static val KIND = 'SCChart_graph_AddSuccessorState' + String kind = KIND + + public String id + public String state_name + public String trigger + public String effect + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("state_name", "String", "Name of state"); + val input2 = new InputType("trigger", "String", "Trigger"); + val input3 = new InputType("effect", "String", "Effect") + return #[input1, input2, input3]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + AddSuccessorStateAction.LABEL, + AddSuccessorStateAction.KIND, + false, + AddSuccessorStateAction.getInputs() + ) + } +} + +/** + * Action received from the client to add a hirachical behavior to a state. + * Given information is the states id the new regions name and the new states name. + * + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class AddHierarchicalStateAction implements Action { + public static val LABEL = "Add region" + public static val KIND = 'SCChart_graph_AddHierarchicalState' + String kind = KIND + + public String id + public String state_name + public String region_name + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + val input1 = new InputType("state_name", "String", "State Name"); + val input2 = new InputType("region_name", "String", "Region Name"); + return #[input1, input2]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + AddHierarchicalStateAction.LABEL, + AddHierarchicalStateAction.KIND, + false, + AddHierarchicalStateAction.getInputs() + ) + } +} + +/** + * Action received from the client if a state should be made initial. + * Given information is the state id of the state that should be initial. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class MakeInitialStateAction implements Action { + public static val LABEL = "Make initial state" + public static val KIND = 'SCChart_graph_MakeInitialState' + String kind = KIND + + public String id + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + MakeInitialStateAction.LABEL, + MakeInitialStateAction.KIND, + false, + MakeInitialStateAction.getInputs() + ) + } +} + +/** + * Action received to toggle a state to a final state or back. + * Given information is the state id which should be toggled. + * @author fjo + */ +@Accessors +@EqualsHashCode +@ToString(skipNulls=true) +class ToggleFinalStateAction implements Action { + public static val LABEL = "Toggle final state" + public static val KIND = 'SCChart_graph_MakeFinalState' + String kind = KIND + + public String id + + new() { + } + + new(Consumer initializer) { + initializer.accept(this) + } + + /* Returns the array of inputs requested from the user to perform the action. */ + def static InputType[] getInputs() { + return #[]; + } + + /* Used in the synthesis to append the supported actions to the root node for the use on the client. */ + def static StructuredEditMsg getMsg() { + return new StructuredEditMsg( + ToggleFinalStateAction.LABEL, + ToggleFinalStateAction.KIND, + false, + ToggleFinalStateAction.getInputs() + ) + } +} \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/ControlflowRegionSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/ControlflowRegionSynthesis.xtend index 06facd8182..6c7fee8e2f 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/ControlflowRegionSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/ControlflowRegionSynthesis.xtend @@ -50,6 +50,9 @@ import static de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.* import static extension de.cau.cs.kieler.annotations.ide.klighd.CommonSynthesisUtil.* import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* import static extension de.cau.cs.kieler.klighd.util.ModelingUtil.* +import de.cau.cs.kieler.klighd.microlayout.PlacementUtil +import de.cau.cs.kieler.klighd.KlighdOptions +import de.cau.cs.kieler.sccharts.ui.synthesis.filtering.SCChartsSemanticFilterTags /** * Transforms {@link ControlflowRegion} into {@link KNode} diagram elements. @@ -76,6 +79,13 @@ class ControlflowRegionSynthesis extends SubSynthesis override performTranformation(ControlflowRegion region) { val node = region.createNode().associateWith(region); + val semanticTags = newArrayList( + SCChartsSemanticFilterTags.REGION, + SCChartsSemanticFilterTags.CONTROLFLOW_REGION + ) + node.setLayoutOption(KlighdOptions.SEMANTIC_FILTER_TAGS, semanticTags) + val proxy = createNode().associateWith(region) + val maxProxyLabelLength = 5 node.configureNodeLOD(region) @@ -200,14 +210,46 @@ class ControlflowRegionSynthesis extends SubSynthesis if (region.final) addFinalRegionStyle ] } + + proxy.addRegionFigure => [ + if (region.override) addOverrideRegionStyle + if (region.abort) addAbortRegionStyle + if (region.final) addFinalRegionStyle + val label = region.serializeHighlighted(true) + if (label.length > 0) { + val name = label.get(0) + if (name.key.length > maxProxyLabelLength) { + label.set(0, new Pair(name.key.subSequence(0, maxProxyLabelLength) + "...", name.value)) + } + } + ] val returnNodes = newArrayList(node) if (SHOW_COMMENTS.booleanValue) { region.getCommentAnnotations.forEach[ - node.children += it.transform + val comments = it.transform + node.children += comments + // Comments shouldn't be rendered as proxies + comments.forEach[ + setProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY, false) + ] ] - } + } + + // Set size to be square and at least 34 (same as minimal node size) + val proxyBounds = PlacementUtil.estimateSize(proxy) + val minSize = 34 + val bigEnough = proxyBounds.width > 10 && proxyBounds.height > 10 + proxy.width = bigEnough ? proxyBounds.width : minSize + proxy.height = bigEnough ? proxyBounds.height : minSize + // Use this size to make proxies square + // val size = Math.max(minSize, Math.max(proxyBounds.width, proxyBounds.height)) + // Use this to make proxies always be at least minSize x minSize + // proxy.width = Math.max(minSize, proxyBounds.width) + + node.setProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY, true) + node.setProperty(KlighdProperties.PROXY_VIEW_PROXY_RENDERING, proxy.data) return returnNodes } diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/DataflowRegionSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/DataflowRegionSynthesis.xtend index 99c2797eaf..bf19089c3b 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/DataflowRegionSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/DataflowRegionSynthesis.xtend @@ -41,6 +41,9 @@ import org.eclipse.elk.core.options.EdgeRouting import static de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.* import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* +import de.cau.cs.kieler.klighd.microlayout.PlacementUtil +import de.cau.cs.kieler.klighd.KlighdOptions +import de.cau.cs.kieler.sccharts.ui.synthesis.filtering.SCChartsSemanticFilterTags /** * @author ssm @@ -80,6 +83,13 @@ class DataflowRegionSynthesis extends SubSynthesis { override performTranformation(DataflowRegion region) { val node = region.createNode().associateWith(region) + val semanticTags = newArrayList( + SCChartsSemanticFilterTags.REGION, + SCChartsSemanticFilterTags.DATAFLOW_REGION + ) + node.setLayoutOption(KlighdOptions.SEMANTIC_FILTER_TAGS, semanticTags) + val proxy = createNode().associateWith(region) + val maxProxyLabelLength = 5 node.addLayoutParam(CoreOptions::ALGORITHM, LayeredOptions.ALGORITHM_ID) //node.setLayoutOption(LayeredOptions.CONSIDER_MODEL_ORDER, OrderingStrategy.PREFER_EDGES) @@ -173,11 +183,30 @@ class DataflowRegionSynthesis extends SubSynthesis { ] ] + proxy.addRegionFigure => [ + if (sLabel.length > 0) it.setUserScheduleStyle + if (region.override) addOverrideRegionStyle + if (!CIRCUIT.booleanValue) { + if (label.length > 0) { + val name = label.get(0) + if (name.key.length > maxProxyLabelLength) { + label.set(0, new Pair(name.key.subSequence(0, maxProxyLabelLength) + "...", name.value)) + } + } + } + ] + node.setSelectionStyle + proxy.setSelectionStyle if (SHOW_COMMENTS.booleanValue) { region.getCommentAnnotations.forEach[ - node.children += it.transform + val comments = it.transform + node.children += comments + // Comments shouldn't be rendered as proxies + comments.forEach[ + setProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY, false) + ] ] } @@ -187,6 +216,21 @@ class DataflowRegionSynthesis extends SubSynthesis { if (!CIRCUIT.booleanValue) { node.setLayoutOption(CoreOptions::PADDING, new ElkPadding(18d, 7d, 7d, 7d)); } + + + // Set size to be square and at least 34 (same as minimal node size) + val proxyBounds = PlacementUtil.estimateSize(proxy) + val minSize = 34 + val bigEnough = proxyBounds.width > 10 && proxyBounds.height > 10 + proxy.width = bigEnough ? proxyBounds.width : minSize + proxy.height = bigEnough ? proxyBounds.height : minSize + // Use this size to make proxies square + // val size = Math.max(minSize, Math.max(proxyBounds.width, proxyBounds.height)) + // Use this to make proxies always be at least minSize x minSize + // proxy.width = Math.max(minSize, proxyBounds.width) + + node.setProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY, true) + node.setProperty(KlighdProperties.PROXY_VIEW_PROXY_RENDERING, node.data) return newArrayList(node) } diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/GeneralSynthesisOptions.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/GeneralSynthesisOptions.xtend index 7d5999b133..b33cbfe59b 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/GeneralSynthesisOptions.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/GeneralSynthesisOptions.xtend @@ -35,49 +35,70 @@ final class GeneralSynthesisOptions { /** * The appearance category */ - public static final SynthesisOption APPEARANCE = SynthesisOption.createCategory(GeneralSynthesisOptions, "Appearance") + public static final SynthesisOption APPEARANCE = SynthesisOption.createCategory(GeneralSynthesisOptions, + "Appearance") /** * The navigation category */ - public static final SynthesisOption NAVIGATION = SynthesisOption.createCategory(GeneralSynthesisOptions, "Navigation", false) + public static final SynthesisOption NAVIGATION = SynthesisOption.createCategory(GeneralSynthesisOptions, + "Navigation", false) /** * The debugging category */ - public static final SynthesisOption DEBUGGING = SynthesisOption.createCategory(GeneralSynthesisOptions, "Analysis / Debugging", false) + public static final SynthesisOption DEBUGGING = SynthesisOption.createCategory(GeneralSynthesisOptions, + "Analysis / Debugging", false) /** * The layout category */ - public static final SynthesisOption LAYOUT = SynthesisOption.createCategory(GeneralSynthesisOptions, "Layout", false) + public static final SynthesisOption LAYOUT = SynthesisOption.createCategory(GeneralSynthesisOptions, "Layout", + false) /** * Dataflow category */ - public static final SynthesisOption DATAFLOW = SynthesisOption.createCategory(GeneralSynthesisOptions, "Dataflow", false).setCategory(APPEARANCE) + public static final SynthesisOption DATAFLOW = SynthesisOption.createCategory(GeneralSynthesisOptions, "Dataflow", + false).setCategory(APPEARANCE) /** * References and OO / Inheritance */ - public static final SynthesisOption OO = SynthesisOption.createCategory(GeneralSynthesisOptions, "Object Orientation / References", false).setCategory(APPEARANCE) - + public static final SynthesisOption OO = SynthesisOption.createCategory(GeneralSynthesisOptions, + "Object Orientation / References", false).setCategory(APPEARANCE) + // -- OPTIONS -- /** * Option for setting the KLayLayered layout */ - public static final SynthesisOption USE_KLAY = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "KLayLayered", true).setCategory(LAYOUT) - public static final SynthesisOption SHOW_ALL_SCCHARTS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "All SCCharts", false).setCategory(APPEARANCE) - public static final SynthesisOption SHOW_COMMENTS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Comment Nodes", true).setCategory(APPEARANCE) - public static final SynthesisOption SHOW_USER_LABELS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "User Labels", true).setCategory(APPEARANCE) + public static final SynthesisOption USE_KLAY = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "KLayLayered", true).setCategory(LAYOUT) + public static final SynthesisOption SHOW_ALL_SCCHARTS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "All SCCharts", false).setCategory(APPEARANCE) + public static final SynthesisOption SHOW_COMMENTS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "Comment Nodes", true).setCategory(APPEARANCE) + public static final SynthesisOption SHOW_USER_LABELS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "User Labels", true).setCategory(APPEARANCE) /** * Scope call parameters synthesis option */ - public static final SynthesisOption SHOW_BINDINGS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Binding Parameters", true).setCategory(OO) + public static final SynthesisOption SHOW_BINDINGS = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "Binding Parameters", true).setCategory(OO) /** * inherited declarations and regions synthesis option */ - public static final SynthesisOption SHOW_INHERITANCE = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Inheritance Preview", true).setCategory(OO) - public static final SynthesisOption SHOW_INHERITANCE_EDGES = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Inheritance Hierarchy", false).setCategory(OO) - public static final SynthesisOption SHOW_AGGREGATION_EDGES = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Aggregation", false).setCategory(OO) - public static final SynthesisOption SHOW_METHOD_BODY = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Method Implementation", true).setCategory(OO) + public static final SynthesisOption SHOW_INHERITANCE = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "Inheritance Preview", true).setCategory(OO) + public static final SynthesisOption SHOW_INHERITANCE_EDGES = SynthesisOption.createCheckOption( + GeneralSynthesisOptions, "Inheritance Hierarchy", false).setCategory(OO) + public static final SynthesisOption SHOW_AGGREGATION_EDGES = SynthesisOption.createCheckOption( + GeneralSynthesisOptions, "Aggregation", false).setCategory(OO) + public static final SynthesisOption SHOW_METHOD_BODY = SynthesisOption.createCheckOption(GeneralSynthesisOptions, + "Method Implementation", true).setCategory(OO) /** * inherited declarations and regions synthesis option */ - public static final SynthesisOption SHOW_CAUSAL_DATAFLOW = SynthesisOption.createCheckOption(GeneralSynthesisOptions, "Causal Dataflow",false).setCategory(DEBUGGING) + public static final SynthesisOption SHOW_CAUSAL_DATAFLOW = SynthesisOption.createCheckOption( + GeneralSynthesisOptions, "Causal Dataflow", false).setCategory(DEBUGGING) + /** + * Enables the structured programming mode on the client + */ + public static final SynthesisOption ENABLE_STRUCTURED_PROGRAMMING = SynthesisOption.createCheckOption( + GeneralSynthesisOptions, "Structured Programming", true).setCategory(APPEARANCE) } diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/SCChartsSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/SCChartsSynthesis.xtend index accdc3b81b..8850b5d773 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/SCChartsSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/SCChartsSynthesis.xtend @@ -55,6 +55,10 @@ import org.eclipse.elk.graph.properties.IProperty import org.eclipse.elk.graph.properties.Property import static de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.* +import de.cau.cs.kieler.klighd.filtering.SemanticFilterRule +import de.cau.cs.kieler.klighd.filtering.SemanticFilterTag +import de.cau.cs.kieler.klighd.KlighdOptions +import de.cau.cs.kieler.sccharts.ui.synthesis.filtering.SCChartsSemanticFilterRules /** * Main diagram synthesis for SCCharts. @@ -125,7 +129,8 @@ class SCChartsSynthesis extends AbstractDiagramSynthesis { SHOW_METHOD_BODY, SHOW_COMMENTS, SHOW_USER_LABELS, - SHOW_CAUSAL_DATAFLOW + SHOW_CAUSAL_DATAFLOW, + ENABLE_STRUCTURED_PROGRAMMING ) // Adaptive Zoom @@ -167,6 +172,12 @@ class SCChartsSynthesis extends AbstractDiagramSynthesis { } else sccharts val rootNode = createNode + + // Set semantic filter rules + val rules = SCChartsSemanticFilterRules.fields.map[ + get(null) as SemanticFilterRule + ] + rootNode.setLayoutOption(KlighdOptions.SEMANTIC_FILTER_RULES, rules) // If dot is used draw edges first to prevent overlapping with states when layout is bad usedContext.setProperty(KlighdProperties.EDGES_FIRST, !USE_KLAY.booleanValue) @@ -187,7 +198,12 @@ class SCChartsSynthesis extends AbstractDiagramSynthesis { if (scc.hasPragma(PRAGMA_SKINPATH)) { setSkinPath(scc.getStringPragmas(PRAGMA_SKINPATH).head.values.head, usedContext) } - + + //Enables or disables the display of the contextmenu on the client. + //We want this on the server since not all languages are suported and the contextmenu should only be generated if there is a server implementation for it + rootNode.setProperty(KlighdProperties.SHOW_STRUCTURED_EDITING_MENU, ENABLE_STRUCTURED_PROGRAMMING.booleanValue) + + if (SHOW_ALL_SCCHARTS.booleanValue) { val rootStateNodes = newHashMap val rootStates = newLinkedHashSet diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend index 72e4031467..0b84b70b96 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend @@ -88,6 +88,12 @@ import static de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.* import static extension de.cau.cs.kieler.annotations.ide.klighd.CommonSynthesisUtil.* import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* +import org.eclipse.emf.common.util.EList +import de.cau.cs.kieler.klighd.kgraph.KGraphData +import de.cau.cs.kieler.klighd.kgraph.KGraphElement +import de.cau.cs.kieler.klighd.microlayout.PlacementUtil +import de.cau.cs.kieler.klighd.KlighdOptions +import de.cau.cs.kieler.sccharts.ui.synthesis.filtering.SCChartsSemanticFilterTags /** * Transforms {@link State} into {@link KNode} diagram elements. @@ -140,10 +146,15 @@ class StateSynthesis extends SubSynthesis { override List performTranformation(State state) { val node = state.createNode().associateWith(state) + val semanticTags = newArrayList(SCChartsSemanticFilterTags.STATE) + node.setLayoutOption(KlighdOptions.SEMANTIC_FILTER_TAGS, semanticTags) + val proxy = createNode().associateWith(state) + val maxProxyLabelLength = 5 // Set KIdentifier for use with incremental update if (!state.name.nullOrEmpty) { node.KID = state.name + proxy.KID = '''«state.name»-proxy''' } // configure region dependency layout config if an appropriate result is present. @@ -164,12 +175,22 @@ class StateSynthesis extends SubSynthesis { // Basic state style switch state { - case isConnector: + case isConnector: { node.addConnectorFigure - case state.isMacroState: + semanticTags.add(SCChartsSemanticFilterTags.CONNECTOR_STATE) + proxy.addConnectorFigure + } + case state.isMacroState: { node.addMacroFigure - default: + semanticTags.add(SCChartsSemanticFilterTags.HIERARCHICAL_STATE) + proxy.addMacroFigure + } + default: { node.addDefaultFigure + semanticTags.add(SCChartsSemanticFilterTags.SIMPLE_STATE) + // Proxy should be more square-like + proxy.addMacroFigure + } } // Styles from modifiers @@ -178,12 +199,18 @@ class StateSynthesis extends SubSynthesis { } if (state.isInitial) { node.setInitialStyle + semanticTags.add(SCChartsSemanticFilterTags.INITIAL_STATE) + proxy.setInitialStyle if (USE_KLAY.booleanValue && state.parentRegion.states.head == state) { node.setLayoutOption(LayeredOptions::LAYERING_LAYER_CONSTRAINT, LayerConstraint::FIRST); } + }else{ + semanticTags.add(SCChartsSemanticFilterTags.NOT_INITIAL_STATE) } if (state.isFinal) { node.setFinalStyle + semanticTags.add(SCChartsSemanticFilterTags.FINAL_STATE) + proxy.setFinalStyle } if (state.isViolation) { val isHaltState = state.outgoingTransitions.size == 0 @@ -241,8 +268,21 @@ class StateSynthesis extends SubSynthesis { } } node.addMacroStateLabel(label) + if (label.length > 0) { + val name = label.get(0) + if (name.key.length > maxProxyLabelLength) { + label.set(0, new Pair(name.key.subSequence(0, maxProxyLabelLength) + "...", name.value)) + } + } + proxy.addMacroStateLabel(label) } else { - node.addSimpleStateLabel(state.serializeHR.toString) + val label = state.serializeHR.toString + node.addSimpleStateLabel(label) + if (label.length > maxProxyLabelLength) { + proxy.addSimpleStateLabel(label.substring(0, maxProxyLabelLength) + "...") + } else { + proxy.addSimpleStateLabel(label) + } }) => [ setProperty(TracingVisualizationProperties.TRACING_NODE, true) associateWith(state) @@ -254,6 +294,7 @@ class StateSynthesis extends SubSynthesis { ] } else { node.addEmptyStateLabel + proxy.addEmptyStateLabel } // Add declarations @@ -326,8 +367,12 @@ class StateSynthesis extends SubSynthesis { if (SHOW_INHERITANCE.booleanValue) regions.addAll(0, state.allVisibleInheritedRegions.toList) for (region : regions) { switch region { - ControlflowRegion: node.children += region.transform - DataflowRegion: node.children += region.transform + ControlflowRegion: { + node.children += region.transform + } + DataflowRegion: { + node.children += region.transform + } } } @@ -353,9 +398,34 @@ class StateSynthesis extends SubSynthesis { if (SHOW_COMMENTS.booleanValue) { state.getCommentAnnotations.forEach[ - returnNodes += it.transform - ] - } + val comments = it.transform + returnNodes += comments + // Comments shouldn't be rendered as proxies + comments.forEach[ + setProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY, false) + ] + ] + } + + if (!isConnector) { + // Set size to be square and at least 34 (same as minimal node size) + val proxyBounds = PlacementUtil.estimateSize(proxy) + val minSize = 34 + val bigEnough = proxyBounds.width > 10 && proxyBounds.height > 10 + proxy.width = bigEnough ? proxyBounds.width : minSize + proxy.height = bigEnough ? proxyBounds.height : minSize + // Use this size to make proxies square + // val size = Math.max(minSize, Math.max(proxyBounds.width, proxyBounds.height)) + // Use this to make proxies always be at least minSize x minSize + // proxy.width = Math.max(minSize, proxyBounds.width) + } else { + val connectorSize = 7 + proxy.width = connectorSize + proxy.height = connectorSize + } + + node.setProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY, true) + node.setProperty(KlighdProperties.PROXY_VIEW_PROXY_RENDERING, proxy.data) return returnNodes } diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend index 2f3985866e..eb7ba92745 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend @@ -38,6 +38,9 @@ import static de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.* import static de.cau.cs.kieler.sccharts.ui.synthesis.styles.ColorStore.Color.* import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* +import de.cau.cs.kieler.klighd.util.KlighdProperties +import de.cau.cs.kieler.sccharts.ui.synthesis.filtering.SCChartsSemanticFilterTags +import de.cau.cs.kieler.klighd.KlighdOptions /** * Transforms {@link Transition} into {@link KEdge} diagram elements. @@ -68,7 +71,9 @@ class TransitionSynthesis extends SubSynthesis { override performTranformation(Transition transition) { val edge = transition.createEdge().associateWith(transition); edge.configureEdgeLOD(transition) - + + val semanticTags = newArrayList(SCChartsSemanticFilterTags.TRANSITION) + if (USE_KLAY.booleanValue) { edge.setLayoutOption(LayeredOptions::SPACING_EDGE_LABEL, 3.0) if (transition.isImplicitlyImmediate) { @@ -166,9 +171,16 @@ class TransitionSynthesis extends SubSynthesis { } switch (transition.preemption) { - case STRONG: edge.addStrongAbortionDecorator - case TERMINATION: edge.addNormalTerminationDecorator + case STRONG:{ + edge.addStrongAbortionDecorator + semanticTags.add(SCChartsSemanticFilterTags.ABORTING_TRANSITION) + } + case TERMINATION:{ + edge.addNormalTerminationDecorator + semanticTags.add(SCChartsSemanticFilterTags.TERMINATING_TRANSITION) + } default: { + semanticTags.add(SCChartsSemanticFilterTags.WEAK_TRANSITION) } }; @@ -192,6 +204,8 @@ class TransitionSynthesis extends SubSynthesis { ] } + edge.setProperty(KlighdProperties.SEMANTIC_FILTER_TAGS, semanticTags) + return newArrayList(edge) } diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/filtering/SCChartsSemanticFilterRules.java b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/filtering/SCChartsSemanticFilterRules.java new file mode 100644 index 0000000000..7385110192 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/filtering/SCChartsSemanticFilterRules.java @@ -0,0 +1,68 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.synthesis.filtering; + +import de.cau.cs.kieler.klighd.filtering.*; + +/** + * Contains semantic filter rules for SCCharts. + * + * @author stu215592 + */ +public class SCChartsSemanticFilterRules { + // Not sure wy those strings were added since they throw errors since the constructor only takes the Tag + /** Rule to exclude elements that are states. */ + public static final SemanticFilterRule NO_STATES = + new NegationConnective(SCChartsSemanticFilterTags.STATE);//, "Don't Show States"); + /** Rule to exclude elements that are regions. */ + public static final SemanticFilterRule NO_REGIONS = + new NegationConnective(SCChartsSemanticFilterTags.REGION);//, "Don't Show Regions"); + + /** Rule to exclude elements that are simple states. */ + public static final SemanticFilterRule NO_SIMPLE_STATE = new NegationConnective( + SCChartsSemanticFilterTags.SIMPLE_STATE);//, "Don't Show Simple States"); + /** Rule to exclude elements that are hierarchical states. */ + public static final SemanticFilterRule NO_HIERARCHICAL_STATE = new NegationConnective( + SCChartsSemanticFilterTags.HIERARCHICAL_STATE);//, "Don't Show Hierarchical States"); + /** Rule to exclude elements that are connector states. */ + public static final SemanticFilterRule NO_CONNECTOR_STATE = new NegationConnective( + SCChartsSemanticFilterTags.CONNECTOR_STATE);//, "Don't Show Connector States"); + /** Rule to exclude elements that are controlflow regions. */ + public static final SemanticFilterRule NO_CONTROLFLOW_REGION = new NegationConnective( + SCChartsSemanticFilterTags.CONTROLFLOW_REGION);//, "Don't Show Controlflow Regions"); + /** Rule to exclude elements that are dataflow regions. */ + public static final SemanticFilterRule NO_DATAFLOW_REGION = new NegationConnective( + SCChartsSemanticFilterTags.DATAFLOW_REGION);//, "Don't Show Dataflow Regions"); + + /** Rule to exclude elements that are initial states. */ + public static final SemanticFilterRule NO_INITIAL_STATE = new NegationConnective( + SCChartsSemanticFilterTags.INITIAL_STATE);//, "Don't Show Initial States"); + /** Rule to exclude elements that are final states. */ + public static final SemanticFilterRule NO_FINAL_STATE = new NegationConnective( + SCChartsSemanticFilterTags.FINAL_STATE);//, "Don't Show Final States"); + + /** Rule to exclude elements that are either initial XOR final states. */ + public static final SemanticFilterRule NO_INITIAL_XOR_FINAL_STATE = new NegationConnective( + new OrConnective( + new AndConnective(SCChartsSemanticFilterTags.INITIAL_STATE, NO_FINAL_STATE), + new AndConnective(NO_INITIAL_STATE, SCChartsSemanticFilterTags.FINAL_STATE)));//, "Don't Show Initial XOR Final States"); // TODO + // new NegationConnective( + // new OrConnective( + // new AndConnective(SCChartsSemanticFilterTags.INITIAL_STATE, + // new NegationConnective(SCChartsSemanticFilterTags.FINAL_STATE)), + // new AndConnective( + // new NegationConnective( + // SCChartsSemanticFilterTags.INITIAL_STATE), + // SCChartsSemanticFilterTags.FINAL_STATE)), + // "Don't Show Initial XOR Final States"); +} diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/filtering/SCChartsSemanticFilterTags.java b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/filtering/SCChartsSemanticFilterTags.java new file mode 100644 index 0000000000..c2763c0fdc --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/filtering/SCChartsSemanticFilterTags.java @@ -0,0 +1,56 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright ${year} by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.synthesis.filtering; + +import de.cau.cs.kieler.klighd.filtering.SemanticFilterTag; + +/** + * Contains semantic filter tags for SCCharts. + * + * @author stu215592 + */ +public class SCChartsSemanticFilterTags { + /** Tag giving semantic meaning that the element is a state. */ + public static final SemanticFilterTag STATE = new SemanticFilterTag("state"); + /** Tag giving semantic meaning that the element is a region. */ + public static final SemanticFilterTag REGION = new SemanticFilterTag("region"); + + /** Tag giving semantic meaning that the element is a simple state. */ + public static final SemanticFilterTag SIMPLE_STATE = new SemanticFilterTag("simpleState"); + /** Tag giving semantic meaning that the element is a hierarchical state. */ + public static final SemanticFilterTag HIERARCHICAL_STATE = + new SemanticFilterTag("hierarchicalState"); + /** Tag giving semantic meaning that the element is a connector state. */ + public static final SemanticFilterTag CONNECTOR_STATE = new SemanticFilterTag("connectorState"); + /** Tag giving semantic meaning that the element is a controlflow region. */ + public static final SemanticFilterTag CONTROLFLOW_REGION = + new SemanticFilterTag("controlflowRegion"); + /** Tag giving semantic meaning that the element is a dataflow region. */ + public static final SemanticFilterTag DATAFLOW_REGION = new SemanticFilterTag("dataflowRegion"); + + /** Tag giving semantic meaning that the element is a transition. */ + public static final SemanticFilterTag TRANSITION = new SemanticFilterTag("transition"); + /** Tag giving semantic meaning that the element is a transition. */ + public static final SemanticFilterTag WEAK_TRANSITION = new SemanticFilterTag("weakTransition"); + /** Tag giving semantic meaning that the element is a transition. */ + public static final SemanticFilterTag TERMINATING_TRANSITION = new SemanticFilterTag("terminatingTransition"); + /** Tag giving semantic meaning that the element is a transition. */ + public static final SemanticFilterTag ABORTING_TRANSITION = new SemanticFilterTag("abortingTransition"); + + /** Tag giving semantic meaning that the element is an initial state. */ + public static final SemanticFilterTag INITIAL_STATE = new SemanticFilterTag("initialState"); + /** Tag giving semantic meaning that the element is a dataflow region. */ + public static final SemanticFilterTag NOT_INITIAL_STATE = new SemanticFilterTag("notInitialState"); + /** Tag giving semantic meaning that the element is a final state. */ + public static final SemanticFilterTag FINAL_STATE = new SemanticFilterTag("finalState"); +} diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/hooks/StructuralEditingHook.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/hooks/StructuralEditingHook.xtend new file mode 100644 index 0000000000..b773541b4c --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/hooks/StructuralEditingHook.xtend @@ -0,0 +1,99 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2022 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.ui.synthesis.hooks + +import java.util.HashMap +import de.cau.cs.kieler.sccharts.Scope +import de.cau.cs.kieler.klighd.kgraph.KNode +import de.cau.cs.kieler.klighd.krendering.ViewSynthesisShared +import de.cau.cs.kieler.klighd.util.KlighdProperties +import de.cau.cs.kieler.klighd.structuredEditMsg.StructuredEditOptions +import de.cau.cs.kieler.sccharts.ui.synthesis.filtering.SCChartsSemanticFilterTags +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangeTargetStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.EditSemanticDeclarationAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.RenameStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.AddSuccessorStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.AddHierarchicalStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.AddTransitionAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ToggleFinalStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.DeleteAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.MakeInitialStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangeSourceStateAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangeTriggerEffectAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangePriorityAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangeToTerminatingTransitionAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangeToAbortingTransitionAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.ChangeToWeakTransitionAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.RenameRegionAction +import de.cau.cs.kieler.sccharts.ui.structuredProgramming.AddConcurrentRegionAction + +/** + * Class for adding a property to the root state of a scchart diagram + * @author fjo + */ +@ViewSynthesisShared +class StructuralEditingHook extends SynthesisHook { + + override finish(Scope scope, KNode node) { + val map = new HashMap() + + // all states support the following actions + map.put(SCChartsSemanticFilterTags.STATE, #[ + EditSemanticDeclarationAction.getMsg(), + RenameStateAction.getMsg(), + AddSuccessorStateAction.getMsg(), + AddHierarchicalStateAction.getMsg(), + AddTransitionAction.getMsg(), + ToggleFinalStateAction.getMsg(), + DeleteAction.getMsg() + ]) + + // Non initial states support one more action + map.put(SCChartsSemanticFilterTags.NOT_INITIAL_STATE, #[ + MakeInitialStateAction.getMsg() + ]) + + // Transitions supported actions + map.put(SCChartsSemanticFilterTags.TRANSITION, #[ + ChangeTargetStateAction.getMsg(), + ChangeSourceStateAction.getMsg(), + ChangeTriggerEffectAction.getMsg(), + DeleteAction.getMsg(), + ChangePriorityAction.getMsg() + ]) + // Depending on the type of transition we can change it to the other two options + map.put(SCChartsSemanticFilterTags.WEAK_TRANSITION, #[ + ChangeToTerminatingTransitionAction.getMsg(), + ChangeToAbortingTransitionAction.getMsg() + ]) + map.put(SCChartsSemanticFilterTags.ABORTING_TRANSITION, #[ + ChangeToWeakTransitionAction.getMsg(), + ChangeToTerminatingTransitionAction.getMsg() + ]) + map.put(SCChartsSemanticFilterTags.TERMINATING_TRANSITION, #[ + ChangeToWeakTransitionAction.getMsg(), + ChangeToAbortingTransitionAction.getMsg() + ]) + + // Actions supported by regions + map.put(SCChartsSemanticFilterTags.REGION, #[ + RenameRegionAction.getMsg(), + AddConcurrentRegionAction.getMsg(), + DeleteAction.getMsg() + ]) + + val options = new StructuredEditOptions(map) + // sets the property for the root node + node.setProperty(KlighdProperties.STRUCTURED_EDITING, options) + } +}