Child pages
  • Generator User Guide Demo7
Skip to end of metadata
Go to start of metadata

Generator User Guide Demo 7

In this final demo we will go back to Demo 3 and implement the desired capabilities in a slightly different way, so that we explore another area of the MPS generator - the weaving rules and root mapping rules.

In Demo 2, which we will build on here, we were generating java statements like:

to create a Swing component and add it to the application's content pane.

In Demo 7, just like in Demo 3, we are going to add support for component properties, which will require a generation of more complex initialization code - not just a constructor invocation. Moreover, the generated property initialization code is going to be different for different types of components.
Therefore we will choose a generation strategy that is capable of handling such requirements - instead of making use of a SWITCH macro we will use Weaving Rules, which will 'inject' component's initialization code into the DemoApp class.

New Language

Again, we need to setup a new language and copy some of the Demo 2 generator artifacts to it.

  • create a new language: 'generator_demo.demoLang7'
  • in the language properties dialog add extended dependencies on 'jetbrains.mps.sampleXML' as well as on 'jetbrains.mps.baseLanguage'
  • create a new generator for this language, if it does not exist (see Demo 1 for details)
  • delete the (empty) mapping configuration 'main' from the demoLang7 generator (as in Demo 2, we will copy all needed parts from the demoLang2 generator to the demoLang7 generator)
  • copy-paste the mapping configuration 'main' from the demoLang2 generator to the demoLang7 generator

    when pasting nodes - don't forget to exclude the demoLang2 generator model from imports

    Icon

    See Demo 2 for details.

  • copy-paste the 'DemoApp' template from the demoLang2 generator to the demoLang7 generator

Enhance the language

This time, instead of having all input XML Elements represented as standalone roots in the input model, we're going to wrap them into a single root concept, called XMLDocument. The demoLang7 language will introduce this new concept as well as its editor:


The concept may hold a collection of XML Elements and it will present them on the screen as a vertical collection.

addContent(container)

  • open the DemoApp template
  • add a static void method 'addContent(Container)'
  • in the 'main()' method find the statement:

  • replace the statement above with statement below:

New Test Model

Before going on with the generator let's create a new input test model:

  • go to the 'test_models' solution
  • create a new model 'test7'
  • int the Used Languages tab add 'jetbrains.mps.sampleXML' and 'jetbrains.mps.samples.generator_demo.demoLang7'
  • add a new XMLDocument root node and type the code:

Now, let's define the semantics for these documents.

Root mapping rules

We used to utilize conditional root rules for instantiating the DemoApp class template in the previous demos. In Demo 7 we'll use a different mechanism - root mapping rules. In the 'main' mapping configuration delete the entry in the conditional root rules section as well as the one in the abandon roots section and instead add a root mapping rule:

This rule will replace an XMLDocument with our desired DemoApp class template.

Weaving Rules

Weaving rules are a vehicle that can add extra nodes into the output model. In our case we'll utilize them to insert code for adding swing components to their containers.

  • return to the demoLang7 generator and open the mapping configuration 'main' in the editor
  • create a new Weaving Rule (press Insert or _Enter _while cursor is in the weaving rules section)
  • choose the rule applicable concept - 'Element'
  • enter the rule condition as shown below:

Weaving Context

Weaving Rules inject additional generated code into other generated code. The Weaving Context is the exact place to which the Weaving Rule will inject the code. (the injected nodes will be the children of the context node).

In our case, we will inject the component's set-up code into our DemoApp class. Thus, we have to find the generated DemoApp class in the output model and pass this class to our weaving rule as its weaving context.

Mapping Label

We will use a mapping label to find the generated 'DemoApp' node in the output model. As already discussed in Demo 3, they serve as registries for generated nodes so they could be retrieved later by other parts of the generator. This loosens the coupling between independent generator parts.

Declaring Mapping Label

Before a mapping label can be used it must be declared in the mapping configuration:

  • go to the mapping labels section of the main mapping configuration
  • add a new mapping label (press Insert or Enter)
  • give it a name: 'main_class'
  • select 'ClassConcept' as the label's output concept, since we're going to store ClassConcepts in the mapping label

mapping label

Icon

In our case, we are going to attach the mapping label to a rule, which generates the 'Demo App' class (a Root mapping rule).
That's why we have chosen 'ClassConcept' as the label's output concept.
The expression (we will use it soon):

will then have type: 'node<ClassConcept>'.
The fact that the 'get output' expression has a type, provides no benefits for us in this particular case, but in general, it is a very helpful feature.

Attaching the Label to a Rule

Attach the mapping label 'main_class' to the Root mapping rule using the rule's inspector:

Using the Mapping Label for Finding the desired output node

Now, return to Weaving Rule and enter the following code into the context function:


This will now correctly resolve the DemoApp class corresponding to the processed XMLDocument and pass it into the weaving rule, so that it can add new methods to it. Notice the use of the jetbrains.mps.smodel query node.ancestor, which retrieves the closest node's ancestor of the specified concept in the AST.

External Template

Now let's create a template for this weaving rule, which will be used to generate the code to add to the DempApp class:

  • type 'weave_Button' in the red 'choose consequence' cell
  • press Alt-Enter and choose 'New Template' (apply intention)
  • open the template in the editor (use Ctrl-<left-click> or Ctrl+B on reference cell)

