Code Snippet
Table of Contents
Tutorial | Actifsource Tutorial – Code Snippet |
---|---|
Required Time | - 120 Minutes |
Prerequisites | - Actifsource Tutorial – Installing Actifsource - Actifsource Tutorial – Simple Service - Actifsource Tutorial – State Machine |
Goal | - Add conditional expression to a transition - Add code as an action to a transition which will be executed together with the transition |
Topics covered | - Code Snippet Editor - Code templates to generate code from code snippets |
Notation | ↪ To do ⓘ Information • Bold: Terms from actifsource or other technologies and tools • Bold underlined: actifsource Resources • Monospaced : User input• Italics: Important terms in current situation |
Disclaimer | The authors do not accept any liability arising out of the application or use of any information or equipment described herein. The information contained within this document is by its very nature incomplete. Therefore the authors accept no responsibility for the precise accuracy of the documentation contained herein. It should be used rather as a guide and starting point. |
Contact | Actifsource AG Täfernstrasse 37 5405 Baden-Dättwil Switzerland www.actifsource.com |
Trademark | Actifsource is a registered trademark of Actifsource AG in Switzerland, the EU, USA, and China. other names appearing on the site may be trademarks of their respective owners. |
Overview
- Create a meta-model for Statemachine instantiate Statemachine and write a code template to generate a simple implementation of your Statemachines in C++ as seen in the Actifsource Tutorial State Machine
- Add a Code Snippets with conditional expressions to Transition
- Create a Domain Diagram for an instance of Statemachine and show the conditions for Transitions in the diagram
- Generate code for Statemachines that checks the associated condition before executing a transition
- Add a Code Snippet with an action to Transition and generate code that executes the action together with the state transition
Part I Preparation
Setup a project ch.actifsource.tutorial.statemachine.code
with a meta-model for Statemachine create two instances of Statemachine and implement a code template for Statemachine as seen in the Actifsource Tutorial State Machine:
- Prepare a new Actifsource Project
ch.actifsource.tutorial.statemachine.code
- Create a meta-model for Statemachine
- Create two instances of type Statemachine namely Statemachine1 and Statemachine2
- Write a code template StatemachineImpl for the type Statemachine
- Use the package structure shown above
Part II Implement conditional transitions
First, we import all the resources needed to enable the support of Code Snippets in our project:
- Select the project
ch.actifsource.tutorial.statemachine.code
and choose Project->Properties from the main menu - In the properties dialog choose Actifsource and go the Built-in Dependencies tab
- Click on Add Builtin
- In the dialog Select a built-in dependency choose CODESNIPPET and click on OK
- Close the Properties dialog by clicking on OK
We add a Code Snippet relation to class Transition:
- Open the class Transition in the Resource Editor
- Add a (third) property to the class
- Choose StructuredCodeSnippetRelation in the dialog Type Selection
- Insert condition as name of the relation
- Create a new CodeSnippetRelationAspect as shown above
- Choose Cardinality0_1 as subjectCardinality and as objectCardinality
- Create a new language statement by calling the Content Assist and choosing CMinusCondition
We add a new Composition called variable to the class Statemachine:
- Open Transition in the ResourceEditor
- Add a new property and choose Composition in the Type Selection dialog
- Insert variable as the name of the relation
- Choose new
ch.actifsource.core.Class
as the range with the support of the Content Assist
- Insert
Variable
as name of the newly created class, which is opened automatically in the Resource Editor
A conditional expression for a Transition should be able to use all Variables that are owned by the Statemachine of the Transition as variables:
- Insert a RelationTokenProvider
- Define the Selector Transition.-transition.-state.counter which selects all Variables that are owned by the Statemachine of the Transition
- Choose
ch.actifsource.codesnippet.metamodel.TokenType.Variable
as tokenType
We add a condition to the state transition triggered by the event start that restricts the number of times the Statemachine can switch to the State Started
- Open the instance Statemachine1 in the ResourceEditor
- Add two statements for variable to Statemachine1
- Choose startCounter as the name of the first and startLimit as the name of the second Variable
We add a conditional expression to the state transition triggered by the Event start in the State Initialized
- Open the state Initialized in the Resource Editor and then open its Transition start
- Open the Code Snippet Editor by selecting the relation condition and pressing Enter
- Call the Content Assist in the Code Snippet Editor and choose startCounter
- Enter ' < ' in the Code Snippet Editor and call the Content Assist again
- Choose startLimit in the Content Assist
We enforce that the Statemachine can only switch a maximum of startLimit times to the State Started by adding the same conditional expression also to the Transition triggered by the Event start in the State Stopped
- Enter the conditional expression in the same way as on the previous page
Part III Create a Domain Diagram
We create a new Diagram Type called Statemachine in order to define properties of Domain Diagrams of Statemachines:
- Select the package
ch.actifsource.tutorial.statemachine.code.generic
and choose New -> Diagram Type
- Enter Statemachine as name for the newly created DiagramType in the New DiagramType Wizard
- Choose ch.actifsource.tutorial.statemachine.code.generic.Statemachine as RootClass
- Define allowedClass as ch.actifsource.tutorial.statemachine.code.generic.State
- Choose ShowPaletteEntry as paletteEntry
- Insert an allowedRelation of type AllowedIndirectRelation with selector State.transition.targetState
Now we create a new Domain Diagram for Statemachine1 based on the newly created Diagram Type:
- Select
ch.actifsource.tutorial.statemachine.code.specific
and choose New -> Domain Diagram
- Check the chosen settings in the open dialog and click Finish
- Choose Select in the Palette of the Diagram Editor and arrange the States such that all transitions are visible
Next, we will see how to display the conditional expressions associated with a Transition in the Domain Diagram. First, we create a function that generates the displayed text from the conditional expression:
- Select the package
ch.actifsource.tutorial.statemachine.code.generic
and choose New -> FunctionSpace
- Enter NameFunctions as name of the FunctionSpace
- Create a new FunctionContext with typeRef ch.actifsource.tutorial.statemachine.code.generic.Transition
- Create a new function in the FunctionContext and choose TemplateFunction in the Type Selection dialog
- Enter displayName as name of the TemplateFunction
- Open the TemplateFunction in the Template Function Editor by double-clicking on displayName in the Project Explorer
The function should display the name of the event triggering the Transition and in brackets the conditional expression if there is one:
- Enter Transition.event.name in the TemplateEditor followed by '[]'
- Place the cursor inside the brackets and choose Insert ColumnContext from the menu
- Choose Transition.condition as selector for the column context
- Enter
CodeSnippet.displayCodeSnippetSingleLine@DisplayCodeSnippet
in the column context. This function outputs a string that represents the expression in the Code Snippet as entered by the user, i.e., without parsing or processing it
We will use the newly created function displayName to define a NameAspect for Transitions
- Create a NameAspect with the Content Assist and choose TextSelectorAspectImplementation in the Select Decoration Type dialog
- Enter
Transition.displayName@NameFunction
as selector of the NameAspect, i.e., the name of the Transition will be the output of the function we have defined before
- Open the Statemachine1 Domain Diagram again and check that the Transitions are now displayed with their name including the conditional expressions as specified above
Part IV Generate code for conditional transitions
In this part, we will extend the template StatemachineImpl as implemented in the Actifsource Tutorial State Machine such that the statemachine executes a state transition only if the associated condition is fulfilled:
- Open the template StatemachineImpl in the Template Editor
We write an if-statement with the conditional expression associated with a Transition as condition:
- Insert a new Line Context and write the selector Transition.condition for the newly created context
- Write an if-statement in the new line context and insert a call to the function
toC@CodeSnippetToCode
on the CodeSnippet available in the this context as condition of the if-statement - Add opening and closing braces around the existing assignment expression
- Open the generated files
Statemachine1Impl.hpp
(overwritten) andStatemachine2Impl.hpp
(unchanged) and inspect the changes.
Part V Customized variable names
- The built-in template functions that we used so far generate the name of variables (and also functions) by calling simple name on the corresponding resource
- In this part we will change the generated variable names by adding a prefix to the variable names according to our naming convention
- Create a new Java source folder: select the project
ch.actifsource.tutorial.statemachine.code
and choose New -> Source Folder from the menu
- Insert aspects as name of the new folder
- Create a new package
ch.actifsource.tutorial.statemachine.code.generic
in the aspects folder: select the aspects folder and choose New -> Package from the menu
- Select the package
ch.actifsource.tutorial.statemachine.code.generic
in the folder aspects and choose New -> Interface from the menu - Write
IMyNameProvider
as name of the new interface - Choose
ch.actifsource.codesnippet.metamodel.template.INameProvider
as Extended interface
- Select the package
ch.actifsource.tutorial.statemachine.code.generic
in the folder aspects and choose New -> Class from the menu - Write
MyNameProviderLiteralAspect
as name of the new class - Choose
ch.actifsource.core.model.aspects.impl.AbstractStatelessAspectImpl
as Superclass - Choose
ch.actifsource.core.model.aspects.impl.IGenericLiteralAspect<IMyNameProvider>
as Interface - Click Finish
package ch.actifsource.tutorial.statemachine.code.generic;
import javax.annotation.CheckForNull;
import ch.actifsource.core.INode;
import ch.actifsource.core.Literal;
import ch.actifsource.core.job.IReadJobExecutor;
import ch.actifsource.core.model.aspects.impl.AbstractStatelessAspectImpl;
import ch.actifsource.core.model.aspects.impl.IGenericLiteralAspect;
import ch.actifsource.core.scope.IResourceScope;
public class MyNameProviderLiteralAspect extends AbstractStatelessAspectImpl implements IGenericLiteralAspect<IMyNameProvider> {
@Override
public boolean allowMultiline() {
return false;
}
@Override
public @CheckForNull String isValid(IReadJobExecutor executor, IResourceScope scope,
String value) {
return null;
}
@Override
public Literal create(IMyNameProvider value) {
return new Literal(value.toString());
}
@Override
public @CheckForNull String getJavaConstructionExpression(IReadJobExecutor executor,
IResourceScope scope, INode node) {
return null;
}
@Override
public @CheckForNull IMyNameProvider getValue(IReadJobExecutor executor,
IResourceScope scope, INode node) {
return null;
}
@Override
public Class<IMyNameProvider> getValueType() {
return IMyNameProvider.class;
}
}
- In the newly created class remove all TODO comments
- Write the statement
return MyNameProvider.class;
in the methodgetValueType()
- Write the statement
return new Literal(value.toString())
;
- Create a new Literal type: select the package
ch.actifsource.tutorial.statemachine.code.generic
in the folderasrc
and choose New -> Resource from the menu - Choose the type ch.actifsource.core.Literal as Type of the new Resource
- Write
MyNameProviderLiteral
as Name of the new Resource
- Open the new Literal in the Resource Editor
- Create a LiteralAspect with type JavaAspectImplementation and choose the class
ch.actifsource.tutorial.statemachine.code.generic.MyNameProviderLiteralAspect
as className - Let the MyNameProviderLiteral extend
ch.actifsource.codesnippet.metamodel.parsetree.template.NameProvider
, which is the default NameProvider and generates names by calling simpleName@BuiltIn
- Create a new FunctionSpace Select the package
ch.actifsource.tutorial.statemachine.code.generic
in theasrc
folder and choose New -> FunctionSpace - Insert TokenNameFunctions as the name of the FunctionSpace and click Finish
- Let the new FunctionSpace TokenNameFunctions extend from TokenToName
- Open the FunctionSpace TokenNameFunctions in the Resource Editor
- Create a new FunctionContext with typeRef MyNameProviderLiteral
- Create a new function and choose TemplateLineFunction from the Type Selection dialog
- Insert variableName as the name of the new TemplateLineFunction
- Create a parameter Param of type ClassType and with classRef ch.actifsource.core.Resource
- Insert
m_Resource.simpleName@BuiltIn
as text, i.e., the function appends the prefixm_
to the output ofResource.simpleName@Builtin
- Warning: Please choose the exact function name for variableName or functionName since these functions are overwritten
- Create a new FunctionContext with typeRef Statemachine
- Create a new function and choose JavaFunction from the Type Selection dialog
- Insert
generateNameProvider
as name of the function - Create a statement returnType of type LiteralType with literalRef MyNameProviderLiteral
@Override
public ch.actifsource.tutorial.statemachine.code.generic.IMyNameProvider generateNameProvider(final ch.actifsource.tutorial.statemachine.code.generic.javamodel.IStatemachine statemachine) {
/* Begin Protected Region [[835774eb-ee6c-11ef-8f29-099ac721970d]] */
return new IMyNameProvider() {};
/* End Protected Region [[835774eb-ee6c-11ef-8f29-099ac721970d]] */
}
- Save the FunctionSpace TokenNameFunctions
- Open the newly generated file TokenNameFunctions.java in the Java Editor
- Write the statement
return new IMyNameProvider(){}
inside the protected region in the method body of the methodgenerateNameProvider
- Save the file
- Open the template StatemachineImpl in the Template Editor
- Insert a new LineContext on the line after the case expressions
- Choose Transition.condition as the selector of the new context
- Insert a new LineContext on the same line
- Choose
Statemachine.generateNameProvider@TokenNameFunctions:NameProvider
as selector of the new context - Write an if-statement with
CodeSnippet.toCwithNameProvider@CodeSnippetToCode
Note that there is an error on the edited line because the parameter to the function cannot be resolved
-
Insert a new line context on the same line and choose CodeSnippet:CodeSnippet (This dummy context allows Actifsource to correctly and automatically resolve the parameter to the function from the contexts)
-
Save the template and make sure that the code is generated
- Open the file Statemachine1Impl.hpp and check that the variable names have been generated with the defined prefix "m_"
Part VI Implement actions for transitions
In this part, we will see how to add an action to a transition. We add the code corresponding to the action as a Code Snippet to the model. This code will be executed together with the transition:
- Open the class Transition in the Resource Editor and add a Code Snippet relation as already seen in Part II
- Insert action as name of the StructuredCodesnippetRelation choose subjectCardinality and objectCardinality Cardinality0_1
- Create a RelationTokenProvider with selector Transition.-transition.-state.variable and tokenType Variable
- Choose the language CMinus
We will now increment the startCounter each time that we switch to the State Started:
- Open Statemachine1 in the Resource Editor
- Add an action to the State Initialized and insert the code startCounter = startCounter + 1; into the Code Snippet Editor
- Add an action to the State Stopped and insert the code startCounter = startCounter + 1; into the Code Snippet Editor
- Open the template StatemachineImpl in the Template Editor
- Add a new line context after the assignment statement that updates the state
- Choose Transition.action as the selector of the state (to insert code for Transitions which have an action defined)
- Call the function
toCwithNameProvider@CodeSnippetToCode
on the CodeSnippet in the new line context
Note that there is an inconsistency because we have not yet added a NameProvider to our contexts which can be used as the parameter to the function
- Add a new line context on the same line as before and choose Statemachine.generateNameProvider@TokenNameFunctions:NameProvider as selector of the context
- Add another line context on the same line and choose the selector CodeSnippet:CodeSnippet (needed for the parameter matching)
- Save the template and make sure that the code in Statemachine1Impl.hpp is generated and overwritten
- Open the file Statemachine1Impl.hpp and check that the code that increments the counter has been correctly inserted
- Complete the generated classes by adding a member function initialize() and the missing variables (and probably adding the cases for the missing enumeration values to make the compiler happy).