Show in Frame No Frame
Up Previous Next Title Page Index Contents Search

5.2.1 Starting from the beginning

Before writing any actual MERL code for our exercise we have to understand what exactly needs to be done. We have to answer the following questions:
*What design data do we need to extract from the models?
*How do we navigate to and access the data we need?
*Where and how do we output the extracted data?
In our example case, the answer to the first question is simple: we need to get the names of all Watches and the names of their buttons and applications. Studying the models will then reveal the answer to the second question: we have to look at each filled green Watch relationship in turn, getting the Watch’s name. We can follow its green role line to its Display object on the left to get its Buttons’ names. We can follow its red role line to its LogicalWatch object on the right, and from there down into the LogicalWatch’s subgraph to get the names of the applications there.

If we keep the third question simple for the moment and stick with plain text output to the screen, we can already write the first – albeit preliminary – version of our generator:
01 SpecSheet()
02 'SpecSheet for ' id newline newline
03 foreach >Watch
04 { 'Watch: ' id newline
05   do .Display
06   { '  Buttons: '
07     do :Buttons { id ' ' }
08     newline
09   }
10   do .LogicalWatch
11   { '  Apps: '
12     do decompositions
13     { foreach .State [Watch] { id ' ' } }
14     newline
15   }
16   newline
17 }
When studying the MERL code above it is important to understand the concept of execution context. When the generator is executing, it always operates in the context provided by the currently accessed model element and its environment. This context defines and constrains what design data can be retrieved and which other design elements can be accessed directly at that time. For example, in the context of a Graph, we can always retrieve the property values of the Graph itself and navigate to design elements that are directly in that Graph: objects, relationships, ports and roles. Navigating to an object in the Graph will change this focus – the context is now that of the object. From the context of the object we can retrieve its property values, navigate to roles attached to the object, or navigate to decomposition or explosion subgraphs associated with it.

Armed with this understanding of the execution context, it is now easier to decode our example MERL code. After the obligatory generator header (line 1) we will first print out the heading text for the specification sheet (line 2). The heading text is a combination of a constant piece of text (‘SpecSheet for ’) and the name of the WatchFamily graph fetched with the id command, followed by two newline characters. It is important to note that the id command operates on the current context, here the current graph itself – it will return the value of this graph’s identifying property.

We continue by looping over all Watch relationships found in the Graph (line 3). The foreach loop iterates over each element of the current graph matching the type specified. Inside this loop the context will be that of the currently accessed Watch relationship – therefore the id command on line 4 will now fetch the identifier from the Watch relationship. The identifier value will be output with a preceding text label and a newline character.

Lines 5–9 print out the list of button names. To fetch these names, we need to navigate from the current Watch relationship through a Display role to the attached Display object. Navigating within a graph uses a do loop, which can specify a chain of navigation steps. In our example, we want to follow the Display role into its Display object, which would look like this:
do ~Display.Display
We can, however, use a handy shortcut in this case: instead of explicitly stating which role to follow, we can just say which type of Object we want to end up in (as in line 5). Inside the do loop (line 6), we are now in the context of the single Display object attached to this Watch relationship. Here, we will first output a header for the list (line 6) and then proceed to retrieve the button names. The buttons are objects contained in a collection property of the Display object, so in order to collect their names we have to loop over that collection using the do command (line 7). Within the loop we will then print out the value of the identifying property of the Button at hand, followed by a blank character as a separator. The ‘}’ character at the end of line 7 marks the end of this loop, and the newline command in line 8 adds a line break after the Button list. Line 9 will then complete the enclosing loop that fetches the Display objects from the Watch relationships.

At this point we are back in the context of our Watch relationship, in the top-level loop. The next step is to collect the names of the Watch’s applications.
10   do .LogicalWatch
11   { '  Apps: '
12     do decompositions
13     { foreach .State [Watch] { id ' ' } }
14     newline
15   }
16   newline
17 }
We start by following the role from the current Watch relationship to its LogicalWatch object (line 10) and printing a header string for the application list (line 11). This time, the content that we want is not in a property of the LogicalWatch, but in a subgraph of it. To follow this link we use the do decompositions command (line 12) that will navigate into the associated subgraph. In this subgraph, we will now loop over all State [Watch] objects, which represent the applications, and retrieve their names. As before, to loop over graph elements we use foreach (line 13). Within the loop the id command will output the name of the State and we will follow it with a blank character as a separator. After that we will close both the innermost foreach loop and the enclosing do decompositions loop (line 13) and add a line break (line 14) after the list of application names. Line 15 finishes the loop over the LogicalWatches, returning to the context of the Watch relationship.

Line 16 will add an empty line into the output as separator between Watches before the next loop iteration. When all Watch relationships have been iterated over, line 17 finally closes the original foreach loop, and our generator ends there.

Executing this MERL code will result in the following output window:

Generator Output for SpecSheet

Figure 5–8. Generator ouput for ‘SpecSheet’ generator.

We have also put together a video lesson showing the execution of this example generator, highlighting the navigation and how it changes the execution context. The video can be watched at http://www.metacase.com/webcasts/MERL_Primer_Generator_Context.html. (In case you can't view the video below, it is also available here.)




Show in Frame No Frame
Up Previous Next Title Page Index Contents Search