5.3.1 Using the Generator Editor
In the following we shall describe the features and functions
of the Generator Editor.
Opening a Generator Editor
As there are several places in MetaEdit+ where generators are
used or referred to, there are also multiple ways to open the Generator Editor.
The simplest way is to open it from the main MetaEdit+ window:
1) | Select
a graph or graph type in the current browser.
|
2) | Select
Metamodel | Generator Editor, select Edit Generators from the
popup menu or press the Generator Editor toolbar button.
|
The Generator Editor is opened for
editing the generators in the selected graph type. For example in
Figure 5–10 the Generator Editor is
open for graphs of type ‘WatchApplication’. To work with generators
for other modeling languages select
Generator | Change Graph Type... in
the Generator Editor.
The Generator Editor can also be opened from an editor
such as a Diagram Editor by selecting
Graph | Edit Generators..., or from
Tools | Generator Editor in a Graph Tool.
To open
a new Generator Editor from the Generator Editor itself, select
Generator |
Open in New Window.
Generators are also used to define identifiers, text
symbol elements, symbol element conditions and subobjects and subsymbol names
for templates. The Generator Editor for these purposes can be opened from the
respective editing tool (an Object Tool or other metamodeling tool, or a Symbol
Editor’s Format dialog).
Creating a new generator
Generators are always defined specific to a given graph type,
because the generator uses the data structures of the graph type in its
definition. The following generator is appropriate only for WatchApplications,
thus for testing it you should have a Generator Editor opened for
WatchApplication.
To make a new generator select
Generator | New...
from the menu or toolbar. You will be asked for the name of the new generator.
Certain names and prefixes have been reserved for special purposes (see Section
6.2.1), but otherwise any name will
do.
To proceed with creating our example generator enter a
name like ‘Test’ and press OK. The contents of the editing
area are now:
Report 'Test'
endreport
The meaning of these lines is described in the
following subsections, but now we can continue the definition. The next step is
to select the operation and object type that we will be operating on. To proceed
with generator definition, put the cursor on the blank (second) line:
1) | Select
the ‘Control’ (or ‘Templates’)
category in the Concept Box (with the left mouse button).
|
2) | Double
click ‘foreach’ from the Choice box.
Or |
2) | Select
‘foreach’ from the Choice Box with the left mouse button and select
Insert from the Choice Box pop-up menu.
|
The contents of the editing area are
now:
Report 'Test'
foreach type
{
}
Endreport
If the inserted template has an argument, e.g.
a condition or type filter, that part will be selected for you in the generator
definition to ease the insertion of proper content. For instance, here the
keyword ‘type’ will be selected, as you will need to replace that
with an actual type.
| For
the following templates: /* */, if, foreach, do, dowhile, filename and variable
you can also make some existing text in the generator definition become the body
of an inserted template. Select the text and hold down the shift key while
inserting a command template. For example, to insert an ‘if’ command
around an existing part of the generator definition, select that part, hold
shift down and double-click the ‘if’ template. The ‘if’
template will be inserted around the existing part, which becomes the
‘then’ block, and the condition part of the ‘if’
template will be selected. |
After
operation selection we proceed by selecting an object
type
. Because we are interested in State
objects:
1) | Select
the text type in the Editing Area if it
is not already
selected. |
2) | Click
‘Object’ in the Concept
Box. |
3) | Double-click
‘State [Watch]’ in the Choice Box. Alternatively you can select
‘State [Watch] and choose Insert from its pop-up menu.
|
As a result of the selection the text
.State [Watch] is added into the
Editing Area, replacing the selected
type text. Notice that the concept was
automatically preceded with its type character, the dot (others being
: for Property, > for
Relationship, ~ for Role and
# for Port).
The next step is to add the desired operations for the
objects in the loop. The operations to be performed for each object of the
specified type (in this case State) are placed within the curly brackets. To
print out the name of each State:
1) | Place
the mouse cursor after the opening
bracket. |
2) | Select
‘Object’ from the Concept
Box. |
3) | Open
the sub-branch for State [Watch] in the
Choice
Box. |
4) | Double-click
State name from below
State [Watch] in the Choice Box tree
view. |
After this operation the generator
specification looks like this:
Report 'Test'
foreach .State [Watch]
{ :State name;
}
endreport
Notice that most statements in a generator
definition can be optionally terminated by a semicolon, ‘;’, but the
semicolon must be used after type names which contain spaces: otherwise the
parser could not tell whether “
:Customer
type” was a command to output the “Customer type”
property, or a command to output the “Customer” property and a
second command to output the
type of
the current element.
Executing generators
Now, if we think that this generator might work, we can test
it by running it by selecting Generator | Generate from the menu bar or
toolbar. This operation executes the generator specification against a graph of
the current type: if no graph has yet been picked, you will be asked to specify
which graph in a dialog. If you want to change to try the generator against a
different graph, select Generator | Generate for Graph.... We can run and
adjust the generator until we are satisfied.
We notice however, that we need to add some strings into
the output and that we should separate the output for each State onto a new
line. To make these modifications we extend the generator definition as
below:
Report 'Test'
foreach .State [Watch]
{ 'State : '
:State name;
newline
}
endreport
By executing the generator again, we can see
what the result looks like (
Figure
5–11). See Section 5.2.1. in the ‘MetaEdit+ User’s
Guide’ for more information about the Generator Output Tool.
Figure 5–11. An example of a generator output.
Debugging generators
The debugging of generators is explained in Section
5.4.
Editing generator definitions
To view and edit existing generator definitions select any of
the generators from the Generator Box. After the operation the generator
definition will be shown in the Editing Area. If your current generator has been
modified and not saved, you will be prompted whether to save it first.
| A
new generator only appears in the Generator Box when it has been saved.
|
You can continue making the
‘Test’ generator. For example, the next major improvement for the
‘Test’ generator could be adding for each State a list of other
States it is connected to. After this modification the new generator definition
should look like:
Report 'Test'
foreach .State [Watch]
{ 'State : '
:State name;
newline
'Connects to: '
newline
do ~From>()~To.State [Watch]
{ ' '
:State name;
newline
}
newline
}
endreport
The behavior of this generator is explained in
more detailed in Section
5. For now, we
will concentrate on the use of the Generator Editor. When we execute the
modified generator, the result looks like
Figure 5–12 below.
Figure 5–12. Generator output for ‘Test’ generator.
Building generators from the desired output
One approach to building a generator is to start with the
desired output as one large fixed string in the generator, and progressively
refactor it and replace fixed parts with values from the model. You can paste
the desired output as a fixed string with Edit | Paste Quoted, which will
paste the current clipboard contents surrounded by single quotes, with any
single quotes in the contents escaped by doubling them. E.g. if your desired
output is:
Generated from graph Foo by MetaEdit+'s MERL
You can
copy and paste that with Paste Quoted, resulting in the following (note
the doubled apostrophe):
'Generated from graph Foo by MetaEdit+''s MERL'
You
can then replace a varying part of the result with a property from the model by
selecting that part of the string and shift-double clicking the desired property
in the Choice Box. This will automatically close the string quotes, insert the
property output command, and re-open the string quotes. E.g. if you select Foo
and shift-double click Name, the result is:
'Generated from graph ' :Name ' by MetaEdit+''s MERL'
Font formatting in generators
Basic character formatting like underline, italic, bold or
font color and background color can be applied to commands in the generator
definition. It is also possible to attach a hyperlink to the selected text. Font
formatting options are applied to the selection from the Format menu or
toolbar buttons, or by pressing one of the shortcut keys (Help | Shortcut
keys, see also Section 3.3.4 in ‘MetaEdit+ user’s Guide’).
To remove all formatting and colors from the selection, choose Format |
Remove Character Formatting from the menu.
The formatting of the generated output is taken from the
first character of the clause that generated the output. This is then applied on
top of any formatting present in the output from that command. For example,
given an object whose id is Account,
the command id with italic
emphasis would be output as Account. If the property value were a Text in
bold, the output from id would be
Account, and from an italic id
the output would be Account.
Note that for literal string clauses, the formatting is
read not from the opening ' but from
the first character of the actual string. For property and chain output
commands, the formatting is taken from the type prefix
:.>~# of the first element, e.g. the
. and
> respectively in
.ObjType:PropName and
>RelType~RoleType:PropName.
Note that the syntax coloring of MERL (dark red for string
output commands like 'some
text' and dark green for explicit type names like
.Object
or property local names like
:Name)
is set automatically when the generator is parsed. The syntax coloring is also
shown in the output, to make it easier to see what is a model value and what is
fixed text. Syntax coloring is not however shown in output to a Text element in
a symbol, in identifier generators, or in the Live Check pane.
Output from a model element will be turned into Live
Code
links, shown with a light
blue underline. Double-clicking a Live Code link in the output will take you to
the original model element. This can be useful to the user of the generator
output, as well as for debugging and tracing while writing the generator. Note
that while generating, the Live Code link for a command is applied after any
translator suffixes, so e.g.
id%var
will have a link, even though translators cannot preserve formatting. Live Code
links are only active within MetaEdit+. They are not shown in output to a Text
element in a symbol, or in identifier generators.
Some operations do not preserve formatting, e.g. saving
either generators or output to a text file, exporting generators in XML files,
or using translators on generator output. An empty translator can thus be used
to remove formatting where it is not wanted, e.g.
to translate
:Name;
endto.
Changing the editing area font and word wrap
You can change the font and word wrap settings of the
generator editing area from the
View menu. Note that these changes only
affect this editor, and not the saved generator or resulting
output.
Deleting generators
To delete the currently selected generator choose
Generator
| Delete... from the menu (or from the toolbar or with the keyboard shortcut
Ctrl+D). A dialog prompts for the confirmation of the deletion operation.
If no generator is selected, the dialog will allow the selection of multiple
generators to delete.
Renaming generators
A generator can be renamed by selecting Generator |
Rename... or by pressing F2. This brings up a dialog for entering a
new name. As generators are called dynamically based on the current graph type
and a possibly dynamic name, calls to the generator cannot be automatically
updated to reflect the new name. You can use Edit | Advanced Find to find
occurrences of XXX() or
subreport 'XXX' run, e.g. with the
following regular expression, replacing XXX with the old name of your
generator:
XXX\(|subreport;?\s+'XXX';?\s+run
You can also
rename the generator by changing its name at the beginning of the generator
definition. When the definition is saved, you will be asked whether you want to
create a new generator with the new name, or rename the existing generator to
the new name.
Saving generator definitions
Generator definitions can be saved with the menu selection
Generator | Save (or from the toolbar or with the keyboard shortcut
Ctrl+S). If the current definition has been modified but not saved and
the Generator Editor is closed or another definition is chosen from the
Generator Box the tool will ask whether the generator definition should be
saved.
Writing generator definitions to files
Generator | Write to file... (or the corresponding
toolbar command) lets you write the current contents of the editing area to a
text file. Note that only the text is saved, not any formatting applied with
shortcut keys. It is also possible to save all currently listed generator
definitions by selecting
Write to file... while having no selection in
the list. Where possible, the file timestamp is set to match the generator
modification timestamp.
Reading generators from files
Generator | Read from file... (or the corresponding
toolbar command) lets you read an existing generator definition from a text
file. When reading from a file, you are normally warned if the generator would
have the same name as an existing one. Also, the generator is not actually
saved, but is left in the text area, ready to be saved explicitly or via a
prompt when you select another generator definition or close the Generator
Editor. You can also select multiple generator definitions to be read at the
same time. In this case, each generator is saved automatically without further
interaction with the user. The generator timestamps are set to match the file
timestamps.
Finding and replacing text within generator definitions
Edit | Find... (or Ctrl+F or the corresponding
toolbar command) lets you find text in the current generator definition. You can
press the F3 key to find the next occurrence in the generator, or
Shift+F3 to find the previous occurrence. If you want to replace text,
use Edit | Replace... which allows replacing text in forward and backward
direction in the current generator definition.
Edit | Advanced Find... (or
Ctrl+Shift+F or
Shift+Find on the toolbar) lets you find text in many generator
definitions and use regular expression
searches. The
current generator must be saved before using this. In the Advanced Find dialog
you can click the button to the right of the Find Text box to see the list of
the available regular expression patterns. When you select a regular expression
pattern from the list, the expression is entered to the current find text and
the ‘Regular Expression’ checkbox is checked. The search results are
shown in the Generator Box titled: ‘Last Find:’ followed by the
search string used. The results can be browsed by using the
F3 and
Shift+F3 which shows the next/previous occurrence of the Find Text in the
selected generator, or if none is found, the next/previous occurrence in the
next generator will be shown.
The scope constrains the number of generators that are
matched against the search criteria:
| All
Graph Types: find generators in all open
projects |
| This
Project: find generators in graphs that belong the the same graph type as the
selected
generator. |
| This
Graph Type: find generators in the currently selected generator's graph type, or
if nothing is selected, find in the Generator Editor’s current graph
type. |
| This
Graph Type and Supertypes: same as ‘This Graph Type’ but also finds
generators in the supertypes of the current Graph
type. |
| Current
Generator: find in the currently selected generator's last saved
text. |
Checking syntax
The syntax of a generator definition is automatically checked
and possible errors reported when the definition is saved. To check the syntax
while writing the definition, select
Edit | Check
Syntax.
Adding and removing breakpoints in generator
definitions
Breakpoints provide a means for interrupting the execution of
a generator in user-definable position for debugging purposes. To add a
breakpoint in a generator definition:
1) | Move
the cursor to the desired breakpoint position in the editing
area. |
2) | Select
Breakpoint | Add from the menu or from the toolbar. A red circle appears
in the selected location to denote the new
breakpoint. |
To remove
breakpoints:
1) | Select
the breakpoints that you want to remove in the editing
area. |
2) | Select
Breakpoint | Remove from the menu or from the
toolbar. |
To remove all breakpoints from
the definition, select
Breakpoint | Remove All from the menu. To remove
all breakpoints from all generators in all loaded Graph types, select
Breakpoint | Remove All Globally from the
menu.
Enabling and disabling breakpoints
To customize the behavior of all breakpoints, select one of
the following options from the Breakpoint menu:
| Enabled:
Breakpoints are enabled in most cases when generator is executed (i.e. from
Generator Editor, Editor, Graph or Type Browser, etc.) but not when executed as
part of a symbol element’s display condition, in a symbol element text
field, or in an identifier
generator. |
| Enabled
While Editing: Breakpoints are active only when the generator is executed
from the Generator
Editor. |
| Enabled
While Debugging: Breakpoints are active only when the generator is executed
from the Generator Editor by using the Debug...
command. |
| Disabled:
Breakpoints are always
ignored. |
Setting breakpoint condition
A breakpoint can also have a condition that defines whether or
not the breakpoint will be activated when it is encountered. Conditions are
defined as generator snippets that are evaluated in the current context and the
output compared to the supplied string or regular expression. To set the
condition, select the breakpoint and then
Breakpoint | Edit Condition to
open the Format Breakpoint dialog (shown in
Figure 5–13). If no breakpoint is
selected, a new one with an empty condition is first added at the start of the
selection.
Figure 5–13. Setting breakpoint condition.
The
condition term is defined in the
Condition source compartment of the
dialog. Select
None for the normal breakpoint behavior (i.e. always
interrupt the execution at the breakpoint) or define the
Generator that
provides the condition term. The
Condition part of the dialog then
defines how to compare the term in order to meet the interrupt condition. Select
String and an appropriate comparison operation for plain string
comparison or
RegExp and either
= or
<> for regular
expression comparison.
Tracing call context
To see where a generator is called by its parent in the
hierarchy, select the generator and choose
Generator | Show Call Context.
This will select the parent generator in the hierarchy and highlight the
subgenerator call.
Comparing generator versions
To track the current changes made to the generator definition
after the last saving, select
Generator | Compare with Saved. This will
open a Changes window that shows the saved versions and current version with
changes highlighted.
Generator icon
A generator can have an icon that will be shown if the
generator is included in the Diagram Editor’s toolbar (i.e. it has a
‘!’ prefix in its name). To open the Icon Editor for the generator
select
Generator | Icon
Editor.