Writing an Example Wizard

In the following sections, we will create a simple wizard as an example, which will support either creating a simulation (complete with NED and INI files), or just a single NED file with a network defined in it. The user will be able to specify the type and the number of the submodules the network contains.

Configuring the Wizard

The first step when creating a wizard is to create a new folder under the templates directory of the project. A file named template.properties must be present in the newly created directory. This file is used to configure the wizard. Create a folder a folder named templates/examplewizard under the project, then create template.properties and add the following lines to it:

templateName = New Test Wizard
templateDescription = Generate an example
templateCategory = Test Wizards
supportedWizardTypes = simulation, network

These lines specify the name, type and category for the wizard. Category is used to specify how the wizards will be grouped in the tree control section of the template selection page. Wizard type specifies in which New … Wizard your wizard will appear. You can specify one or more of project, simulation, network, etc.; the list of all possible wizard types will be covered later in this document. In this case, the wizard will appear in the New Simulation and New Network wizard dialogs.

You can now decide what data you would like to ask the user to provide. Template variables and their values can be defined as key-value pairs:

nodeType = Dummy
networkSize = 6

The nodeType variable will be used as the submodule type in your network, while the networkSize defines how many submodules you want in the network.

We define a custom wizard page where the user can specify the values of the above variables (i.e. override their default value, specified above).

page.1.file = parameters.xswt
page.1.title = Network parameters

We will use the file parameters.xswt to define the layout and the content of the new wizard page.

Note

There are numerous other configuration keys that can be used in template.properties. See the "Configuration Keys" section for an exhaustive list of options.

Creating a New Wizard Page

Files with .xswt extension (Wizard Page definitions) are used to define the UI and add new wizard pages to gather user input for template generation. In the previous section, we specified that the file called parameters.xswt will contain the new wizard page definition. We will add a spinner control to specify the size of our network and a text control to specify the node type. Create a new file called parameters.xswt with the following content:

<xswt xmlns:x="http://sweet_swt.sf.net/xswt">
  <x:import>
    <!-- Import all widgets and utility classes  -->
    <package name="java.lang"/>
    <package name="org.eclipse.swt.widgets" />
    <package name="org.eclipse.swt.graphics" />
    <package name="org.eclipse.swt.layout" />
    <package name="org.eclipse.swt.custom" />
    <package name="org.omnetpp.common.wizard.support" />
    <package name="org.omnetpp.ned.editor.wizards.support" />
    <package name="org.omnetpp.cdt.wizard.support" />
  </x:import>
  <!-- Create a two-column layout  -->
  <layout x:class="GridLayout" numColumns="2"/>
  <x:children>
    <!-- First row -->
    <label text="Number of nodes in the network:"/>
    <spinner x:id="networkSize" minimum="2" x:style="BORDER"/>
    <!-- Second row -->
    <label text="Type of nodes:"/>
    <text x:id="nodeType" x:style="BORDER"/>
  </x:children>
</xswt>

The above defined wizard page will have two columns. The first column contains labels and the second contains editable widgets. The x:id="varName" attributes in the spinner and text control definitions are used to bind a template variable to the control. When a page is displayed, the content of the bound variables are copied into the controls. When the user navigates away from the page or presses the Finish button, the content of the controls are copied back to the bound variables. These variables can be used in the template files we are about to define in the following section.

Note

To see the list of all available widgets, check the Appendix.

Creating Templated Files

When the template is used, the contents of the template folder (and its subfolders) will be copied over into the new project, thus preserving the directory structure with the exception of template.properties (it is also possible to specify other files and folders to be ignored by specifying a file list for the ignoreResources configuration key).

When the wizard is being used, a pool of variables is kept by the wizard dialog. These variables are initialized from the key = value lines in the template.properties files; they can get displayed and/or edited on custom wizard pages. Eventually, they get substituted into *.ftl files (using the ${varname} syntax).

Some variables have special meaning and are interpreted by the wizard dialog (e.g. the nedSrcFolders variable determines which folders get denoted as NED Source Folders by the New OMNeT++ Project wizard). Variables can be used to generate output file names, can be used as input file names, and can serve as input and working variables for arbitrarily complex algorithms programmed in the template (*.ftl) files.

Let us have a quick look at the FreeMarker template language. Variables can be inserted into the output using the ${varname} syntax. One can also write expressions inside ${..}. For example, ${numServers?number + 1} uses the number built-in operator to convert the numServers variable to a number, adds one to it, and inserts the result into the template output. FreeMarker has many other functions (conversion to string, upper-case version of a string, size of an array, etc.) that work as built-in operators.

Programming facilities can be accessed via directives that have the <#...> syntax. Use <#if> (<#if> cond>..<#elseif cond>..<#else>..</#if>) for conditionals; <#list> for iterations and loops (e.g. <#list words as w>..</#list>; <#list 0..9 as i>..</#list>; <#list 9..0 as i>..</#list>); and <#assign> for assigning variables (<#assign x=5>; <#assign x>the number ${i}</#assign>). One can define functions (<#function>) and macros (<#macros>). You can also include other templates (<#include>). Here, we only covered a small fraction of FreeMarker’s possibilities; we recommend that you read the FreeMarker manual for more information.

Let us create a file with a filename that has an .ftl extension (e.g. untitled.ned.ftl). Because of the extension, this file will be processed by the templating engine. The actual name of the file does not matter, because the <@setoutput .../> directive instructs the templating engine to output everything from the current file into the file that is specified by the targetFileName variable. The targetFileName, targetTypeName, bannerComment and nedPackageName variables are automatically filled out by the wizard, based on the filename and folder the user selected on the first wizard page.

<@setoutput path=targetFileName />
${bannerComment}
<#if nedPackageName!="">package ${nedPackageName};</#if>
network ${targetTypeName}
{
    node[${networkSize}] : ${nodeType}
}

The template variables will be substituted into the template automatically.

Specific wizard dialog types will also define extra variables for use in the templates. For example, the wizard type that creates a complete simulation (with all required files) will put the simulationName variable into the context. To see all defined variables, check the Appendix.

Tip

The New Wizard wizard in the IDE provides you with some working examples, useful utilities for writing wizards, and sample code for accessing various features along with other helpful information. The aim of these wizards is to help you become productive in the shortest time possible.

As a last step in our example, we will also create an INI file template:

Create a file called omnetpp.ini.ftl, and fill with:

<#if wizardType=="simulation">
network = ${targetTypeName}
</#if>

We need the INI file only if we are creating a simulation. If the current type is not simulation, the content will be empty, and the file will not be written to the disk.