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 ', ' }
' */' newlineSometimes 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.