Up Previous Next Title Page Index Contents

5.3.4 Control and navigation commands

This category contains the commands for navigating through the design models and structuring the generator definitions: conditionals, loops, following decomposition/explosion links and subgenerators.

If clause

Conditional clauses contain a condition and parts that are performed if the condition is true (‘then’ part) and parts that are performed if the condition is false (‘else’ part, optional). The following table summarizes the available comparison operators.
Operator
Semantics
=
Equal to. Comparison is alphabetic by default; add num for comparing strings as numbers, e.g. id = '3' num
=~
Matches wildcard: similar to = but allows the use of wildcards * (0 to many characters) and # (1 character) on the right side of the comparison
=/
Matches regular expression: similar to = but allows the use of regular expressions on the right side of the comparison
<>
Not equal to. Comparison is alphabetic by default; add num for comparing strings as numbers, e.g. id <> '3' num
< and <=
Less than, less than or equal to. Comparison is alphabetic by default; add num for comparing strings as numbers, e.g. id < '3' num
> and >=
Greater than, greater or equal to. Comparison is alphabetic by default; add num for comparing strings as numbers, e.g. id > '3' num
The ‘not’ keyword can be used before the condition to reverse it. There is a possibility to test whether a unary condition is true or false by giving only the value to test and leaving the condition and other value out. In this case the true part will be performed if the first value is not empty: unary tests can also be reversed by adding not before the value. Boolean properties can also be used as unary tests.

The left and right side of the condition can be either a simpleClause (e.g. id), a chainClause (e.g. :propertyName, >Transition:Name, explosions), a literal string (e.g. ‘java1.0’), or a variable reference (e.g. $counter). This example shows how a property value is tested:
if :property = 'value' then
   /* commands executed when true */
else 
   /* commands executed when false */
endif
More complex conditions can be expressed using the Boolean operators and, or, and not:
if not (:property1 = 'valueA'
   and :property2 = 'valueB')
   or (:property3 = 'valueC' 
   and :property4 = 'valueD')
then 
   /* commands executed when true */
else
   /* commands executed when false */
endif
Note that:
and has higher precedence than or: the condition c1 or c2 and c3 is equal to c1 or (c2 and c3).
not can be used in a condition at different levels: if not(not cond1 or not cond2)
you can use parentheses to improve the readability of a complex condition

Foreach loop

The main control structure of a generator loop is the foreach loop. In a typical generator the foreach clause selects all elements of the specified type to be operated on. A foreach loop can only be used when the element stack contains a graph, most often as the outermost loop at the top level of a generator. The element type to be looped over cannot be a property or port: only objects, roles and relationships are appropriate. The operations that will be performed on the retrieved elements are written within the curly brackets, {}.
foreach .objType
{ /* Operations for the elements */ }

Loop filtering and ordering

As well as selecting the type of the elements, a loop can specify extra parameters to filter and order the elements it iterates over. These parameters are specified by the where, orderby and unique keywords described below. The keywords may be used alone or together, but when used together they must come in this order, and will be applied in this order

The where keyword allows you to add a condition to select only certain elements. A simple form is:
foreach .objType; where :propName = 'value'
{ /* Operations for the elements */ }
Note the semicolon after the object type, to separate it from the where keyword. See the If clause for the full details of the syntax of conditions.

The orderby keyword allows you to specify the order in which to iterate over the selected elements. With no sort criteria specified, elements will be sorted by type, name, and oid. You can give multiple sort criteria per loop, e.g. sort first by name and then by type:
orderby id, type
Each criterion is a series of generator commands whose output will form the strings to be compared. The criteria are separated by commas as seen above.

