Source Generation

The UI Designer supports generating C/C++ source code through a contribution-driven model. A component provides a <sourceGen> element which uses a series of XML elements to define locations, templates, and inline JavaScript code. These elements contribute, eventually, to an ordered list of "contributions" to source. Each component passes its contributions up to its parent, which collates them, and recursively passes them to its parent. The end result is a full set of contributions defining all the generated source.

A contribution is an association of text and a high-level "location" in the project (for example, the "enum TValues" enumeration inside "class CMyClass" inside the file "inc/MyClass.h"). The text is generated either by template expansion or by explicit JavaScript code, or both. A location is defined relative to other locations and may represent either "unowned" code (which is generated once, when it is missed) or "owned" code (which is always regenerated).

A contribution may have a "phase" instead of a location. A "phase" is a moniker for a location that will be resolved later, usually by the parent of a component instance. This may be used to collate contributons within a location, or for filtering, or other purposes. Using phases allows a component to target different parents which may have different location hierarchies. In the final list of contributions returned to the source generator, all phases must be resolved to locations.

The concept of “phases” is intended to alleviate the need for children to know where they are generating code. Thus, a <template> for a child needn’t specify a location if it specifies a phase; the container will supply the location. This implies that the container explicitly “fixes up” child contributions by finding those with a matching phase and setting their locations. Contributions cannot supply their own locations if contributing to a phase.

If a child wants to contribute code to other locations, it either defines its own location, derives from a known one, or contributes to a new phase that the appropriate parent(s) know how to handle.

Usually, phases are used to reduce coupling between components that exist in hierarchies where the specific location ids or specific relations of locations to each other should not be hardcoded. For instance, a parent that generates a class may define several phases for children to use to contribute instance members, initialization statements, and #includes. The parent must resolve contribution phases to actual location ids before returning its contributions to its parent (using Engine.assignLocationsForPhase(List, String, String)).

The internal format of <sourceGen> is a single JavaScript file, which is maintained in memory. The <sourceGen debug="true"> attribute or the Components > Configure SourceGen Debugging... dialog may be used to write this content to disk, for debugging.

The <sourceGen> element may contain the <defineLocation>, <template>, <templateGroup>, <inline>, <useTemplate>, and <useTemplateGroup> elements in any order or number. The strict order of <template>, <useTemplate>, <inline> elements etc. will determine the ordering of contributions in files. Thus, the position of the first template that references the “CLASS” location determines where in “HEADER_FILE” the class will appear.

The <sourceGen> element’s children are interpreted as a linear list of discrete operations, all of which are executed for each instance of an object in the data model. The ordering of these nodes directly corresponds to the order in which text will initially be generated.

Contributions

Each component definition file defines data and code which create contributions. A contribution is a piece of formatted text which is placed into source at a location associated with a domain. Each contribution has a domain and a location describing where to place the text. The complete source generation comes from an algorithm which places the pieces of text in the right locations. A contribution is not complete and cannot be processed by the domain until it has a location. It may exist in a transient state where only a phase is set. At some point (usually in a parent component) the phase is converted to a location. If a location referenced by contributions is missing, then it is created by applying the templates that define the location.

Source contributions are gathered from the bottom and go up. Parents collate child contributions and the top level (data model) sorts contributions and generates source. The UI Designer backend engine interprets contributions and is familiar with specific languages. Source generation is expanded into JavaScript, defining a single JavaScript object that implements the IComponentSourceGen interface that returns a list of contributions to the parent.

A contribution consists of:

Locations

Locations are strings that identify a domain-specific location (currently only cpp) where generated source is placed. In the C/C++ domain, locations are paths that are specified structurally, with reference to easy navigation with the CDT DOM. To reference a location use a statement such as <template location=“location-id” ... />. The location must be in the same directory and file it was originally created in for the domain to find it.

Locations are organized like file paths separated with ‘/’, with more encompassing items on the left, and children of those items on the right.

Each path entry consists of a specifier and arguments:

For example:

class(MyClass)/region(InstanceVariables) — A special block named “InstanceVariables” inside the class declaration “MyClass”

function(MyClass::initComponents()) — The function MyClass::initComponents() {} in a file. Not class(MyClass) then function(initComponents()) unless actually declaring the function inline.

The identifier used in locations is lexically scoped. It can be resolved either in the current instance’s component or in a parent component that directly or indirectly invoked the contribution phase for this component. The location is not global except in the case of a “library” component. A top-level component can serve as such a library for this case. For example, a common pattern for locations which are intended for use by children could be to name them FILE, CLASS, or METHOD. Anything else can have a more descriptive name to distinguish it from other locations defined in parents.