The template should already have a Class set as the template's content node, because that is the type of elements stored in the main_class mapping label. If not - choose 'ClassConcept' as the template's content node.

  • give the class a name

    note

    Icon

    You don't really have to give the class a name. This class is not going to be generated.
    This class will serve as a context or a place, into which we can put the newly generated code
    We will mark this 'real code' with the template fragment tags.

  • add a static method 'createComponent()'
  • create a template fragment: select the whole method declaration and press Ctrl-Shit+F:

template fragment in weaving rules

Icon

Code tagged as template fragment (a method declaration in this case) is inserted into the weaving context node (DemoApp class) during the generation process.

IF-macro

The following steps are very similar to what we've done in Demo 3, since we're effectively generating the same code just using a different technique - weaving rules instead of reduction rules.

Inside the 'createComponent()' method we are going to create and initialize a JButton component.
Optionally, if the input Element has an attribute 'text', then we will generate a statement:

where the text is the string specified in the 'text' attribute in input Element.

  • enter the following code in the body of the 'createComponent()' method
  • create an IF-macro around the 'component.setText("text");' statement (select the whole (warning) statement, press Ctrl-Shift+M)
  • enter the code for the condition function of the IF-macro, which will check the presence of the 'text' attribute


  • create a property-macro inside the string literal "text"
  • in the value function of the property-macro enter code that returns the value of the 'text' attribute of the input Element

Complete the Generator

To complete this step we have to create another Weaving Rule, which would be applicable to the 'label' Element.

  • open the 'main' mapping configuration ineditor
  • select the weaving rule, which we have just created for 'button' (select the whole node: put cursorinside the rule, use Ctrl+W to expand selection)
  • press Ctrl+D (duplicate) to create an identical rule right next to the original one
  • in the condition function, in the statement

    replace "button" with "label"

  • in theprojecttree select the template node 'weave_Button'
  • duplicate this template node using the Clone Root command in the popup menu
  • in the editor rename this new template to 'weave_Label'
  • in the template code replace the statement

  • with


  • re-auto-complete 'setText' in the statement if the method call is not resolved automatically

  • attach the 'weave_Label' template to the second weaving rule in the 'main' mapping configuration

  • re-generate generator model

First Test (Error)

Try to rebuild the language and generate files from model 'test7'.
Generation should run with no problems but compilation will fail with an error:

Click on the error message to view the error in generated code:

The problem is that our weaving rules are always injecting method declaration with the same name: 'createComponent()'

Generating Unique Names

To make the names of each generated 'createComponent()' method unique we will create another property-macro:

  • open the 'weave_Button' template in editor
  • add a property-macro to the name of 'createComponent()' method
  • enter the code in its value function as shown

The templateValue is going to be "createComponent" (i.e. the method's name as written in the template).

  • make similar changes in the 'weave_Label' template
  • re-generate generator model

Second Test

Generate files form the 'test7' model - this time we should get no error.

Preview the generated text for 'test7':

The code has no compilation problems, but it is still not working, because the body of the 'addContent()' method is empty. We're not calling the creational methods that we've just weaved in. Let's fix that by weaving the following code:

into the body of the 'addContent()' method.

Second Template Fragment

To generate a method call of the 'createComponent()' method, we will add another weaving rule and a template. The calls to all the generated 'createComponent()' methods must be weaved into the 'addContent()' method of the main generated class, thus we first have to store the 'addContent()' method in a mapping label in order to refer to it from weaving rules.

We can use the LABEL node macro to store the generated method in the mapping label:

Now a new weaving rule needs to be added to the weaving rules section of the main mapping configuration that will insert the calls to the individual 'createComponent()' methods into the 'addContent()' method:

Notice that the context retrieves the 'addContent()' method from the mapping label and returns the method's body as the node that will have the template fragments weaved in.

The weave_ElementInitialization template will insert the call to the individual 'createComponent()' methods into its context node (a method body, i.e. a StatementList):

Icon

Be sure you tagged the whole statement as a template fragment, including the trailing semicolon.

 

Add a parameter 'Container container' to the method declaration ((warning) This parameter must have the same name as parameter in the actual 'addContent()' method in the 'DemoApp' template).

 

Icon

If you decide to name the Container parameter differently, so that there's no longer a name match between the 'container' in the template fragment and the container parameter in the generated 'DemoApp.addContent()' method, you will need to resolve the actual parameter of 'DemoApp.addContent()' with a reference macro:

The actual 'createComponent()' method must be obtained from somewhere - a mapping label would work well, so we need to first create a new mapping label to hold these methods and insert the methods into the mapping label:

The LABEL node macro will help us have the 'createComponent()' methods stored in the createComponentMethods mapping label for both Buttons and Labels in their respective weaving rules/templates:

This should be done for both weave_Button and weave_Label.

Now we can return to the weave_ElementInitialization template and specify a reference macro on the call to 'createComponent()' to obtain the appropriate 'createComponent()' method from the createComponentMethods mapping label:

Third Test

Now you can make the generator model a generate files from the 'test7' model in the 'test_models' solution.

Now you get correct and complete code for our simple application. 

  • No labels