Generator User Guide Demo 1
In this demo we will generate java application for each input XML Document.
The input model is going to be the 'generator_demo.test1' model in solution 'test_models' that we have created earlier.
This model uses 'jetbrains.mps.sampleXML' language and contains two XML Documents - 'Button' and 'Label'.
We will apply root mapping rule to each of those documents to generate output java application.
In the application's main method we will create a swing frame and add swing component to its content pane. The component is going to be either JButton or JLabel - it depends on the name of root element in input document.
- choose New Language in project's popup menu
- enter language name: 'generator_demo.L1'
- add language 'jetbrains.mps.sampleXML' to the extended languages section in the language properties dialog
- choose New Generator in the language 'generator_demo.L1' popup menu
- enter generator name: 'swing'
MPS will create new generator module inside the 'generator_demo.L1' language.
MPS will also create the generator model 'main@generator' and mapping configuration node 'main'.
Mapping configurations are serving as a generator's 'entry points' where all generator rules are declared and from where references on templates are made.
The model stereotype 'generator' (shown after symbol '@' in the model name) allows MPS to tell a generator model from a regular model.
Any node in generator model is interpreted as a template unless that node is a part of generator language itself.
For the instance, a mapping configuration node is a part of the generator language (the 'MappingConfiguration' concept is declared in language 'jetbrains.mps.lang.generator').
And the class node, which we will create on next step, is not a part of generator language and will be serving as a template in our generator.
Root Mapping Rule
Let's create our first rule which will generate Java application class for each input XML Document.
- open mapping configuration 'main' in editor
- add new rule in the mapping rules section (press Insert while cursor is in this section)
- choose the rule's applicable concept: 'Document' ()
As we want to generate Class for each input Document, the template for this rule is going to be Class - instance of concept 'ClassConcept' in baseLanguage.
The easiest way to create that template is to apply intention (see the on the left edge of the editor?):
- press Alt-Enter while cursor is in the red field '<no template>'
- choose New Root Template
- choose class.
- open the newly created class in editor (use Ctrl+left-click or Ctrl+B on the reference)
Property-macro is used to compute value of property of an output node at the time of generation.
In this demo we want to generate output class with the same name as was name of the input document.
Thus we will create a property-macro for the output class's name:
- put cursor somewhere inside the class name in editor
- press Ctrl-Shift+M and choose property macro in menu
- select macro node in editor (rendered as '$' symbol) and open its inspector (Alt-Shift+I)
- enter code into the value function as shown:
This value function will return name of input node (Document).
The main() method
In the main() method we are going to use classes from 'java.awt' and 'javax.swing' packages.
Therefore our first step will be importing of correspondent java_stub models into the generator model.
- press Ctrl-M (import model)
- choose 'javax.awt@java_stub' model
- press Ctrl-M again
- choose 'javax.swing@java_stub' model
Create the main() method as shown:
If you stumbled trying to enter 'String' - don't panic, you are not alone. Help is here
The last thing to do in this template is to replace 'null' argument in the 'container.add(null)' expression with expression creating a swing component.
We will generate 'new JButton()' expression if root element in the input document has name 'button', and 'new JLabel()' if the root element has name 'label'.
To perform this replacement we will 'wrap' the 'null' argument into a SWITCH-macro.
SWITCH-macro replaces 'wrapped' template node with other node depending on which case-condition is satisfied in the associated template switch.
To begin with, let's create new template switch named 'switch_JComponentByElementName'.
To start entering a condition code press Insert
The '<T...T>' things on next screenshot are called in-line template. See also note below the screenshot.
That's it. We are testing element's name. If it is 'button' we generate 'new JButton()' and if it is 'label' we generate 'new JLabel()'. If it is neither 'button' nor 'label' we are raising an error.
Now we are ready to attach SWITCH-macro to the 'null' argument node in the in the 'container.add(null)' expression.
- return to 'main()' method in the 'Document' template
- select the 'null' node in the 'container.add(null)' expression
- press Ctrl-Shift-M to add abstract node-macro
- press Ctrl+Space to invoke completion menu and choose $SWITCH$
- go to inspector of the $SWITCH$ macro
- enter code (see below) in the mapped node function (press Insert to start entering)
- make reference to the 'switch_JComponentByElementName' template switch node created earlier.
What does the mapped node function do and why do we need it?
Up to this point our input node has been a Document.
But the template switch 'switch_JComponentByElementName' expects an Element as its input.
Thus we have to replace the input node - Document, with new input node - Element.
The expression 'node.rootElement' returns root element of current input document which becomes the new input node.
Getting Ready to First Test
It is very important to remember that whatever changes you made in generator models, your last action should always be the same: re-generate generator models.
There is just one model in our generator so hit Shift-F9 (re-generate current model) when you finish editing template.
Generating Test Model
Let's return to model 'test1' in solution 'test_models'.
We have defined semantics for XML nodes in that model. Unfortunately, MPS knows nothing about that.
MPS doesn't understand that model 'test1' is now written in two languages - XML + L1 because we cannot really use language L1 in model 'test1' (language L1 defines no concepts of its own).
Therefore we will explicitly tell MPS to use L1 generator for generation of the model 'test1'.
- open properties of model 'test1'
- add language 'generator_demo.L1' to the Languages Engaged On Generation section
Run Generate Files command in the model's popup menu. MPS will generate two java files:
MPS will also compile these classes but it doesn't provide a way to run generated applications. For this reason, in addition to the MPS project we will create project in IntelliJ IDEA.
Setting Up IntelliJ IDEA Project
Launch IntelliJ IDEA and choose File -> New Project....
In the 'New Project' wizard:
- choose 'Create Java project from existing sources'
- enter project name: 'generator_demo_idea'
- choose project file location: 'C:\MPS_generator_demo'
- on the 2nd page of the wizard - make sure that source root 'c:\MPS_generator_demo\solutions\test_models\source_gen\' is checked (it is recommended to un-check language root 'L1\source_gen' - we don't need it for the purpose of this demo)
- skip the 3d page
- on the 4th page - accept the suggested module structure of the project ('Test_models' module)
The new IDEA project will look like on screenshot:
- open settings of the 'Test_models' module
- set the Compiler output : Output path : 'c:\MPS_generator_demo\solutions\test_models\classes_gen'
- clear the Compiler output : Test output path
The 'classes_gen' folder is the default output location for compiled classes in MPS.
Now that we have set-up an IntelliJ IDEA project we can easily review generated code and run generated application.
Running generated app in IntelliJ IDEA Project
Choose Run in popup menu of either of generated classes:
End of Demo 1