GeneralCodeFactory templating language

Overview

The GeneralCodeFactory plug-in processes template files to produce source code. The syntax of the templating language recognized by the GeneralCodeFactory is a superset of the syntax of the language of the desired output.

Template Syntax

The GeneralCodeFactory template parser processes source code written in the target language, and looks for any comment lines which contain pre-processing tags, which are used to "mark-up" the code.

The UmdCodeFactory's template parser recognizes the following tags:

Open Tag

Close Tag

<per_table(TableIdentifier)>

</per_table>

<per_field(FieldIdentifier)>

</per_field>
<per_field(FieldIdentifier, Flags)> </per_field>

<include_if(Condition)>

</include_if>
<per_relationship(RelationType, RelationIdentifier)> </per_relationship>
<per_ancestor> </per_ancestor>
<file(Filename)> </file>
<merge> </merge>

Each occurrence of an open tag must be matched by a corresponding close tag. Lines of code which fall between corresponding open and close tags are affected by the open tag.

Open and close tags must be at the beginning of a comment line.

eg.

Programming Language

Comment line with pre-processing tag.

Visual Basic

'<per_table(TableNameIdentifier)>

C++, C#, PHP, etc

//<include_if(Condition)>

<per_table(TableIdentifier)>

The <per_table> tag can be used to generate code which will be the same or similar for each table.

eg.

'<per_table(Table)>
Public Class Table
Public ReadOnly Property ClassName As String()
Get
Return "Table"
End Get
End Property
End Class
'</per_table>

Using this template with the Music Database schema will produce the following code:

Public Class Artist
Public ReadOnly Property ClassName As String()
Get
Return "Artist"
End Get
End Property
End Class
Public Class Album
Public ReadOnly Property ClassName As String()
Get
Return "Album"
End Get
End Property
End Class
Public Class Track
Public ReadOnly Property ClassName As String()
Get
Return "Track"
End Get
End Property
End Class

<include_if(Condition)>

The include_if tag can be used to control whether a particular section of a template is included in the generated code.  The template parser will evaluate the supplied Condition and will only process the code between <include_if()> and </include_if>, if the condition is evaluated as True.

Examples of expressions supported by the include_if tag.

Limitations

The include_if tag currently does not support the boolean operators "and" or "or".  However, include_if tags may be nested to simulate "and".

eg.

'<per_table(Table)>
Public Class Table
Public ReadOnly Property ClassName As String()
Get
Return "Table"
End Get
End Property
End Class
</per_table>

Using this template with the Music Database schema will produce the following code:

<per_field(FieldIdentifier)>

The <per_field> tag can be used to generate code that will be the same or similar for each field of each table. The <per_field> tag can only be used within the scope of a <per_table> or <per_ancestor> tag.

Any word or any combination of words and numbers (without spaces) can be used in place of FieldIdentifier.

Any blocks that fall within the scope of a <per_field> tag will be repeated in the output for each field defined in each table definition enumerated by the controlling <per_table> or <per_ancestor> tag.

Inline Expressions

The GeneralCodeFactory template parser can embed the result of evaluating "inline" expressions in the generated code.

The general form of an inline expression is

#{ObjectIdentifier.PropertyName}

This syntax cannot be used when generating code for certain programming languages, such as Ruby, for which this syntax is part of the language of the code being generated. In such cases, it is necessary to use the AlternateCodeFactory template parser, which recognizes inline expressions of the form

$[ObjectIdentifier.PropertyName]

Limitations

Only very simple expressions can be used as inline expressions.

The only allowed operator is the "." operator.

The expression must evaluate to a primitive type (not an object).

The first token in the expression must be an object identifier - a table identifier, a field identifier, or a relationship identifier.

Inline expressions must be placed within the scope of an iteration tag - per_table, per_field, per_relationship, or per_ancestor. An iteration tag is necessary in order to define an object identifier to use as the first token in the expression.

Recognized properties

The property names recognized by the GeneralCodeFactory template parser fall into three categories:  standard properties, custom properties, and special properties.

The standard properties are the properties defined by the UMD type being iterated. If ObjectIdentifier identifies a TableDefinition, the standard properties are the properties of a TableDefinition. If ObjectIdentifier identifies a FieldDefinition, the standard properties are the properties of a FieldDefinition. If ObjectIdentifier identifies a RelationshipDefinition, the standard properties are the properties of a RelationshipDefinition.

Custom properties are defined by the importer plugin used to read the schema into memory. If the schema file is a dbml file, attributes defined in the dbml file which are not mapped to standard properties are instantiated as custom properties in the TableDefinition and FieldDefinition objects by the UmdDbml.DbmlFilter.

Additional custom properties may be defined (or overridden) by adapters (pass-through filters). For example, the UnityTricks.GetDifferences plugin populates a custom property called "MigrationStatus" on both table and field definitions.

Because the available custom properties depend on the sequence of plugins used to read the schema into memory, templates which rely on custom properties may only work with certain plugins.

The following special properties are defined by the template parser:

Applies to

Special Property