You can add num after a criterion to specify numeric comparison
orderby :count num, :key num
You can add desc to the end of a criterion to specify descending order, i.e. reverse the sort. The default order can be made explicit with asc:
orderby :count num asc, :name desc
The unique keyword allows you to specify one or more uniqueness criteria for loops to avoid handling the same element multiple times. Each criterion is a series of generator commands whose output will form the strings which must be unique. For example, assume that you have a graph of Widgets. Each Widget has a property specifying which Library it is defined in, and that library must be included. If you want to output the include statements for the whole graph, without repeatedly including each library, you can do the following:
foreach .Widget; unique :Library
{ '#include <' :Library '>' newline }
If just unique is specified with no criteria, elements must be unique (i.e. not the same object etc., the same as comparing by oid). Foreach guarantees this kind of uniqueness anyway, but with do you could for instance be iterating over connected objects, and there could be two different relationships to the same object.

Do loop

The do loop iterates over a set of things, navigating from the current element. For example one could query the relationships of an object like this:
do >relType
{/* Operations for relationships */ }
Note that do loops differ from foreach. Foreach will always look at the current graph, and can only iterate over objects, roles or relationships directly contained in that graph. Do and dowhile will look at the current element, and iterate over those instances of the specified type that are attached to the current element in the current context. For this reason, foreach will normally be the first loop in a generator, and sub-loops will be do or dowhile, until you follow a link into a new graph.

The dowhile clause is a variant of do, differing only in that it skips any trailing literal strings or newlines on its last iteration over a given set of elements. This is useful for avoiding trailing separators such as commas or spaces after the last item to be output, e.g.
      do .Process {id ', '}	produces “1.0, 1.1, 1.2,”
 dowhile .Process {id ', '}	produces “1.0, 1.1, 1.2”
Do and dowhile can also with an argument of a complex property, either a collection or a property pointing to an object or other non-property. With a collection, the loop is executed once for each item in the collection. With a property whose value is an object or other non-property, the loop is executed once for that object; if there is no object attached, the loop is not executed.

Special do and dowhile loops also exist for iterating over lines of text properties, over decompositions and explosions, nested objects, the stack, and all graphs: see below. Also, note that the loop filtering and ordering clauses described above apply to do and dowhile loops in the same way as for foreach loops.

Iterating over lines of text properties, strings, variables and simple commands

The do and dowhile loops can also be used for iterating over the lines of a multi-line string: a property of data type Text, a MERL string, a MERL variable, or the output of a simple command like id.

For a Text property, the currently selected element should be the object, role, relationship of graph which has the text property, and the operator for the do loop should be the local name of the text property. The loop is then executed as many times as there are lines in that text property, including an empty last line if the text ends in a line break. Within the loop, id outputs the current line (with no line break character).
do :property
{ id }
This is particularly useful for translating line breaks in a property into character sequences which represent a new line or paragraph in a particular formatting language, such as RTF or HTML. The example below adds a <BR> tag in HTML; the newline is simply for clearer formatting of the HTML source.
do :Documentation
{ id '<BR>' newline }
It can also be used for programming languages where a comment is preceded by a certain character sequence, and is not allowed to extend over more than one line. The first example below shows how to make sure each line of a comment is preceded by the comment sequence, and the second example strings all the lines together into one long line, effectively replacing each carriage return with a space.
do :Documentation
{ '// ' id newline }

'// '
dowhile :Documentation
{ id ' ' }
The syntax for iterating over the lines of variables is similar:
do $myVar
{ id }
This can be useful in many situations, e.g. to execute a block multiple times with selected values, or to iterate over the lines of a text file read into a variable.

To iterate over literal strings, you can have the string spread over multiple lines:
do 'first
second
third'
{ id }
In all cases, you can add a translator as a suffix to the do loop argument, e.g. to translate some separator character into a newline:
to '%dotToNL
. \
' endto
do 'first.second.third'%dotToNL
{ id }
When iterating over the output of simple commands like id, using a translator is almost always necessary, as such outputs will generally only contain one line. The translator can split them into multiple lines by translating spaces to newlines etc.

Iterating over explosions