Every <template> element must have a phase or a location, but not both. For a phase, the container supplies the location for all matching phases. For a location, the template supplies the location by reference to <defineLocation> (either global or local).

A location can be defined by a template that builds a location by drilling into a location passed from the parent, or one can be generated from scratch. A location describes a virtual path to actual source. A location is based on another location, such as a class inside a file, or on a file. A root location has no base and is defined by a relative directory and a filename within the current project. A derived location is defined as being contained within the bounds of its base location.

For example, a base location may be the file foo.cpp in the directory "src". A derived location may be the CFoo::CFoo() function defined within that source file. Another location derived from this may be a local enumeration (TChoices) defined within that function. A derived location cannot provide two segments, such as jump directly from a file to an enum nested within a class. There must be one location defined per segment.

A location can be created “inside” another, as long as that one is also defined. Defined locations have globally unique identifiers which are attached to contributions to place that contribution at the given location.

When a parent component asks a child to generate contributions, the child can reference all its parent’s locations. Although, phases can be used instead to reduce coupling. A child may redefine a location that comes from a parent, but this change only has a local effect on the child’s source generation.

A location is realized in source code when

The location and any parent locations are created as needed. If they already exist in source, they are not regenerated. However, if they are owned by the UI Designer, they will be cleared out. Note that root locations will create missing directories and files.

Locations may have filters that control whether a contribution is emitted to it. This is primarily used to check for unique code, for example, to avoid duplicate #includes, member declarations, and base class entries. Locations may be conditional if an event binding exists. The location would then only be declared if one of a given set of events is bound to an instance.

Locations can be owned by the UI Designer or not owned. If owned, a “do not modify” comment is added automatically to the generated source. The location’s contents are cleaned out each time the source is generated. For example, source is regenerated for:

If not owned, no comment is added. The location and its contributions are applied only if the location was created in this source generation phase. For example:

DefineLocation Element

Every location is created in a specific directory and filename. To define a location, create a <defineLocation> element in the <sourceGen> element of a component definition file. All <defineLocation> entries are inherited automatically by derived components.

The following is an example syntax of defining locations.

<defineLocation [baseLocation=“id” | domain=“...” dir=“...” file=“...” ] id=“...” location=“...” owned=“[true|false]” [ realize="true|false"]>
  <template .... /> / <script ... /> / <inline ... />
</defineLocation>

The dir and file attributes specify the path and filename where the location is initially created; after that, the CDT DOM is used to find the location. The dir attribute tells what project-relative directory to use. The template variable may be used to look up a user-configured directory. For example, ${src}, ${inc}, ${resource}, ${build}, or ${aif}. A path may be applied to enter the directory.

Examples:

The filename is specified with the file attribute.

Examples:

The location attribute specifies the domain-specific location details. The owned attribute defines whether the section of code is owned (rewritten) or not owned by the UI Designer. The <template>, <script>and <inline> nodes provide the code that creates the contents for a referenced location if it is missing.

The dir, file, and location attributes use a common pattern for handling variable and template-expanded arguments. Arguments are specifed with this syntax: ${variable}.

Standard variables include:

Variables in locations refer to the current component, however locations can be used in child components or other components. A location is expanded with respect to the component that generates a contribution using that location. A child component which sets “phase” does not set location.

Therefore, the location is expanded relative to the parent which supplied the location to the child. The child can still derive a location from specific location ids in the parent. In such a case, the parts that the child adds are expanded relative to the child.

For example, the parent sets dir=“${src}”, file=“C${viewName}.h”, and location=“class(C${baseComponentId})”, and passes this to the children for the addInstanceVariables phase. The child gets the fully specified dir/file/location attributes that reference the parent component, but it can derive a location with a custom use of ${baseComponentId}, which will be expanded to its own component base.

The <defineLocation> element declares and defines locations which may be used by contributions. For example:

<defineLocation  id="HEADER_FILE" domain="cpp" dir="${inc}"
  file="${instanceName}.h"
  owned="false"
  location="">
</defineLocation>

This example describes a root location which is a header file. The domain, dir, and file attributes are required for root locations, and the location attribute must be empty. The dir and file attributes are template-expanded by referencing the instance global variables. For instance, “${inc}” defines the directory used for includes, and “${instanceName}” is the name of the current instance, so the file will be named after the instance.