Description
TableAncestorIn Schemas which do not specify an EntityBase, the Ancestor property returns the ultimate parent from which the Table (or Class) inherits. In Schemas which do specify an EntityBase, the Ancestor property returns the penultimate parent (in such models the EntityBase is by definition the ultimate parent from which all Tables/Classes derive).
TableParentsReturns a collection of the tables which the current table specializes. This property is only valid if followed by ".Count".
TableParentReturns the table which the current table specializes. If the current table specializes more than one table, an error is raised. If the current table does not specialize any table, nothing is returned.
TableChildrenReturns a collection of the tables which the current table generalizes. This property is only valid if followed by ".Count".
RelationshipTable1Alias for "FirstTable"
RelationshipTable2Alias for "SecondTable"
FieldForeignTableIf the field is the ForeignKey of a BelongsTo/HasMany relationship, the ForeignTable property returns the TableDefinition corresponding to the foreign table in the relationship of which the field is a participant.
FieldIsForeignKeyReturns True if the field is the foreign key of a BelongsTo/HasMany relationship. A primary key field can participate in many BelongsTo/HasMany relationships, but a field can be the foreign key of no more than one BelongsTo/HasMany relationship. Consequently, a foreign key field can also be used to uniquely identify the relationship itself.
FieldToSqlReturns the SQL definition for the field using T-SQL syntax. The IDENTITY keyword is used to specify Fields whose AutoIncrement property is True. The IDENTITY keyword is specific to Microsoft SQL Server.
Field.DataTypeToSqlReturns an SQL data type that maps to the DataType property of the field definition. The DataType property supports a limited number of types, defined by the UT.UnityTypeConstants enumeration. The mapping from UnityTypeConstants to SQL is fixed, cannot be changed, and may differ from the SQL type defined in the schema file. Field.DataType.ToSql returns SQL that is more portable than the SQL returned by Field.ToSql, but does not consider Field properties other than DataType, such as AutoIncrement.
StringPluralReturns the plural form of an English noun. If the string is not an English word, or not a noun, the result unpredictable. If the string is already in plural form, the result will be a superplural.
StringSingularReturns the singular form of an English noun. If the string is not an English word, or not a noun, the result could be anything. If the string is already in singular form, the result is unpredictable.

The Singular & Plural properties can be used to inflect English nouns. This is useful when the tables in a database are named after singular nouns and a class or property name is required to be in plural form.

For example, the following template assumes that tables are named in singular form, creates classes named in singular form, and for each class creates properties for each related class in either singular or plural form depending on the type of relationship.

'<per_table(Table)>
Public Class Table
  '<per_relationship(BelongsTo, Related)>
  Private m_#{Related.Table2.Name} As #{Related.Table2.Name}
  '</per_relationship>
  '<per_relationship(HasMany, Related)>
  Private m_#{Related.Table2.Name.Plural} As System.Collections.Generic.IList(Of #{Related.Table2.Name})
  '</per_relationship>

  '<per_relationship(HasMany, Related)>
  Public ReadOnly Property #{Related.Table2.Name.Plural} As System.Collections.Generic.IList(Of #{Related.Table2.Name})
    Get
      Return m_#{Table2.Name.Plural}
    End Get
  End Property

  '</per_relationship>
  '<per_relationship(BelongsTo, Related)>
  Public ReadOnly Property #{Related.Table2.Name} As #{Related.Table2.Name
    Get
      Return m_#{Table2.Name}
    End Get
  End Property

  '</per_relationship>
End Class
'</per_table>

Using this template with the Music Database schema will produce the following code:

Special Inline Expressions

It is frequently necessary to omit certain characters from the first or last item in an automatically generated list.

For example, consider the following template for a VB.Net class constructor:

Public Sub New( _
'<per_field(Field)>
, ByVal #{Field.Name} _
'</per_field>
)
'<per_field(Field)>
  m_Field = Field
'</per_field>
End Sub

This template might generate the following syntactically incorrect constructor:

Public Sub New( _
, ByVal FirstName As String _
, ByVal LastName As String _
  m_FirstName = FirstName
  m_LastName = LastName
)
End Sub

We want to omit the comma from the first line of the parameter list.

The GeneralCodeFactory template parser defines special inline expressions which behave differently depending on whether they appear at the beginning, middle, or end, of an iteration block defined by a per_table, per_field, per_relationship, or per_ancestor tag.

When placed at the beginning of an iteration block, #{,} will output a space in the first iterations and a comma in the second and subsequent iterations. Placed at the end of an iteration block, it will output a comma in all but the last iteration and will output a space in the last iteration. Placed elsewhere in the iteration block, the #{,} expression behaves identically to a , and is included in the output for all iterations.

The #{,} special inline expression can be used in our example constructor template thus:

Public Sub New( _
'<per_field(Field)>
#{,} ByVal #{Field.Name} _
'</per_field>
)
  '<per_field(Field)>
  m_Field = Field
  '</per_field>
End Sub

to generate the following

Public Sub New( _
   ByVal FirstName As String _
, ByVal LastName As String _
)
  m_FirstName = FirstName
  m_LastName = LastName
End Sub

The following table lists special inline expressions which behave similarly to the #{,} expression:

Character

Special inline expression (GeneralCodeFactory) Special inline expression (AlternateCodeFactory)

,

#{,} $[,]

;

#{;}

$[;]
_ #{_} $[_]
& #{&} $[&]
+ #[+] $[+]
- #[-] $[-]
\ #[\] $[\]
. #[.] $[.]