The links of an object, relationship or role into explosion subgraphs can be followed through the explosions keyword. The related explosion graphs can be operated on in the loop, e.g.:
do explosions 
{  '	'
   id 
   newline
}
makes a list of related explosion graphs. A foreach loop could also be used within the loop: it would then operate on the explosion graph, e.g. to iterate through its objects.

Iterating over decompositions

The link of an object into a decomposition graph can be followed through the decompositions keyword. The related decomposition graph can be operated on in the loop, e.g.:
do decompositions 
{  '	'
   id
   newline 
}
makes a list of related decomposition graphs. A foreach loop could also be used within the loop: it would then operate on the decomposition graph, e.g. to iterate through its objects.

Note that a do decomposition loop’s contents are executed at most once: an object may only have one decomposition. The loop structure is however used to maintain the similarity with explosions.

Iterating over graphically nested objects

Whilst object nesting is normally modeled as decomposition or explosion, in some cases it may be useful to visually draw one object as containing several others. Do and dowhile loops can be used to iterate over these pseudo-structures: see the contents and containers commands in Section 5.3.7.

Iterating over the stack

MetaEdit+ maintains the navigation history, i.e. the graph, objects, relationships etc. used to reach the current point of the generator. Each do, dowhile or foreach block pushes a new element to the navigation history stack, and the end of the block removes that element. Whilst the general output commands like id;1; allow us to pick elements from higher up the stack, sometimes it is useful to reflect over the whole stack. For this, we can use the do stack command, which iterates over the current element, the element one loop out, the element two loops out etc. For instance, we may want to output a little debugging information:
'/* debug trace: '
dowhile stack { id ', ' }
' */' newline
Sometimes we may want to find the closest element of a certain type on the stack. For instance, if we are using do to recurse along relationships in a graph, and want to output some information from the graph itself. Because of the recursion, we will not know how many levels back up the stack the graph itself is. We can use do stack to walk back up the stack until we meet the graph, filtering out other elements using where, and making sure we stop after the first graph using unique:
do stack 
where type = 'MyGraph'
unique type
{  :Graph Name; }
It is also possible to iterate over a truncated version of the stack, omitting elements from the start or the end. To omit elements from the start, i.e. to look at the stack as it was in an outer loop, add a positive level number suffix, e.g. do stack;1. This is useful when you want to find out whether the current element has already been encountered, e.g. to avoid infinite recursion:
do stack;1
where oid = oid;1
unique 'first'
{  $error = 'recursion!' }
To omit elements from the end, i.e. the outermost loops, add a negative level number suffix, e.g. do stack;-1.

Iterating over all graphs

Almost always, generators will be run with the context of a certain graph, and all information they need will be reachable by navigating from that graph. This is indeed a principle of good modeling language design. On some rare occasions, it may however be useful to be able to iterate over all graphs in the currently open projects, for instance when calculating metrics. The do graphs command will accomplish this. For instance, the following fragment will list the types and names of all graphs:
do graphs 
{  type ': ' id newline }

Subreport

Instead of defining a single large generator you can separate the generator into several subgenerators, which can call each other using the Subgenerator template:
subreport 'name' run
All output from commands between subgenerator and run is directed to a temporary stream, where it builds up the name of the generator to run. Normally, the only command would be a single string, e.g. ‘name’ above, but any generator commands can be used, e.g. the name of the current element’s type could be used to call different generators.

The subgenerator to run is looked up from the type of the current graph (in the closest containing loop), through the supertypes of that graph type to the Graph metatype. Thus graph types can override generators in their supertypes, by defining a generator with the same name. In particular, you can override the pre-defined generators from the Graph metatype by defining a generator of the same name in a graph type. For example you could add a new ‘Export graph to HTML’ generator to Class Diagram to output more detail for the individual Attributes and Operations of each class.

If the named generator is not found, a warning is shown when running from the Generator Editor; when run from elsewhere in MetaEdit+, i.e. generally by modelers, no warning is shown.

Up Previous Next Title Page Index Contents