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

Generator User Guide Demo 4

In this demo, we will further evolve the demoLang3 generator (see Demo 3) and add support for a panel component. Unlike buttons or labels, panels can contain other components, including other panels. So we get recursion in the generator. We will see how reduction can help to solve problems of this kind.

New Language

We'll reuse a great part the demoLang3 generator in demoLang4.

  • create new language 'generator_demo.demoLang4'
  • in language properties dialog add extended languages : 'jetbrains.mps.sampleXML' and '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 demoLang4 generator (we will copy all needed parts from the demoLang3 generator into the demoLang4 generator)
  • copy-paste all nodes from the demoLang3 generator into demoLang4 generator

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

    Icon

    See Demo 2 for details.

    copy-paste all nodes in one step

    Icon

    Doing so you won't be even offered to import the generator model from demoLang3 generator.

Template for Panel

In-line with how buttons and labels are generated, we'll create a new template to generate code for panels - insert_Panel. As this template is going to be very similar to, say, insert_Label, we will create the new template as a copy of one of the existing ones.

  • select (in the tree) the insert_Label template in the demoLang4 generator and invoke Clone Root command in the popup-menu
  • in the editor - rename the new template: insert_Label -> insert_Panel
  • in the template code - replace JLabel with JPanel
  • remove the 'setText(...)' statement - panels don't support the 'text' property

  • in the switch_Element template switch adds an entry for panels:

  • re-generate the generator

First Test

Let's create a 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 demoLang3 -> demoLang4 (See Demo 2 for details)
  • in the model 'test4' add a new document 'Panel' with a root element - 'panel'
  • add an attribute: 'background="white"' to the 'panel' element
  • add a couple of 'label' elements to the 'panel' element:

  • generate files for the 'test4' model
  • preview the generated files

The generated UI clearly ignores that the 'Hello MPS!' labels should be nested inside the new panel. Instead, they are added to the frame directly. This is because our generator currently does not make any difference between root and non-root elements and simply turns them all into a top-level visual components. For each element in the input model the generator inserts a static method declaration 'createComponent()' into the 'DemoApp' class (which is perfectly okay) and generates a corresponding method call inside the 'addContent()' method in the 'DemoApp' class (which we need to change, since this should only be done for root elements).

The current implementation flattens our component hierarchy (all components are added directly to the application's content pane).

Restricting the LOOP macro to root elements

The first problem to attack is the LOOP macro in the DemoApp template, which should only iterate through root elements to generate container.add() calls for them. The elements nested inside panels, must not be included in the loop since it should be the containing panels that add them to the UI at some point later.

Let's alter the first LOOP macro. Notice also, that the second LOOP macro, which generated static method declarations for each Element, remains untouched.

Since we're now looping over Documents, not Elements, we need to alter the reference macro inside the LOOP.


If you rebuild the language and see the generated files, you'll notice that we're no longer adding the nested components into the top-most application frame.


However, we're not adding them into the panel, either. So this is what we'll fix now. We will use the COPY_SRC-macro for that.

COPY_SRC-macro

COPY_SRC macros replace the wrapped dummy piece of template code with a node from the input model. If this node has a reduction rule defined, it will be reduced before being added into the output model. In our case, we need panels to have their child Elements correctly added as visual components to the panel's visual representation. If, for example, our concrete Panel get converted into a JPanel, we need its two child Labels to be converted into JLabels and these JLabels need to be added to the JPanel. The conversion part is already done for all Elements correctly irrespective of their nesting thanks to the second LOOP macro in the DemoApp template. The addition part is only done partially, thought. For the nested Elements, it must be the Panel that adds them.

  • open insert_Panel
  • add code to add null to the component
  • wrap the whole statement (including the; character) with a LOOP macro that iterates over the content of the XML Panel



We obviously do not want to add nulls into the panel, do we? Now, the trick with the COPY_SRC macro - we'll add the current node, which refers to the Panel's child_ Element_ that we currently iterate over with the LOOP macro. The COPY_SRC macro will take care of converting Element into a swing component before being added to the component.

 

COPY_SRC-macro

Icon

While processing COPY_SRC-macros, MPS creates a copy of the input node (aka mapped node) in output model. The wrapped node in the template code is ignored.

Well, it may sound really weird - are we going to generate java code where XML element is passed as a parameter in the method call? Of course, we are not, and the trick is that the 'copying' during a generation is not that simple. Whenever the MPS generator performs copying of an input node to output model, it tries to reduce this node (i.e. find and apply a reduction rule).

Thus, our input xml element can easily become something else after being 'copied' by the MPS generator. Now we will define what that 'something else' is going to be.

Reduction Rule

Reduction rules define transformations of source-language elements into target-language elements. For example, we want to transform XML Elements, which represent a Panel's children, into Java swing components that have been created by already generated static factory methods. In our particular case, we want to replace the Panel's children with calls to static methods. Luckily, the static methods to look for have been preserved in the method mapping label, so we can easily get hold of the correct one.

  • go to the mapping configuration in the demoLang4 generator and create a Reduction Rule, which is applicable to Elements
  • apply the intention (Alt + Enter) to create a new template for this rule

  • 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):

template fragment

Icon

Make sure that only the method call expression is marked as a template fragment, not the whole statement. The rightmost ';' symbol should be outside the template fragment, otherwise, you'd be wrapping a Statement not a MethodCall.

  • add reference-macro for method name in method call expression - we'll retrieve the method declaration from the method mapping label:

Final Test

Rebuild the generator and preview generated text for the 'test4' model.

This time both labels "Hello" and "MPS!" are correctly added to the content pane. We're done!

  • No labels