Generator User Guide Demo 4
In this demo we will evolve L3 generator (see Demo 3) and add support for panel component. Unlike button or label, panel can contain other components, including other panels, which involves recursion. We will see how reduction can help to solve problems of that kind.
- create new language 'generator_demo.L4'
- in language properties dialog add extended language : 'jetbrains.mps.sampleXML'
- create new generator for this language (see Demo 1 for details)
- delete (empty) mapping configuration 'main' from L4 generator (we will copy needed parts from L3 generator to L4 generator)
- copy-paste all nodes from L3 generator to L4 generator
Weaving Rule for Panel
Supporting of panel component will require another one weaving rule in L4 generator.
As this rule is going to be very similar to, say, 'weave_Label' rule, we will create this new rule by copying and modifying of existing rule:
- select (in tree) 'weave_Label' rule in L4 generator and invoke Clone Root command in popup-menu
- in editor - rename new rule: 'weave_Label' -> 'weave_Panel'
- in template code - replace JLabel with JPanel
- remove 'setText(...)' statement - panel doesn't support the 'text' property
- in mapping configuration add weaving rule applicable to a 'panel' element:
- re-generate generator
Let's create new test model:
- go to the 'test_models' solution
- clone model 'test3' to model 'test4'
- in the model properties dialog replace 'engaged on generation' language L3 -> L4 (See Demo 2 for details)
- in model 'test4' add new document 'Panel' with root element - 'panel'
- to the 'panel' element add attribute: 'background="white"'
- add a couple of 'label' elements to the 'panel' element:
- generate files form model 'test4'
- switch to IntelliJ IDEA and run the generated application:
The generated UI is looking fine except that 'Hello MPS!' labels are not shown on a white background (those labels are children of 'white panel' element in input model, thus we expect them to show up on a white background).
This is because our weaving rules do not make a difference between root and not-root elements. For each element in input model they insert method declaration 'createComponent()' into 'DemoApp' class (which is perfectly okay) and generate corresponding method call inside 'addContent()' method in the 'DemoApp' class.
The latter has turned out not a very good idea because this way we flatten our components hierarchy (all components are added directly to the application's content pane).
Replacing of Weaving with Reduction
Let's delete unhappy template fragment from all our weaving templates (there are: 'weave_Button', 'weave_Label' and 'weave_Panel').
Instead of injecting of 'container.add(..)' statements we are now going to enumerate input elements and generate 'container.add(..)' explicitly, right in the 'DemoApp' template.
- open the 'DemoApp' template
- find the 'addContent()' method and add 'container.add(..);' statement into its body
- wrap the statement (whole statement ) into a LOOP-macro
- enter code in mapped nodes function as shown:
The LOOP-macro will generate 'container.add(..);' statement for each document in input model.
Next, we will use COPY_SRC-macro to generate actual parameter in this method call.
- create COPY_SRC-macro wrapping the 'null' expression
- enter code in its mapped node function as shown:
The mapped node function is returning an XML element - root element of input document.
Well, now it sounds really weird - are we going to generate java code where XML element is passed as a parameter in method call?
Of course we are not, and the trick is that the 'copying' during a generation is not that simple.
Thus, our input xml element can easy become something else after being 'copied' by MPS generator.
And now we will define what that 'something else' is going to be.
- go to mapping configuration in L4 generator and create Reduction Rule which is applicable to Element
- apply intention to create template for this rule (Alt-Enter):
- open the 'reduce_Element' template in editor (Ctrl-click on reference)
- select ClassConcept as the rule content node
- add a static method to this class (name doesn't matter)
- create this method's call expression and make it a template fragment (Ctrl-Shift+F):
- add reference-macro for method name in method call expression:
In Demo 3 we already used reference-macro to resolve reference to color constant.
In that case it was enough to return name of a color constant from macro's referent function.
Unfortunately, we can't use the same approach here because we don't know exact name of method that we are going to call (method names 'createComponentX()' are generated with making use of create unique name service).
Resolving Reference Using a Mapping Label
To find 'createComponentN()' method generated for given input XML element we will once again use mapping label.
- in mapping configuration add new mapping label and give it name 'factory_method' (use inspector):
- in each weaving template (weave_Button/Label/Panel) attach this label to template fragment:
Now we can easily find generated method using this mapping label and input node.
- return to reference-macro in 'reduce_Element' template
- enter code in its referent function as shown:
Re-generate generator, generate files form model 'test4' and run the 'DemoApp' application.
This time labels "Hello" and "MPS!" are not added to content pane - good.
Our last task in this demo will be to add these labels as children components to our 'white panel'.
- open the 'weave_Panel' template in editor
- add code similar to that we have added to 'DemoApp' template earlier (i.e. 'component.add();' statement wrapped into $LOOP$ and with $COPY-SRC$ in parameter):
The LOOP-macro will enumerate children elements in current input node (which is 'panel' element) and generate 'component.add(..);' statement for each such child element.
COPY_SRC-macro will reduce input node (which is child of 'panel' element in this case).
Re-generate generator, generate files from model 'test4' and run the 'DemoApp':
End of Demo 4