State machines, as they are used today in software projects, are an effective way of developing software for a lot of different applications. They have been used in the embedded systems domain to model reactive systems for decades. Highly-sophisticated tools, like and , help embedded systems engineers to model and simulate the behavior of their systems and generate high-quality code out of the statechart models. Mathworks Stateflow YAKINDU Statechart Tools In recent times, there has been a trend towards state machines for web applications. Frameworks, like and , support developers in using finite-state machines for front-end and back-end web development. Wouldn’t it be nice to reuse existing tools with all their modeling and simulation capabilities for modern web frameworks? XState Spring Statemachine What you will learn in this article This article is a step-by-step guide on how to develop custom code generators for . All parts of YAKINDU Statecharts that we use throughout this article are open source. We will use as the target framework to explain the development of our custom code generator. And we will use — a flexible and expressive dialect of Java with a lot of features that are helpful for code generator development. YAKINDU Statechart Tools Spring Statemachine Xtend Grab your copy of YAKINDU Statechart Tools , and let’s start coding! here Spring Statemachine Turnstile Example Let’s have a look at a simple example and how it is written in Spring Statemachine. The turnstile example was taken from the . It consists of two states, and . If the event is raised, the turnstile enters the state, if the event is raised, the turnstile goes back to the state. Spring project example repository Locked Unlocked coin Unlocked push Locked As you can see in the following code listing, only 29 lines of Java code are required to have a running example of the turnstile in Spring Statemachine. { States { LOCKED, UNLOCKED } Events { COIN, PUSH } { states.withStates().initial(States.LOCKED) .states(EnumSet.allOf(States.class)); } { transitions.withExternal().source(States.LOCKED).target(States.UNLOCKED) .event(Events.COIN).and().withExternal().source(States.UNLOCKED) .target(States.LOCKED).event(Events.PUSH); } } @Configuration @EnableStateMachine public < , > class TurnstileConfig extends EnumStateMachineConfigurerAdapter States Events public static enum public static enum @Override Exception public void configure (StateMachineStateConfigurer<States, Events> states) throws @Override Exception public void configure ( StateMachineTransitionConfigurer<States, Events> transitions) throws The class starts with two annotations — and that are required for the Spring runtime environment to set up things properly. For more details on how the instantiation with spring works, check out the . TurnstileConfig @Configuration @EnableStatemachine — Spring Statemachine online documentation The class extends the framework class. This is a generic type that takes two classes as type arguments — the a nd enumerations. These enumerations contain literals for every and within the state machine. There are two methods, which are derived from the base class. TurnstileConfig EnumStateMachineConfigurerAdapter States Events State Event configure They are used to define the and connect them to each other via . All are added to the states’ configuration, and the state is marked to be the initial state of the state machine. The method adds the with source and target and , respectively, to the configuration. States Transitions States Locked configure Transitions States Events In the next part, we will set up a new generator project and see how we can generate that code from our example model. Creating the Code Generator Project Creating custom code generators is a built-in concept in You can develop custom code generators directly within your project workspace. You can choose between or Java for implementing the code generator templates. Throughout this example, we will use Xtend as the programming language. YAKINDU Statechart Tools. Xtend Xtend compiles into optimized, readable and pretty-printed Java code. Java and Xtend have a lot in common syntactically and semantically. Xtend is aimed to improve several aspects of Java and is especially useful when developing code generators. Since the output is plain Java code, it is easily possible to integrate Xtend code with any existing Java library and vice versa. Xtend is — like Java — a statically-typed language with the ability to infer variable and expression types in most cases. This slims down the code and increases its readability. Features that are useful for the development of code generators are extension methods, lambda expressions, dispatch functions and especially template expressions. Don’t worry — if you are familiar with Java you will learn Xtend in no time. To set up a new generator project in , we will use the generator template from our example wizard. So start up YAKINDU Statechart Tools and do the following: YAKINDU Statechart Tools Select → - → . In the Wizard, select YAKINDU Statechart Examples and click . File New Example Next If it is the first time you use the example wizard, you have to click the button in order to load the latest examples from the . Download Github repository Next, select the from the menu on the left. Hit and click Install Dependencies Directly From the Example Wizard Custom Generator Template Install Dependencies Finish. A background task is scheduled that will install some additional components into YAKINDU Statechart Tools. They are required for custom code generator development. Confirm the appearing dialog with the terms and conditions and click . Next. Accept Install Once the installation is finished, a restart is required. That’s all you have to do to get started! 🎉 🎉 The custom code generator template Let us have a look at the generated artifacts and their purpose. Since Xtend code compiles to Java code, there is a source folder called . It contains only generated resources and should therefore not be added to a version control systems like Git. The folder contains the source code of our code generator. xtend-gen src YAKINDU Statechart Tools uses a dependency injection framework, Google Guice, to manage dependencies. All references to implementation types are handled in the . CustomGeneratorModule is the class containing the actual code generator implementation. In the folder, you will find the example state machine and the code generator model CustomGenerator model turnstile.sct turnstile.sgen. The code generator model is used to test our new code generator. When you right-click the file and select from the context menu, the generator is executed and creates a new file with the following contents: turnstile.sgen Generate Statechart Artifacts src-gen/turnstile.txt The name of the state machine is I am a region main region I am an entry of kind INITIAL I am a state Locked I am a state Unlocked 'turnstile' This is the default output of the initial generator. Congratulations, everything is set-up properly! 💪 The Custom Generator Template explained As previously stated, the code that is responsible for the generated output is located in the class. It is just a simple template that prints out the names of the states contained in the example model into a text file — not very useful right now 🙈. But it is a good start to dive deeper into the details of YAKINDU’s code generator API: CustomGenerator { { access.generateFile(sm.name + , sm.generate.toString()); } { «name» .toString } { } { } { } } class CustomGenerator implements ISGraphGenerator override generate (Statechart sm, GeneratorEntry entry, IFileSystemAccess access) '.txt' def dispatch String generate (Statechart it) '' ' The name of the state machine is ' ' «FOR Region region : regions» «region.generate» «ENDFOR» ' '' def dispatch String generate (Region it) '' ' I am a region «name» «FOR state : vertices» «state.generate» «ENDFOR» ' '' def dispatch String generate (State it) '' ' I am a state «name» «FOR region : regions» «region.generate» «ENDFOR» ' '' def dispatch String generate (Entry it) '' ' I am an entry of kind «it.kind.toString» ' '' The class implements the interface, which is the basic interface for all custom code generators. It defines only a single method with three parameters. The first parameter is of type and represents the actual model, in our case. The following figure gives an overview of the structure of the statechart model. CustomGenerator ISGraphGenerator generate sm Statechart turnstile.sct A contains zero or more contains zero or more can be either of type or of type A is a state that can not be active — an , , or . If a contains one or more , it is called a . Statechart Regions. A Region Vertices. A Vertex State Pseudostate. Pseudostate Entry Exit Choice Synchronization State Regions Composite State The second parameter is of type and allows access to the code generator features of the generator model entry GeneratorEntry turnstile.sgen . The last parameter, is of tye can be used to write the result to the file system. It is a file system abstraction that is preconfigured with the specified in the generator model. To create a new source file, the method can be used. It takes two arguments, the first one is the name of the file and the second one is the content. As the latter, we pass in the result of the call . We use here. access, IFileSystemAccess and target folders generateFile sm.generate.toString() Xtends extension methods The method creates the content that is written to the file. We use for readable string concatenation. Such a template expression is surrounded by triple single quotes(‘ ‘ ‘). Each character that is placed in between a pair of triple single quotes is taken as plain text, except for Xtend expressions like «something». generate Xtends template expressions Xtend expressions are surrounded by so-called guillemets. They are evaluated at runtime. In our example, the result of the «name» expression will be . Template expressions even allow control structures like loops or statements. Turnstile FOR IF We iterate over every and — again — call the generate method. selects the suitable method depending on the runtime type of the argument. Region Xtends dispatch keyword generate Customizing for Spring Statemachine Now that you are familiar with the basic language features lets have a look at how we can adopt this generic template to produce actual code for Spring Statemachine. Here is the example code that can handle basic features like , , and . States Regions Events, Entries org.yakindu.sct.generator.spring org.eclipse.xtext.generator.IFileSystemAccess org.yakindu.sct.generator.core.ISGraphGenerator org.yakindu.sct.model.sgen.GeneratorEntry org.yakindu.sct.model.sgraph.Entry org.yakindu.sct.model.sgraph.State org.yakindu.sct.model.sgraph.Statechart { { access.generateFile(statechart.name + + statechart.name.toFirstUpper + , statechart.code); } { , , transitions .and() ; } { regions.head.vertices.filter(Entry).head.outgoingTransitions.head.target.name.toUpperCase } ; org.springframework.context.annotation.Configuration; org.springframework.statemachine.config.EnableStateMachine; org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; org.springframework.statemachine.config.builders.StateMachineStateConfigurer; org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; «name».«name.toFirstUpper»Config.*; /** * (c) 2019 by Andreas Muelder */ package import import import import import import class SpringGenerator implements ISGraphGenerator override generate (Statechart statechart, GeneratorEntry entry, IFileSystemAccess access) "/" 'Config.java' def code (Statechart statechart) '' ' package «statechart.name»; «statechart.imports» @Configuration @EnableStateMachine public class «statechart.name.toFirstUpper»Config extends EnumStateMachineConfigurerAdapter<States, Events> { public static enum States { «FOR state : statechart.regions.map[vertices].flatten.filter(State) SEPARATOR ' '» «state.name.toUpperCase» «ENDFOR» } public static enum Events { «FOR event : statechart.scopes.map[events].flatten SEPARATOR ' '» «event.name.toUpperCase» «ENDFOR» } @Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.«statechart.initialState») .states(EnumSet.allOf(States.class)); } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { «FOR transition : statechart.regions.head.vertices.filter(State).map[outgoingTransitions].flatten BEFORE ' ' SEPARATOR ' ' AFTER ' '» .withExternal() .source(States.«transition.source.name.toUpperCase») .target(States.«transition.target.name.toUpperCase») .event(Events.«transition.specification.toUpperCase») «ENDFOR» } } ' '' def protected initialState (Statechart it) def ''' java.util.EnumSet protected imports (Statechart it) import import import import import import import '' ' } The method takes a as an argument and returns a template expression. Please note: Everything that is not written between « and » is taken as a plain string. The loop iterates over all that are contained in the and generate an Enumeration Literal for each . The same is done for all . code Statechart For States Statechart State Events The first method sets the initial state of the and adds all the from the generated Enumeration above. The second method uses a expression to iterate over all Every is connected to its source and target and the triggering configure Statechart States configure For- Transitions. Transition State Event. This is everything you need to generate Spring Statemachine code for simple state machines. The full source code is available at . Do not forget to open a pull request if you’d like to provide additional features! 😃 GitHub