Architecture of the Text Template Transformation Process
Updated: July 2008
The text template transformation process takes a text template file as the input, transforms it, and generates a new text file as the output. For example, you can use text templates to generate working Visual Basic or C# code from a domain-specific language.
The text template transformation process has three components: the engine, the host, and directive processors. The engine component controls the process and interacts with the host and zero or more directive processors to complete the process. If the default components do not meet the specific goals of your project, you can write a custom host, custom directive processors, or both.
The text template transformation process has two steps. In the first step, the engine creates a temporary class, which is called the generated transformation class. The generated transformation class contains the code of the text template (that is, the text blocks, statements, expressions, and class features) and the results of calling directives.
In the second step, the engine compiles and executes the generated transformation class to produce the generated text output.
The engine component controls the text template transformation process. For more information, see The Engine.
The host is the interface between the engine and the user environment. Visual Studio is a host, and Domain-Specific Language Tools also provides a command line host. For more information, see The Host.
Yes. You can write a custom host. For more information, see Creating Custom Text Template Hosts.
Directive processors are classes that handle directives in text templates. You typically use directives to provide data from an input source (for example, from a model) to a text template. When you use Domain-Specific Language Tools, a directive processor is generated for each domain-specific language, so that text templates can access the models in that language as input sources. You can also write custom directive processors. For more information, see Directives and Directive Processors.
Yes. You can write custom directive processors. For more information, see Creating Custom Text Template Directive Processors.
The engine receives the template as a string from the host, which handles all files. The engine subsequently communicates with the host to locate directive processors and to get other environmental components. The engine then compiles and runs the generated transformation class. The engine returns the generated text to the host, which then saves the text to a file.
The host is responsible for anything that relates to the external environment, including the following:
Providing a file or an assembly if the engine or a directive processor requests one. The host can search directories and the global assembly cache to locate files and assemblies. The host can locate custom directive processor code for the engine. The host can open and read files and return their contents as strings.
Providing lists of standard assemblies and namespaces to the engine. The engine uses these assemblies and namespaces when it creates the generated transformation class as part of the text template transformation process.
Providing the application domain that the engine uses to compile and execute the generated transformation class.
Writing the generated text output file.
Setting the default extension for the generated text output files.
Receiving the text template transformation errors from the engine and deciding what to do with them. For example, the host can display the errors in the user interface or write them to a file.
(Optional) Helping a directive processor resolve a required parameter value if a user has called a directive without providing a value. The directive processor can specify the name of the directive and the parameter and ask the host to provide a default value if it has one.
If you want to use the text template transformation functionality in a custom application, you can write a custom text template host. To create a custom host, you create a class that inherits from ITextTemplatingEngineHost. For more information, see Walkthrough: Creating a Custom Text Template Host.
Some examples of a custom host could be:
A host that is embedded in a custom application that wants to use the text template transformation functionality.
A command line host that is optimized for a particular directive processor.
Visual Studio provides a command line host called TextTransform.exe. For more information, see Command-Line Tools for Text Templates.
Directives and directive processors provide additional functionality to text templates. This functionality can be a simple as the ability to specify the extension of the output file or as complex as custom directive processors that read data from a database. A directive processor contains one or more directives. The same directive processor can process more than one directive. As an analogy, you can think of directive processors as classes, and you can think of directives as methods in those classes. You call directives directly from your text templates.
Directives fall into one of three types:
Frequency of Use
Built-in directives are provided by the text template transformation toolkit. By using built-in directives, you can specify common options such as the programming language of the text template and the extension of the output file. There are five built-in directives: assembly, import, template, output, and include. For more information, including the syntax to call a built-in directive, see Directive Syntax (Domain-Specific Languages).
Generated directives are based on the domain-specific language that you create. If you call a generated directive, you can access models from the statements and expressions in text templates. For more information, see Generated Directives.
You can write custom directives to provide custom functionality to text templates. You can use a custom directive processor any time that you want to access external data. For more information, see Custom Directives.
Directives work by adding code to the generated transformation class. You call directives from a text template, and the engine processes all of the directive calls when it creates the generated transformation class. After you call a directive successfully, the rest of the code that you write in your text template can rely on the functionality that the directive provides. For example, you can make the following call to the import directive in your template:
<#@ import namespace="System.Text" #>
If you make such a call, you can then use the StringBuilder class in the rest of your template code without qualifying it as System.Text.StringBuilder.
When you use Domain-Specific Language Tools to create a domain model, the tools generate a directive processor for your domain model. By calling the generated directive processor from a text template, you can access the domain classes that underlie a model. You can then write code in the text template that creates reports or code based on the model.
For example, you can run the Minimal Language wizard, build and run the solution, create a text template, and add the following directive call to it:
<#@ examplemodel processor="DSLMinimalTestDirectiveProcessor" requires="fileName='Sample.min'" provides="ExampleModel=ExampleModel" #>
In this example, DSLMinimalTestDirectiveProcessor is the generated directive processor, and ExampleModel is the directive. After you call the directive, you can access the model in the text template code. For example, you can specify the following:
For more information, see Accessing Models from Text Templates.
You can write custom directive processors to provide custom functionality to your text templates. You can use a custom directive processor any time that you want to access external data or resources from a text template.
Different text templates can share the functionality that a single directive processor provides, so directive processors provide a way to factor code for reuse. The built-in include directive is similar, because you can use it to factor out code and share it among different text templates. The difference is that any functionality that the include directive provides is fixed and does not accept parameters. If you want to provide common functionality to a text template and allow the template to pass parameters, you must create a custom directive processor.
Some examples of custom directive processors could be:
A directive processor to return data from a database that accepts a user name and password as parameters.
A directive processor to open and read a file that accepts the name of the file as a parameter.
For more information, see Walkthrough: Creating a Custom Directive Processor.