Following is an example of a derived location where the location is understood as a class definition in the header file.

<defineLocation id="CLASS"  baseLocation="HEADER_FILE"
  owned="false"
  location="class(${className})">
  <template>
 …
</defineLocation>

Common to any <defineLocation> element are the following attributes:

NOTE The user might delete a generated owned location. When it is regenerated, it will not go in the original location again, but to the end of the parent location. This is a known limitation.

A <defineLocation> element may include a number of <template> or <inline> elements. When a location is realized, the contained templates and inline blocks are executed to define it. The templates are responsible for contributing the text that will correctly define the location, optionally populating it with initial content.

In the example above, the header file location has no templates, since an empty file is a valid file. Although, to define a class, the definition must be provided:

<defineLocation id="CLASS"  baseLocation="HEADER_FILE"
   owned="false" 
   location="class(${className})"> 
   <template>
<![CDATA[ /**  * Container class for ${instanceName}  *  * @class ${className} ${instanceName}.h  */ class ${className} : public CCoeControl       { public:       // constructors and destructor       ${className}();       virtual ~${className}();       }; ]]>                     </template> </defineLocation>

Note that the variable expansion ${className} is used inside the location attribute. These same variables are available in the text and substituted using the same syntax, although the semantics are different. See <template> element for more details.

Cpp location segments

A location segment in the cpp domain is a string representing a node in a C/C++ parse tree. Each takes the syntax “<name> ‘(‘ <arguments…>‘)’”. Certain nodes may only appear within certain others. This list defines top-level nodes:

Inside a class(), namespace() is not allowed, and this additional segment is allowed.

In a function(), only class(), region(), enum(), and to-file() are allowed.

In an enum(), bases() or region(), only region() and to-file() are allowed.

Owned/Non-owned/Filtered Locations

The term "owned" refers to a section of code which is always rewritten by the UI Designer. Such sections are either associated with specific C/C++ syntactic elements, such as classes, functions, and enums. These sections may also appear as a region within such an element or at the file level. Any user edits within the comments are lost.  These comments look like:

// [[[ begin generated region: do not modify! [Generated User Includes]
     #include "MyForm.h"
     #include "My.rsg"
// ]]] end generated region [Generated User Includes]

The term "non-owned" refers to anything outside the “owned” comments.  In this example, the function is non-owned, since there is no comment around it. The user is allowed to change anything before or after the owned region inside the function.

/**
* Construct  the MyForm instance
* @param  aCommandObserver command observer
*/
MyForm::MyForm( MEikCommandObserver* aCommandObserver )
  {
    iCommandObserver  = aCommandObserver;
    // [[[ begin generated region: do not modify! [Generated Contents]
    // ]]] end generated region [Generated Contents]
  }

"Filtered" refers to non-owned locations which have the "filter" attribute specified. Even if the location already exists, every contribution will be considered and added if it does not pass the filter.

Elements

Template element

A <template> element defines text for contributions. Ordering of <template> contributions is retained for the parent. The <template> element attributes include:

A <templateGroup> element combines several <template> elements into a named group. Grouping is strictly a convenience and has no impact on generated script. Attributes of <templateGroup> are automatically applied to the contained <template> elements. The attribute “id” may be specified to name the group. Included templates have a unique “id” namespace.

Example:

<sourceGen>
 <templateGroup form="CAknView"> <!-- group of templates inhheriting form -->
  <template phase=“initcomponents” id=“foo”>
   ${instanceName}->SetName(${instanceName.toString()});
  </template>
  <template phase=“parenting”>
   ${parent.instanceName}->Add(${instanceName});
  </template>
 </templateGroup>
 <template location="INCLUDES"> <!-- standalone template -->
  #include <akndialog.h>  <!-- XML  needs to be escaped unless in <![CDATA[...]]> -->
 </template>
</sourceGen>

The lines in text nodes of templates should be flush left on the line, unless multiple lines are defined, where some of the lines have more indentation. If extra indentation is added, use the tab character only. The source generator will convert tabs to spaces according to the workspace settings. The nesting of a particular location defines the indentation level. The indentation for a contribution may be adjusted with a script escape at the start of the node, for example:

<template location=”CLASS”><![CDATA[
  <% contrib.indentAdjust(-1); %>protected: ]]>
</template>

The object “contrib” refers to the IContribution instance currently being generated, which is automatically available for any <template>. The argument specifies the number of levels to adjust. This example outdents the “protected:” line in a class declaration.

Except for the very beginning and very end, almost every character is used in the template’s text. After the end of the <template> open-element, any spaces and newlines are removed if present before the </template> close-element. Any whitespace on any line before the </template> close-element is also removed. Such line stripping does not occur when escapes are used. Thus, script escapes often need to start at the very end of the previous line or end at the very start of the next line in order to avoid spurious newlines in the generated text.

Expression Escapes

A <template> element is converted into a small JavaScript function that at runtime generates segments of text into source, in a manner similar to JSP. Initially, the text in a template is converted into code that inserts literal text into source. Placeholders in the form ${expression} may appear in literal text. These resolve to JavaScript expressions whose string representations are inserted into source. If expressions involve complex expressions, enclose them in parentheses.

Most attributes in <sourceGen> XML also allows for syntax that resembles expression escapes. Such attributes, however, are not evaluated in JavaScript. Such expansions may only reference variable names, not expressions. A set of predefined variables is a subset of that available to script, with some extensions to account for common operations:

${src}: the source directory used when creating the project
${inc}: the source directory used when creating the project
${build}: the build directory used when creating the project (e.g. "group")
${resource}: the resource directory used when creating the project (e.g. "data")

${projectName}: the name of the generated project
${instanceName}: the name property of the current instance
${instanceName$title}: the titlecased version of the instance name, with the first letter capitalized (e.g. "my_var" becomes "My_var")
${instanceName$upper}: the uppercase version of the instance name
${instanceName$lower}: the lowercase version of the instance name
${instanceMemberName}: the expected name for an instance variable for this component instance: 'i' + the titlecased version of the instance name
${className}: the value of the 'className' property in the nearest enclosing parent (or the current instance) that defines it. May be null.
${parentClassName}: like ${className}, but the className property of the next enclosing instance. Note that this is not related to nesting of classes.
${handlerClassName}: this is the value of the 'className' property for the nearest enclosing parent (or the current instance) which defines the attribute 'event-handler-target' to "true". May be null.
${handlerInstanceName}: this is the value of the 'name' property for the nearest enclosing parent (or the current instance) which defines the attribute 'event-handler-target' to "true". May be null.

These variables are available if the component generates resources (see RSS Source Mapping):

${resourceName}: the name of the primary generated resource for the instance. May be null.
${resourceName$upper}: the uppercase name of the primary generated resource, as used for the macro in the .rsg file. May be null.
${resourceFileNameBase}: the filename, without extension, used for the resources this instance generated. May be null.

Script Escapes

Script escaped in <%...%> blocks specifies JavaScript code which is inserted directly into the generated function to control the source generation process. This escape usually either surrounds raw text in a condition, adds looping, or creates contributions programmatically. The entire sequence of text, including escapes, is interpreted in order, and expanded into a sequence of script. Raw text should be understood as script that emits the given raw text.

For example, a loop that iterates properties may be specified like this:

<template>
 <% for (var property in properties) { %>
  ${instanceName}->Set${titleCase(property.name)}
   (${property.value});
  <% } %>
</template>

This example shows expressions. Here, the instanceName is expanded and emitted in line with the raw text around it.

<template location="HEADER_FILE"><![CDATA[
   #include "${instanceName}.h"
 ]]>
</template>

A more complicated example shows how script interacts with text.

<template  location="HEADER_FILE"><![CDATA[
  <% if (properties.contextMenu != "") { %>
  #include <eikmenub.h>
  <% } %>
]]> </template>

Here, the “<%” escape allows entry of script which tests whether a property is non-empty. The JavaScript open block “{“ precedes the close script escape “%>”.  Then, raw text is emitted inside the if, and then “<% } %>” closes the JavaScript if statement.

NOTE The braces are required when wrapping text inside "if" like this. The content outside the script escape is not guaranteed to be a single statement.

Inline Element

The <inline> element may specify a segment of raw JavaScript.

An <inline> element allows JavaScript to be embedded within a component definition file. Zero or more <inline> elements can be used to define script snippets. An <inline> element provides functions and properties used by templates and ordering with regard to <template> elements is preserved. The <inline> element defines code which is added to the current function, for example:

<inline>contribs.clear();</inline>

The scope=”…” attribute tells whether the script is placed in the current function (“function”) or at the file level (“file”). The scope=“file” attribute redirects content to the file, outside the current function (read: named template). For direct access to the prototype name, use “${jsObject}”. For example, to define an inheritable method in a current JavaScript object, use:

<inline scope=“prototype”>${jsObject}.prototype.method = function(...) {...}</inline>

Scripting Example:

<sourceGen>
  <template  domain=“cpp” phase=“initcomponents” id=“foo”>
    <!-- references function added to prototype  below -->
    ${id}->SetName(${specialFormatter(id.toString())});
  </template>
  <inline>
    <!-- adds  method to prototype, which will be inherited -->
    ${jsObject}.prototype.specialFormatter =  function(property) { return ... }
  </inline>
  <inline> <!-- standalone function accessible only from this component -->
    function checkFeatureX() { return  properties[“hasFeatureX”] == “true”; }
  </inline>
  <template  domain=“cpp” location=“#includes”>
    #include &lt;BaseHeader.h&gt;
    <!--  templates can escape to script too -->
    <% if (checkFeatureX()) { %>
    #include &lt;SpecialHeader.h&gt;
    <% } %>
  </template>
</sourceGen>
CDATA Tag

As with any XML, text nodes must be escaped if it contains XML metacharacters like ‘<’, ‘>’, or ‘&’. Since C/C++ source code uses these a lot, it may be easiest and most readable to surround text blocks in “<!CDATA[“ … “]]>”. Everything inside a CDATA section is ignored by the XML parser. If your text contains a lot of "<" or "&" characters, which is common in program code, the XML element can be defined as a CDATA section. A CDATA section starts with "<![CDATA[" and ends with "]]>". For example:

<script>
<![CDATA[
 function match(a,b) {
 if (a < b && a < 0) then
   {
    return 1
   }
 else
{ return 0 } } ]]> </script>

A CDATA section cannot contain the string "]]>", therefore, nested CDATA sections are not allowed. Also make sure there are no spaces or line breaks inside the "]]>" string.

Forms

A form is used to filter contributions, for the case where a component may have completely different styles of source generation based on its parent. A parent passes a regular expression for permissible forms down to its children when asking for their contributions. Only contributions matching the form's regular expression are generated.

Therefore, a template’s form attribute (if specified) filters the contribution of the parent container, for example a CEikEdwin is defined differently if hosted in a CCoeComponent versus in a CEikDialog. A form is passed down when a parent generates child contributions. The form may be null if the parent dictates no special child source generation pattern.

Every template element’s form attribute (either explicit or inherited from templateGroup) determines which parents will see it. If no form is defined, then contribution is always used, no matter what form the parent uses. If form is defined, then use it only if parent’s form matches. If form is defined as “ALL”, then always use, regardless of the parent’s form. If an element defines a form, different elements may share “id” (only one instance will be used). This aids inheritance, but the ids of elements sharing a form must not intersect.

Form Example:

<sourceGen>
 <templateGroup form=“CEikDialog”>
  <template phase=“initcomponents” id=“foo”>
   ${id} =static_cast&lt;CEikEdwin*&gt; (ControlOrNull(${property[“dialogId”]}));
  </template>
  <template phase=“parenting”> ... </template>
 </templateGroup>
 <templateGroup form=“default”>
  <template phase=“initcomponents” id=“foo”>  <!--  note: “id” can be shared in different forms -->
   ${id} = new (ELeave) CEikEdwin;
   ...
   iCoeEnv-&gt;CreateResourceReaderLC (reader, ${property[“rssName”]});
  </template>
  <template phase=“parenting”> ...  </template>
 </templateGroup>
</sourceGen>

Macros

Source generation elements may be packaged in macros and expanded later, allowing for the often complex collection of <defineLocation>, <template> elements to be stored in a pattern once and used multiple times.

Defining Macros

A macro consists of arguments and common sourcegen elements. A macro may be built from other macros by importing their arguments and expanding them as part of its definition. Macro arguments are untyped strings, which are referenced by name. An argument may be marked as optional, required, having a default value, or having no value at all if unspecified.

Arguments may be referenced anywhere in the XML of the sourcegen elements that define the macro (attributes or text). They take the form “$(<macro name>)”. The use of parentheses instead of curly braces allows templates to use expression escapes without escaping them. Arguments may take modifiers when they are expanded, to perform common functions like testing them for being defined, converting them to strings, converting case, etc.

Macro Expansion

Macros may be expanded almost anywhere inside the sourcegen XML. Arguments may be passed as XML attributes named after the argument names, for convenience, or multi-line arguments may be passed as explicit XML sub-elements. The former syntax allows great brevity when using macros. When defining macros built upon other macros, then a macro expansion implicitly passes any arguments it was passed that match the argument list of the one it is calling. There are ways to suppress this behavior, as well. Again, these provisions make for brevity in defining macros as well.

XML Syntax

Defining macros example:

<defineMacro id="GenerateAssert" help="Generate an assertion  call" >
  <macroArgument name="LocationId" help="The location id into which  to place the call" />
  <macroArgument name="Expr" help="The expression to test" />
  <macroArgument name="Label" optional="true" default="Assertion  failed!" help="The label to emit with an  assertion error" />
  <template location="$(LocationId)"><![CDATA[
    if (!($(Expr)))
     { 
        printf("%s: %s\\n",  $(Label::as-string), $(Expr::as-string));
     }
   >]]>
</template> </defineMacro>

The “id” defines the name of the macro as used in <expandMacro>. The “LocationId” and “Expr” arguments are simple strings which must be provided by the client. The “Label” argument is optional and has a default value.

The <template> contained in the macro expands the “LocationId” argument in an XML attribute and inside the template text itself. “Expr” is substituted into the template body twice: once, without translation, into the “if”, and again, into the printf() call, after being converted to a literal string (i.e., adding quotes and escaping embedded quotes). “Label” is substituted similarly.

Detailed syntax

The <defineMacro> element defines a macro consisting of a set of templates and inlines which may be variable-substituted.

The <defineMacro> element consists of zero or more <importArguments> elements followed by any number of <macroArgument> elements, and finally followed by any number of <template>, <inline>, <defineLocation>, and <expandMacro> elements.
                       
<defineMacro> takes these attributes:

<importArguments> brings in argument declarations from another macro, including their default values and “required” flag.

<importArguments> takes these attributes:

The <macroArgument> element defines an argument for use with the macro. The default value may be specified in the 'default' attribute or in the text of the element.  The text supercedes the attribute.

<macroArgument> takes these attributes:

The included sourceGen elements, <template>, <inline>, and <defineLocation>, act as usual.  Note that <templateGroup> is not allowed. Instead, if you need to pass the same parameters to several templates, then wrap the <expandMacro> call inside <templateGroup>. This restriction makes it easier to specify sourcegen inheritance and avoids the disallowed situation of nested <templateGroups>.

<expandMacro> may be used, as well, intermixed with the sourceGen elements mentioned above. By default, all the relevant arguments in the current macro will be passed to the macro named in <expandMacro> (including those taking default values). This allows macros to be safely built from each other without updating all of them if a common macro’s argument list changes. You may suppress this behavior, however. See <expandMacro> for specific details.

Macro argument modifiers

When macro arguments are referenced, they may take any number of modifers, which are in the syntax: <macro argument> { ‘::’ <modifier> }.
For example, “$(myName::to-title::as-string)”.

Recognized modifiers are:

These modifiers work with argument values taking the canonical form of a function declaration’s argument list in C++, for example, with types, argument names, and optional default values:

Expanding macros

Example:
<expandMacro name="GenerateAssert"  LocationId="ID_METHOD"  Expr="6+7+'a'" />

or

<expandMacro name="GenerateAssert">
  <expandArgument name="LocationId">ID_METHOD</expandArgument>
  <expandArgument name="Expr">6+7+'a'</expandArgument>
</expandMacro>

Both of these have the same effect. In the first form, the argument names are used as XML attributes to <expandMacro>. Typically, the macro argument names are capitalized to emphasize the distinction between the fixed attributes in the XML schema and the dynamic attributes allowed for macro expansion. The second form explicitly passes arguments in the text part of the <expandArgument> element. This allows for multi-line arguments to be passed more clearly.

<expandMacro> expands a given macro into the sourceGen of the caller. This has the same effect as inserting the same templates and inlines from the macro's definition at the point of call. Variable references from those templates and inlines are substituted with the values provided in attributes (e.g. variableName="value") or expandArgument child elements. The latter may be preferred for cases where code is substituted, so the formatting may be retained.

<expandMacro> takes these arguments:

This attribute is only valid in expandMacro called from a defineMacro. Passing arguments is different from adding attributes argName="$(argName)" because it avoids defining otherwise undefined arguments. A missing optional argument is null, not the empty string. The '::is-defined' modifier can be used to check this.

Elements in the list of strings are names of arguments, or renames of the form targetArgumentName=hostArgumentName which passes hostArgumentName from the hosting macro with the name targetArgumentName (again, only if the argument is actually defined in the call).

If this argument is not specified, all arguments in the invoked macro are passed (zero or more may have defaults which are overridden in this macro).

The <expandArgument> element may appear any number of times inside <expandMacro> in place of using attributes to pass arguments. It takes these arguments:

Script Reference

Sourcegen Contribution Variables

These variables are available inside functions automatically generated by the <template> element.

Instance Global Variables

The following variables are available for use within the sourceGen XML element and scripts. These augment the JavaScript globals.

When an event handler is being handled, triggered by ifEvents="..." matching a bound event, then the first matching event provides these variables:

Contribution Engine Routines

When generating source inside <sourceGen>, scripts have access to a library of routines related to generating contributions. All these are prefixed with “Engine.”, for example: Engine.createContributionForLocation().

assignLocationsForPhase(contributions, phase, location)

Assign locations to phased contributions without a location. The contributions with the given phase are assigned the given location. Parameters:

  • contributions - the list of contributions; modified in place
  • phase - the phase to match (must not be null)
  • location - the location to assign
collateContributionsByPhase(contributions, phases)

Collate contributions by phase. Takes a list of phase names and orders contributions so that they reflect the ordering of the phases. Note that this will have no effect if a contribution already has a location. Parameters:

  • contributions - the list of contributions; modified in place
  • phases - list of phases (null may be used to represent un-phased contributions)
createContribution() Creates a contribution.
createContributionForLocation(location)

Create a contribution at a given location. Parameters:

  • location - the unique location id, defined either in this location or some calling parent
createContributionForPhase(phase)

Create a contribution for the given phase. Parameters:

  • phase - the phase, for parent collation
createFromStockFile(directoryId, filename, stockPath)

Create a stock file in the project from a file relative to the current component or an applicable base, if such file does not already exist. This is preferred to explicitly defining a location and applying a template when the file must not have any user-specified comment at the top or any variable expansion. Parameters:

  • directoryId - the directory ID for the target (see INameGenerator#..._DIRECTORY_ID)
  • filename - the filename within the directory for the target file
  • stockPath - component-relative path to stock file
findBuiltinOrGeneratedEnumeratorForAlgorithm(com.nokia.sdt.datamodel.adapter.IComponentInstance instance, propertyPath, nameAlg)

Find the enumerator generated by the given name algorithm with the given property value. These match the attributes used in <mapEnumMember>. The difference is, this routine does not generate a new enumerator; it returns null if none was generated. Parameters:

  • instance
  • propertyPath - path to the property @param uniqueValue the enumerator value which indicates a unique value was generated, or "*" if a unique value is always generated
  • nameAlg - the unique name algorithm used
findDefiningFileForEnumerator(enumerator) Get the filename where this enumerator is declared.
findGeneratedRssFiles(regex) Get the list of RSS generated files matching the given pattern.
findLocation(locationId)

Look up the ILocation for the given location id. Parameter:

  • locationId - name of the location
findOrCreateEnumeratorForAlgorithm(com.nokia.sdt.datamodel.adapter.IComponentInstance instance, propertyPath, nameAlg)

Find or create the enumerator generated by the given name algorithm with the given property value. These match the attributes used in <mapEnumMember>. This routine can create new enumerators. Such enumerators will be created in a globally accessible file (i.e. an HRH file for the application). If the enumerator was generated before but not visible in the DOM, this recreates it. Parameters:

  • instance
  • propertyPath - path to the property
  • nameAlg - the unique name algorithm used
formMatches(form, forms) Tell whether the given form regex matches any of the forms in the array.
generateAllViewContributions(form)

Generate the "glue" contributions for all the view data models. These are the contributions that an application needs to get from a view in order to register it. This automatically filters out contributions not matching the form, with the assumption that the default form templates will never apply to the case of generating from a view. Parameters:

  • form - the sourcegen form to pass
generateChildContributions(com.nokia.sdt.datamodel.adapter.IComponentInstance child, form)

Generate child contributions. Called by parent on each child. Parameters:

  • form - a regular expression matching form names ("" = blank/no form)
generateChildContributions(form) Generate all child contributions, in order. Parameters:
  • form - a regular expression matching form names ("" = blank/no form)
generateRedirectedInstanceContributions(com.nokia.sdt.datamodel.adapter.IComponentInstance instance, form, com.nokia.sdt.datamodel.adapter.IComponentInstance refInstance)

Generate child contributions using a referencing instance for context. This is used when component instance A (refInstance) points to component instance B (instance), and instance B's sourceGen relies on properties and/or events from instance A. This supplies an additional parameter to sourceGen called 'refInstance' that can be queried. Note that none of the predefined variables are defined for refInstance, as they are for instance (e.g. children, properties, instanceName, or instanceMemberName). Parameters:

  • instance - the instance to generate
  • form - a regular expression matching form names ("" = blank/no form)
  • refInstance - the instance holding a reference to this instance
generateViewContributions(viewFilePath, form) Generate the "glue" contributions for a view data model. These are the contributions that an application needs to get from a view in order to register it. This automatically filters out contributions not matching the form, with the assumption that the default form templates will never apply to the case of generating from a view.
getAllProjectViewDesigns() Get a list of project-relative paths specifying view designs.
getContributionsForForm(contributions, form)

Return sublist of contribution matching the given form. Parameters:

  • contributions - the list of contributions
  • form - regular expression to match form ("" = blank/no form)
getContributionsForLocation(contributions, locationId)

Return sublist of contribution matching the given location Id. Parameters:

  • contributions - the list of contributions
  • locationId - the location name (or null to match contributions without location)
getContributionsForPhase(contributions, phase)

Return sublist of contribution matching the given phase. Parameters:

  • contributions - the list of contributions
  • phase - the phase (or null to match contributions without a phase)
getGeneratedResource(com.nokia.sdt.datamodel.adapter.IComponentInstance instance) Get the unnamed resource generated for the given component.
getGeneratedResource(com.nokia.sdt.datamodel.adapter.IComponentInstance instance, rsrcId)

Get the unnamed resource generated for the given component. Parameters:

  • instance - the component instance
  • rsrcId - the identifier of the generated resource, or null
getGlobalDictionary() Get global dictionary for one sourcegen pass. This is entirely for the use of sourcegen script. It provides common state which is available to all instances in a design when sources are generated; nothing persists past a save of the data model.
queryEnumeratorForAlgorithm(com.nokia.sdt.datamodel.adapter.IComponentInstance instance, propertyPath, nameAlg)

Query the enumerator generated by the given name algorithm with the given property value. These match the attributes used in <mapEnumMember>. This routine does not rely on an enumerator having been created and does not uniquify the value, and can possibly return a misleading result. Parameters:

  • instance
  • propertyPath - path to the property
  • nameAlg - the unique name algorithm used
removeContributionsForPhase(contributions, phase) Remove contributions with the given phase.
removeDuplicateContributionsForLocation(contributions, location) Remove duplicate contributions based on location. The list is scanned for contributions matching the given location exactly. If such contributions have the same trimmed text, the duplicates are removed. Note that this doesn't account for any contributions implicitly supplied by the current component's <defineLocation> contributions. Either call this function from a caller or do not define possibly duplicatable content in <defineLocation> (you can supply this as <template> appearing at that location).
titleCase(text)  

 

Debugging

In the <sourceGen> element, set the debug attribute to “true” to generate a dump of the generated JavaScript that is used to generate contributions. Alternately, the Components > Enable SourceGen Debugging... dialog may be used to enable debugging and specify the directory for generated JavaScript files.

Inheritance

Source generation behavior allows inheritance by derived components, though it is not automatically inherited. If no <sourceGen> element appears in a derived component instance, the UI Designer automatically iterates the instance’s children and gathers contributions for them. If <sourceGen> does appear in a derived component, and left empty, then no source is generated for that component.

If <sourceGen> appears in a derived component, it may

A component can inherit from a base. For source generation, this implies automatic inheritance of the JavaScript base object, its locations, and its macros, but not of the behavior, which is explicitly inherited to allow selection and reordering.

NOTE Locations defined with realize="true" are not automatically realized in derived components.

The <useTemplate> element is used to inherit <template> elements from base.

The attribute “ids” lists the named <template> elements from the base component (“*” indicates all). A <template> element without an “id” cannot be inherited. This element can be repeated and ordered arbitrarily with other <template> elements. An “id” can only be referenced or defined once per <sourceGen> element.

The <useTemplateGroup> element is used to inherit <templateGroup> elements from base. A standalone <useTemplateGroup> element inherits all named <template> elements, or can contain <useTemplate> elements to select specific sub-elements.

NOTE All templates are implicitly named, or given ids, unless otherwise suppressed by specifying id="".

All <inline> elements may be inherited from the base, if they are given ids. For inlines, on the other hand, ids are not implicitly defined inside <templateGroup>.