Click here to Skip to main content
Email Password   helpLost your password?

Introduction

So, what is TaHoGen, you may ask?

TaHoGen is a 100% free, Open Source code generation engine (licensed under the GPL) with the following features:

Like CodeSmith, TaHoGen is a code generator generator. It parses the text from template files and converts that text into a CodeDom graph which, in turn, is compiled (using the .NET language of your choice) into a customized text generator which outputs exactly the text that you specify.

What TaHoGen is not

Unlike its commercial counterpart, TaHoGen doesn’t come with a fancy GUI out of the box. In fact, in its rawest form, it doesn’t even have a GUI at all. In this first article, I will show you how to use the engine itself. In the next article in this series, we’ll create a simple GUI for running our templates, and we’ll even integrate it into the VS.NET IDE so that we can send the output of the template directly to the code window—but that’s for later. For now, sit tight, and I promise that I will make this current article worth your while.

Background

At first, I was fascinated with how Eric Smith (author of CodeSmith) was able to parse ASP-style text files and convert them into templates. I wanted to learn how to do this myself without having to learn regular expressions, so I looked at a few alternatives such as ANTLR, Bison/Lex, and GoldParser. For me, ANTLR-generated code was a nightmare to behold, and Bison/Lex was a little too immortally cryptic for my mere mortal reach. GoldParser, on the other hand, had problems parsing context-sensitive grammars such as ASP.NET, so that was out of the question as well. I needed something that would allow me to build an ASP tag parser incrementally, while at the same time affording me the speed of C++ generated native code. That’s where The Spirit Parser came in handy. After tinkering with it for nearly three months, I finally got the right BNF grammar to parse most CodeSmith template files without a hitch. Once the grammar was done, like any other curious coder with a lot of time on his hands, I figured, “If I can write a parser for this, why not go all the way and write my own implementation from scratch?”

..And so I did. Now, after a year and nearly four rewrites, it is finally ready for public consumption! :)

“I still don’t get it – would you write another engine if CodeSmith is free anyway?”

The main reason why I spent this much time writing another engine is that I felt that CodeSmith, while extremely useful, didn’t fit all of my needs. Like many users, I wanted to send the output of a single template to multiple locations at once, and I also wanted to generate multiple files from a single template without resorting to a few hacks in the template source (no offense, Eric). Secondly, I wanted to use the engine in my own personal projects, but I certainly didn’t have the funds to pay for a license. Third, even if I had the money, I wasn’t going to pay for a product that didn’t have all the features that I was looking for. Lastly, I wanted to write something that would be useful for the developer community at large as a way of saying “Thank you” for all of the help they’ve given me in the past, especially at CodeProject. For me, this was a labor of love, and I hope you enjoy it as much as I had fun coding it!

Using the code

Since this is only an introductory article (and my very first article ever!), I’ll show you just enough to get you started in using this library. Hopefully (once I get more time), we can delve further into the internals of this library in future articles of this series. For now, the following sections will have to suffice. This article will be divided into two parts—first, I’ll show you what the template code looks like, and second, I will show you how you can use the engine in your own applications.

Minimum Requirements for Building

You are going to need:

What You Need to Know Before We Begin

This article assumes that you’ve had some experience with writing CodeSmith templates, and that you are familiar with coding in either VB.NET, or C#. With that aside, let’s get started.

Template Language Differences from CodeSmith

The differences between the template languages of TaHoGen and CodeSmith are minimal. For the most part, they are identical, with a few notable exceptions. Let’s start with a simple example – a property get/set generator for C#:

<%@ CodeTemplate ClassName="PropertyGenerator" 
   Namespace=”MyTemplateNamespace” Language="C#" TargetLanguage="C#"%>
<%@ Property Name="Name" Type="System.String" Category="Options" %>
<%@ Property Name="Type" Type="System.String" Category="Options" %>
<%@ Property Name="ReadOnly" Type="System.Boolean" 
                Default="true" Category="Options" %>
public <%=Type%> <%=Name%>
{
 get { return _<%=Name.Substring(0, 1).ToLower() + 
       Name.Substring(1)%>; }<%if (!ReadOnly) {%>
 set { _<%=Name.Substring(0, 1).ToLower() + 
       Name.Substring(1)%> = value; }<%}%>
}

For those of us who are familiar with CodeSmith’s template syntax (or ASP.NET), this is self explanatory. The template above reads two strings, Name and Type, respectively, and generates text similar to the following:

public string MyProperty
{
 get { return _myProperty; }
 set { _myProperty = value; }
}

The difference between the two syntaxes is found in the following line:

<%@ CodeTemplate ClassName="PropertyGenerator" 
  Namespace=”MyTemplateNamespace” Language="C#" TargetLanguage="C#"%>

The ClassName attribute sets the name of the generated template to “PropertyGenerator”, while the Namespace attribute assigns that template to a namespace named “MyTemplateNamespace”. TaHoGen will then generate a template class that looks like this:

namespace MyTemplateNamespace
{
    using System;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing.Design;
    using System.IO;
    using TaHoGen.Generators;
    using TaHoGen.Targets;
    
    public class PropertyGenerator : TaHoGen.Generators.TextGenerator
    {
        private string _name;
        private string _type;
        private bool _readOnly;
        public PropertyGenerator()
        {
        }
        public PropertyGenerator(TaHoGen.PropertyBag propertyBag) : 
                this()
        {
            this.LoadProperties(propertyBag);
        }
        [Category("Options")]
        public string Name
        {
            get
            {
                return this._name;
            }
            set
            {
                this._name = value;
            }
        }
        [Category("Options")]
        public string Type
        {
            get
            {
                return this._type;
            }
            set
            {
                this._type = value;
            }
        }
        [Category("Options")]
        public bool ReadOnly
        {
            get
            {
                return this._readOnly;
            }
            set
            {
                this._readOnly = value;
            }
        }
        protected override void GenerateImpl(System.IO.TextWriter writer)
        {
            writer.Write("\r\npublic ");
            writer.Write(Type);
            writer.Write(" ");
            writer.Write(Name);
            writer.Write("\r\n\t\t{\r\n\t\t\tget { return _");
            writer.Write(Name.Substring(0, 1).ToLower() + Name.Substring(1));
            writer.Write("; }");
            if (!ReadOnly) {
            writer.Write("\r\n\t\t\tset { _");
            writer.Write(Name.Substring(0, 1).ToLower() + Name.Substring(1));
            writer.Write(" = value; }");
            }
            writer.Write("\r\n\t\t}\r\n\t\t");
        }
    }
}

As you can see from the example above, setting the name of the template and assigning it to a particular namespace is trivial. No need to worry if you decide not to define a class name and a namespace for your template—if you omit either one of these attributes, then the default values will be entered for you.

CodeBehind in Other Languages

The syntax for including a codebehind file in TaHoGen is the same as the one in CodeSmith. The only difference here is that TaHoGen supports building codebehind files in languages other than the one currently being used in the template, as shown in this example:

<%@ CodeTemplate ClassName="MyTemplate" 
        Namespace=”MyNamespace” Language="C#" TargetLanguage="C#"%>
<%@ Assembly Src=”myclass1.vb” %> <%---Build a VB.NET source file--%>
<%@ Assembly Src=”myclass2.js” %> <%---Build a JScript source file--%>

<%---The rest of your template would go here--%>

SubTemplate Support

Compiling Templates within Templates at Build Time

This library allows you to compile templates within templates and have those externally compiled templates automatically included as part of the main assembly of your template. The directives:

<%@ Compile Template="Property.tgt" outputfilename="MyExternalAssembly.dll" %>
<%@ Import Namespace=”MyTemplateNamespace”%>

…will compile the example above and include the “MyTemplateNamespace” namespace as part of the build output, and include the namespace of that compiled assembly (“MyExternalAssembly.dll”) as part of your main template.

Compiling Templates (within Templates) at Run Time

You can also build templates at runtime from within your application (and even from within your own templates). Here is a complete example in VB.NET:

Imports System
Imports System.Diagnostics
Imports System.IO
Imports System.Reflection
Imports TaHoGen
Imports TaHoGen.Targets
Module Module1

    Sub Main()
        Dim text As String

        'Read the contents of the template
        Dim reader As New StreamReader("property.tgt")
        text = reader.ReadToEnd()

        'Compile it into a single assembly
        Dim templateAssembly As [Assembly] = TemplateCompiler.Compile(text)

        'Did it succeed?
        If templateAssembly Is Nothing Then
            Console.WriteLine("Template Compilation Failed!")
            Return
        End If

        'There's only going to be one template in this assembly
        'so it's safe to return just the first one 
        Dim templateType As Type = templateAssembly.GetTypes()(0)

        'This *should* work
        Debug.Assert(Not (templateType Is Nothing))

        'Set the properties for the template
        Dim properties As New PropertyTable

        properties("Type") = "string"
        properties("Name") = "MyProperty"
        properties("ReadOnly") = False

        'Instantiate the template and assign the properties at the same time
        Dim args As Object() = {properties}
        Dim generator As ITextGenerator = _
          CType(Activator.CreateInstance(templateType, args), ITextGenerator)

        'We should have a valid generator at this point
        Debug.Assert(Not (generator Is Nothing))

        
        'Write to the console
        Dim output As New ConsoleTarget

        'Attach the output of the generator to the console
        output.Attach(generator)

        'Generate the output itself
        output.Write()
    End Sub
End Module

Most of the code above is pretty straightforward. The call to TemplateCompiler.Compile() builds the template into an assembly, and once that assembly is compiled, the only thing left to do is to create an instance of that template and run it. Notice that although the template itself was instantiated, the code never calls the template directly to generate the text:

        Dim generator As ITextGenerator = _
           CType(Activator.CreateInstance(templateType), ITextGenerator)

        'Write to the console
        Dim output As New ConsoleTarget

        'Attach the output of the generator to the console
        output.Attach(generator)

        'Generate the output itself
        output.Write()

Instead, the output of the template is attached to the console, and the template executes as soon as Output.Write() is invoked. The template itself never directly knows about which target it will be writing to. In short, the output targets and the templates never directly refer to each other outside of their respective interfaces. This approach makes it very easy to attach many outputs to the same template, and vice-versa, and for me, it has come in handy on many occasions.

SubTemplates as Template Properties

There might be times where you would want to leave a portion of your template open, or define a region in that template where you could insert the output of another template. Here’s one way you can do it:

<%@ CodeTemplate ClassName="SubTemplateSample" Language="C#" TargetLanguage="C#"%>
<%@ Property Name="FirstRegion" Type="ITextGenerator" Category="SubTemplates" %>
<%@ Property Name="SecondRegion" Type="ITextGenerator" Category="SubTemplates" %>

#region This is the First Region
<%=RunTemplate(FirstRegion)%>
#endregion

#region This is the Second Region
<%=RunTemplate(SecondRegion)%>
#endregion

Now, you might be wondering how to assign a template to another property of another template. We’ll handle that in the next section. For now, you can think of template properties as being no different from other properties based on primitive types.

Executing Property SubTemplates from within Your Template

Notice that both the FirstRegion and SecondRegion subtemplates from the examples above are template properties of the type ITextGenerator. (This is the base interface that all templates must implement in order to be recognized as templates). In this case, we’re going to use the ITextGenerator properties to act as stubs. Each call to the RunTemplate() method checks if there is a template attached to the current property, and if there is a template attached, it will run that template and insert its output into that particular section of the template. Otherwise, that section will remain blank as if the template never existed.

Engine Usage

Now that we’re done with some of the basics of the template syntax, I’m going to show you how to integrate the engine into your own application. Once you’ve built the entire solution, you’re going to need to reference the TaHoGen.Core.dll assembly in your project in order to use the engine.

A Few Notes before We Begin

If you are going to build or rebuild the binaries for the engine, make sure you have the latest version of the Boost libraries. (At the time of this writing, TaHoGen uses Boost v1.31). You’re going to need it to compile the template parser. Since the template parser is written in C++/COM, you also might need to run regsvr32.exe on CodeSmithParser.dll to register it with COM Interop once it has been built. Anyway, let’s go on to the discussion.

The TemplateCompiler Class

This is the class that does most of the work. It has a single static method, Compile(), which has the following overloads:

// Methods for compiling a single template
public static Assembly Compile(string text)
public static Assembly Compile(string text, bool addDebugSymbols)
public static Assembly Compile(string text, 
       string outputFileName, bool addDebugSymbols)
public static Assembly Compile(string text, string outputFileName, 
       bool addDebugSymbols, ICompilerCallback compilerCallback)

// Methods for compiling multiple templates into one assembly
public static Assembly Compile(string[] fileList)
public static Assembly Compile(string[] fileList, string outputFileName)
public static Assembly Compile(string[] fileList, 
       string outputFileName, bool addDebugSymbols)
public static Assembly Compile(string[] fileList, 
       string outputFileName, bool addDebugSymbols, 
       ICompilerCallback compilerCallback)

Most of the parameters above are self-explanatory, with the exception of the last parameter, compilerCallback. The Template compiler uses ICompilerCallback interface to report the results of a compilation attempt. Its interface is defined as follows:

public interface ICompilerCallback
{
  void BeginCompile(CompilerArgs args);
  void EndCompile(CompilerArgs args);
}

The CompilerArgs class, in turn, is defined as:

public class CompilerArgs 
{
    private string _source;
    private CompilerErrorCollection _errors = new CompilerErrorCollection();
    public CompilerArgs(string source, CompilerErrorCollection errors)
    {
        _source = source;

        if (errors != null)
            _errors.AddRange(errors);
    }
    public string CompiledCode
    {
        get { return _source; }
    }
    public CompilerErrorCollection Errors
    {
        get { return _errors; }
    }
}

This interface can come in handy if you want to implement a GUI that displays the result of a compilation. For now, since we aren’t going to make a GUI in this article, we can safely ignore it.

Compiling a Single Template into an Assembly

Building a template into a compiled assembly is easy. A single call to TemplateCompiler.Compile() does the job:

Dim templateAssembly As [Assembly] = TemplateCompiler.Compile(text)

…where text is a string variable that holds the contents of a single template file.

Compiling Multiple Template Files into a Single Compiled Assembly

Combining multiple template files into a single assembly is just as easy as compiling a single template:

'Define the list of files
Dim files as String() = {“template1.tgt”, “template2.tgt”}

'Build it! 
Dim combinedAssembly As [Assembly] = TemplateCompiler.Compile(files)

Note that all of the template files in that list of files must share the same language. For example, if one of the templates is written in C#, then the rest of the templates have to be written in C# as well.

Shared Properties

As I mentioned earlier, TaHoGen allows you to set multiple property values onto a single, shared object that you can pass to multiple templates so that you will only have to set the properties for all of the templates once. For example, here is how you would insert the property generator template into the FirstRegion from the previous example above:

PropertyTable properties = new PropertyTable();
properties["FirstRegion"] = new PropertyGenerator();

// The sample objects will automatically be initialized with
// the new property generator with the same values.
// Notice that we only have to set the properties once. Cool, eh? 
SubTemplateSample sample1 = new SubTemplateSample(properties);
SubTemplateSample sample2 = new SubTemplateSample(properties);

. . .

// (This is where you would tell the sample templates to output the code, etc)

Alternatively, you can use the LoadProperties() method to assign the property values in the same manner:

…
sample1.LoadProperties(properties);
sample2.LoadProperties(properties);
…

Notice that templates sample1 and sample2 share the same set of properties. I tried to keep the design of the library as simple as possible, and hopefully this feature will help keep things simple.

(Note: The example above assumes that you’ve compiled both the subtemplate sample and the property generator templates into their respective assemblies and referenced them in your project. If you have built your templates from within another template, you will have to instantiate those templates through reflection, similar to what we did above in the complete VB.NET example.)

Another important thing to note is that the PropertyTable is type safe. It will only assign a property value to a template if the property type of that template and the value of whatever is stored in that property table are compatible with each other. Otherwise, the current value stored in the property table will be ignored. (You can also connect a property table to a PropertyGrid control and edit its properties directly—but I’ll save that little detail for the next article.)

Handling Changes in Property Table Values

At this point, you might be wondering what happens if you change a value in a property table object that is shared among two or more templates. If a property value changes in a PropertyTable, does that mean that you have to assign that same PropertyTable to all of those templates all over again? Not at all! Once you change a value on that particular PropertyTable object, that same change will be propagated to all templates that are attached to that object. You only have to set that property value once.

Single Template, Multiple Outputs

There might be times when you might want to send the output of a template to more than one location. Suppose that you wanted to send the output of the PropertyGenerator template to the debug window, the console, and an external file all at the same time. This is how you do it:

// Set the property values as we did before
PropertyTable properties = new PropertyTable();
properties[“Type”] = “string”;
properties[“Name”] = “MyProperty”;
properties[“ReadOnly”] = false;

// Instantiate the property generator and assign the property values
PropertyGenerator generator = new PropertyGenerator(properties);

DebugTarget debugOut = new DebugTarget();
ConsoleTarget consoleOut = new ConsoleTarget();
FileTarget fileOut = new FileTarget(“output.txt”, FileMode.Create);

// Connect the generator to its respective outputs
debugOut += generator;
consoleOut += generator;
fileOut += generator;

// Generate the output, and we’re done
debugOut.Write();
consoleOut.Write();
fileOut.Write();

…It doesn’t get any easier than that. :)

Chaining Templates Together

You can also combine templates together to form more complex templates. Here is a trivial, but useful example with the built-in SimpleTextGenerator template:

SimpleTextGenerator hello = new SimpleTextGenerator(“Hello, ”);
SimpleTextGenerator world = new SimpleTextGenerator(“World!”);

TextGenerator helloWorld = hello + world;

// Say “Hello, World!” to the console
ConsoleTarget console = new ConsoleTarget();
console.Attach(helloWorld);
console.Write();

You can even combine the helloWorld template with another template to form another composite template. As you can see, there’s absolutely no limit to the templates you can make using this feature. The rest I leave to your imagination.

If your favorite .NET language doesn’t support operator overloading

Alternatively, if the .NET language you’re using does not support operator overloading (i.e., VB.NET), you can use the TextGenerator.Combine() method instead:

Dim helloWorld as TextGenerator = TextGenerator.Combine(hello, world)

In C#, the signature of the Combine() method is defined as follows:

public static TextGenerator Combine(params TextGenerator[] generators);

No matter which method you choose (whether it is operator overloading, or the Combine() method), both methods will produce the same result.

Features That Are Not Supported

Inherits Attribute

I chose not to implement the inherits="" attribute in order to keep things simple. Allowing the user to insert their own custom TextGenerator-derived template into the object model unnecessarily complicates the CodeDom portion of the engine, and I think that many of the features that inheritance affords in this particular case can be easily replaced by other methods, such as containment and delegation.

CodeTemplateInfo Object

Since I do not have (nor intend to purchase) a CodeSmith source license, I can’t include anything that might be a part of Eric J. Smith’s object model from CodeSmith. On the other hand, although it’s really easy to implement our own custom CodeTemplateInfo object, I fail to see any immediate use for it at this point.

Features That Will Be Implemented in the Future

There are a lot of things that I would love to implement in TaHoGen, but I either just don’t have the time, or, I just don’t have the skill to implement them at this point. Among these are:

Features That Will Never Be Implemented

Points of Interest

I think that it goes without saying that an engine like this one has an infinite amount of potential uses. You can plug this engine into an ASP.NET page and use it to emulate Master Pages, or you can use it to act as an SQL generator for an object-relational mapping layer. The possibilities are endless. I think the best part about templates in this engine (and CodeSmith to some extent) is that it doesn’t impose any sort of methodology on you. It lets you decide what your code should look like, and not the other way around. In the end, the only limit is your imagination.

Special Thanks

I have to thank the following people who made this project possible:

License

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAnother way
somalezu
3:53 15 Sep '06  
Hello there,
I am wondering .. did you know you can use the ASP.Net parser in your custom, desktop application ? Hosting the asp.Net engine in your application is done in 10 lines of code, and then you have 100% CodeSmith compatibility - because I think this is the way Eric Smith "implemented" his parser as well Smile .
I've done this very simple test a few months ago - feel free to contact me at my gmail account (cosmin dot oprea) if you still want to make some Open Source CodeSmith-like generator.

Somalezu
GeneralRe: Another way
WillemM
4:33 5 Aug '07  
I'm not sure if eric build his parser that way, but the TahoGen engine looks rather different to me. There are constructions that aren't possible in ASP.NET, so I'm actually pretty sure Eric at least customized the ASP.NET parser at some point.

WM.
What about weapons of mass-construction? "What? Its an Apple MacBook Pro. They are sexy!" - Paul Watson My blog

NewsTaHoGen now has a hosting site
Philip Laureano
16:44 26 Oct '05  
If you have any comments/suggestions (or you just want to get the latest source), you can find it at this link:

http://wdevs.com/Default.aspx?tabid=126


Thanks!

GeneralRe: TaHoGen now has a hosting site
Marc Sommer
2:54 23 Feb '07  
http://wdevs.com/Default.aspx?tabid=126[^]
GeneralBug in TemplateCompiler.cs
Styx31
0:50 20 Sep '05  
in method
Assembly TemplateCompiler.Compile(string text, string outputFileName, bool addDebugSymbols, ICompilerCallback compilerCallback) you create the array ReferencedAssemblies and call AssemblyCompiler.Compile() with sourceGen.GetCompileUnit() parameter.

But in Assembly AssemblyCompiler.Compile(CodeCompileUnit compileUnit, string outputFileName, CodeDomProvider provider, StringCollection ReferencedAssemblies, bool addDebugSymbols, ICompilerCallback compilerCallback) you don't use compileUnit.ReferencedAssemblies anymore and just extract code.

So, when calling Compile() overload, you just pass sourceText and no copy of ReferencedAssemblies...

Resulting is loss of any <% Assembly Name="..." %> directive in template source...

My workaround was just to change the method into this :

public static Assembly Compile(CodeCompileUnit compileUnit, string outputFileName, CodeDomProvider provider, StringCollection ReferencedAssemblies, bool addDebugSymbols, ICompilerCallback compilerCallback)
{
StringWriter writer = new StringWriter();
string sourceText = string.Empty;

#region Code Generation Options

CodeGeneratorOptions codeGenOptions = new CodeGeneratorOptions();

// Change this option if you want the hanging
// curly brace style of output in the compiled
// template source code (e.g. Java's
// programming style)
codeGenOptions.BracingStyle = "C";
codeGenOptions.BlankLinesBetweenMembers = false;
provider.GenerateCodeFromCompileUnit(compileUnit, writer, codeGenOptions);

#endregion
foreach (string assemblyName in compileUnit.ReferencedAssemblies)
{
ReferencedAssemblies.Add(assemblyName);
}

sourceText = writer.ToString();

return Compile(sourceText, outputFileName, provider, ReferencedAssemblies, addDebugSymbols, compilerCallback);
}

GeneralRe: Bug in TemplateCompiler.cs (Fixed)
Philip Laureano
17:34 20 Sep '05  
Styx31 wrote: foreach (string assemblyName in compileUnit.ReferencedAssemblies)
{
ReferencedAssemblies.Add(assemblyName);
}

Hi Styx,

Thanks for pointing that out. Actually that bug has already been fixed (with the *exact* same fix you mentioned--heh, great minds think alike eh? Smile with the latest version of the library, but I'm having problems updating this article since it was filed under a different account ("Philip.Laureano" instead of "Philip Laureano") and I can't seem to update it. Any CP editors out there that can help me out with this?

Anyway, if you need to get the latest copy of the libraries itself, you can download it from the link posted in my other article about TaHoGen. If you need a copy of the updated source, just drop me a line via email and I'm more than willing to send you the latest copy. HTH

-Philip Laureano

GeneralRe: Bug in TemplateCompiler.cs (Fixed)
Styx31
1:22 21 Sep '05  
Ok, I'll download the updated version.

Please, for further updates, consider separating all your class into different files Smile I've done it locally, but without any "changelog" between this version and the updated one, I have to override all my files with the new ones.

I'll contact you to obtain the lastest source version.

Thanks anyway Wink
GeneralRe: Bug in TemplateCompiler.cs (Fixed)
Styx31
7:08 21 Sep '05  
I've encountered some problems with unicode templates with special chars (using french characters). This simple template causes problems :
<%@ CodeTemplate ClassName="Sample" NameSpace="" Language="C#" TargetLanguage="C#" %> Test de caractères spéciaux
When I compile this, I have this content for GenerateImpl method :

protected override void GenerateImpl(System.IO.TextWriter Response)
{
Response.Write("\r\nTest de caract");
}

The parser stops when it encounters special chars... I load my template content using standard StreamReader (debugging the string show the correct content).
GeneralRe: Bug in TemplateCompiler.cs (Fixed)
Philip Laureano
16:51 21 Sep '05  

Styx31 wrote: I've encountered some problems with unicode templates with special chars (using french characters). This simple template causes problems :

<%@ CodeTemplate ClassName="Sample" NameSpace="" Language="C#" TargetLanguage="C#" %>
Test de caractères spéciaux

When I compile this, I have this content for GenerateImpl method :


protected override void GenerateImpl(System.IO.TextWriter Response)
{
Response.Write("\r\nTest de caract");
}


The parser stops when it encounters special chars... I load my template content using standard StreamReader (debugging the string show the correct content).


Interesting. It seems that the parser itself chokes on unicode chars--I'll have to roll up my sleeves again and check the C++ source files and see if I can find a way to hack it so that it accepts unicode as input. Anyone out there have any experience in using the Spirit library in unicode?


GeneralUnicode Not Supported
Philip Laureano
17:37 25 Sep '05  
Styx,

I've looked at the source for CodeSmithCompiler.dll and unfortunately, it only supports the ANSI text format. Not to worry, though--I've already started working on porting the a Unicode version of the parser to the .NET Framework, and I should have it ready and available by as early as the end of this week (the week of Sept 26, 2005).



GeneralStrange behavior
Styx31
7:59 27 Sep '05  
I've changed some behaviors in TextGenerator.cs, specially the LoadProperties() method called in template's ctor.

The problem for me was the binding I made of ITextGenerator juste after the _generator = (ITextGenerator)Activator.CreateInstance(templateType, new object[] { _properties });.

Because _properties has some values, and they don't show up in property grid until I've called the first Generate(), I has to change LoadProperties() behavior.

I think it's caused by the lazyLoad initialized at true in default ctor. I've changed default ctor to call LoadProperties() with lazyLoad = false.

Dunno if this behavior is already corrected in last version or is "by design" or if there is a tip to avoir changes in TextGenerator.cs... I cannot use the version provided here because I must use a .net v2 generator that is generic's enabled, so, I have to use source code (not currently provided for your last version).
GeneralRe: Strange behavior
Philip Laureano
15:29 20 Oct '05  
Hi Styx,

You don't need to change the default behavior of LoadProperties--it's lazy loaded by design. If you have to load the properties into the TextGenerator immediately, you can just call LoadProperties(propertyBag, false) and it should change its property values immediately.

On the other hand, if you want to use a CodeDomProvider that supports generics (presumably the one for C# v2.0), all you have to do is make the following calls to the Providers class:

// Note: I don't know what the name of the C# v2 provider is, so
// just replace it with the proper class name
Providers.RegisterExtension("cs", new CSharpCodeProvider2());
Providers.Register("C# v2", new CSharpCodeProvider2());

...
and in your template:

<@CodeTemplate Language="C# v2" > ..and that's all you have to do in order to add support for generics. If you wanted to add other languages (such as C++/CLI), you can just repeat the steps I gave you above, and you'll get the same result. Sorry for the delay in the reply!

p.s. If you want to email me directly, please forward any questions/comments to my other email account at: marttub (at_sign) hotmail_dot_com. thanks!

GeneralBuilding boost libraries for Visual Studio
Unruled Boy
18:49 11 Sep '05  
maybe this helps:

http://www.codeproject.com/useritems/Building_boost_libraries.asp[^]

Regards,
unruledboy@hotmail.com
GeneralDBInspector uses TaHoGen...
WSRG
18:02 24 Aug '05  
I've finally put the final touches on my .9 release of DBInspector. I frequently develop data-centric web applications, and get tired of writing the same code over and over. I first integrated the NVelocity templating engine, but when I saw Codesmith I recognized the power of the templates. Thanks be to Philip for all his hard work on this. My program is also GPL and includes a syntax highlighting editor, a datamodel which you can call from your templates, and a pluggable database/template engine architecture.

One thing I had to do with TaHoGen was to modify one of the files so that I could include my datamodel assembly into its ReferencedAssemblies collection.

Here's the affected code (TemplateCompiler.cs):

public sealed class TemplateCompiler
{
/* added this public static property to expose the ReferencedAssemblies collection */
private static StringCollection _ReferencedAssemblies = new StringCollection();
public static StringCollection ReferencedAssemblies
{
get { return _ReferencedAssemblies;}
set { _ReferencedAssemblies=value;}
}
.....

further down...

CodeDomProvider provider = Providers.GetProvider(language);
/* removed declaration for ReferencedAssemblies and made it a public property */

string tahogenAssemblyLocation = typeof(TextGenerator).Assembly.Location;
ReferencedAssemblies.Add(tahogenAssemblyLocation);

----------

Now, the reason I had to do that was so that I could include an assembly so the compiler could see it during the template compilation. This allows my templates to call methods and properties from my datamodel assembly. This also conveniently allows me to append lots of functionality into the template's namespace such as XML, etc. I thought I could do the same thing by using these tags:

<%@ Assembly Name="DBInspector.DataModel" %>
<%@ Import Namespace="DBInspector.DataModel" %>

However, that does not do what I had suspected...Without the changes I made, I would have to put these assemblies into the GAC because no matter what i tried, the TemplateCompiler could not locate the dll.

I believe the reason the above doesnt work as I expected is because of this code:

// We're looking for the @Assembly Src="..." directive
if (!StringHelper.AreEqual(directive.Name, "Assembly"))
return;

if (!directive.HasAttribute("Src"))
return;

Basically, if there's no src attribute, the @Assembly line is ignored. What it should do is add the named assembly to the ReferencedAssemblies collection for me Smile

I have a TahoProvider.dll assembly which extends an abstract class called ITransformer. This allows me to dynamically load these transformers at run time.

Then, inside my TahoProvider's execute method, I have this block of code:

TemplateCompiler.ReferencedAssemblies.Clear();
string path=Assembly.GetExecutingAssembly().CodeBase;
path=path.Replace("tahoprovider.dll","DBInspector.DataModel.Dll");
path=path.Replace("file:///","");
TemplateCompiler.ReferencedAssemblies.Add(path);

The .Add method adds the path to my dll into TaHo's assemblies list. i've tested it several times, and without the above code, i get an error saying it could not find the assembly. And I dont want to put it into the GAC either.

So, I dont know if my ideas will help any of you or Philip, but there they are. You can visit my website at http://www.websiterepairguys.com for info on my project and download the code once i'm done polishing it.Smile

GeneralRe: DBInspector uses TaHoGen...
Philip Laureano
21:30 25 Aug '05  
WSRG,

Thanks for the input. You certainly contacted me at the right time! Smile I was actually looking for a way to generate an entire DB schema with TaHoGen, but I couldn't figure out a way to do it just yet. Now, since there's no Open-Source equivalent for CodeSmith's SchemaExplorer, how did you manage to reverse-engineer the DB? Did you write a schema exploration tool yourself? If it's stable enough for a 1.0 release, I'd certainly consider it to be included as part of the TaHoGen distribution.

Oh, and about that @Assembly Src="" attribute. From what I remember about that section of code, I wrote that in one section because the @Assembly directive has two situations in which it is used:

1. @Assembly Src="" -- This is to compile a source file into an assembly before including it into the references

2. @Assembly Name="" -- This references an existing assembly and includes it as part of the compilation process.

I'll have to take a look at those two instances again and see what's going on.

I definitely appreciate the feedback, WSRG! Smile In your own experience with TaHoGen, have you run into any problems? (I swore I must have tested that thing to death, but user feedback is the ultimate litmus test, heh)

GeneralRe: DBInspector uses TaHoGen...
WSRG
8:11 26 Aug '05  
Yes, I've been using my schema code for about 2 years now. It works against Microsoft SQL Server, but since I made the architecture "pluggable" I can write more providers as needed. I started a MySQL provider a while back but the core code was kinda hosed at that time.

I really like TaHoGen. NVelocity got me a long way, and i have some cool templates using it, but writing the templates in a native language is better. And theoretically, we can add Python.net or Perl.net Wink in the future.

But, I never could get around that assembly loading problem. I dont know how to explain it better than I did above. I tried several times with/without the hack i made and it only works with the hack.

The Assembly token code should load the named assembly into the referenced assemblies collection if there is no src attribute. IMHO

Keep in touch.
GeneralRe: DBInspector uses TaHoGen...
Philip Laureano
19:59 29 Aug '05  
Would you mind sharing that schema code, Mark? Is it already a part of the DBInspector distribution?
GeneralRe: DBInspector uses TaHoGen...
Mark Williamson
8:28 30 Aug '05  
The schema is included in the source which is located on my website

In a nutshell, I have the following classes in the DBInspector.DataModel assembly:

Table
Field
StoredProcedure
Param (stored procedure parameter)

(a view in SQL server is the same as a table)

The Providers (MssqlProvider) will populate these objects based on the database schema. It is this set of objects that I dub my "schema" and use throughout the templates.

Because I have the base class "BaseProvider" and the structure is all there, I can create a MySQL provider etc at ease. Outside of the provider assembly, none of DBInspector deals with database code, its all done through the DataModel.

Regards,
Mark
Generalhey, wrong section? or pure c#?
Unruled Boy
4:19 5 Aug '05  
it's most in c#, why put it in c++ section? any chance to make it all pure c#?

Regards,
unruledboy@hotmail.com
GeneralRe: hey, wrong section? or pure c#?
Anonymous
17:30 7 Aug '05  
Unruled Boy wrote: it's most in c#, why put it in c++ section? It's the C++ section because the parsing engine is written in C++ (using the Boost Spirit library).


Unruled Boy wrote: any chance to make it all pure c#?
There's no chance of that happening at this point. In order to make the entire project all in C#, you'd have to rewrite the entire parsing engine in C#, and that's quite an enormous task to undertake--it's quite possible, but I certainly don't recommend it.

-Philip Laureano
GeneralRe: hey, wrong section? or pure c#?
Unruled Boy
18:20 6 Sep '05  
how about not InterOp? activex dll -> InterOp seems a little odd.

Regards,
unruledboy@hotmail.com
GeneralRe: hey, wrong section? or pure c#?
Anonymous
22:59 9 Sep '05  
That won't work either. I tried using IJW in managed C++ with the boost spirit libraries, and believe me, it was a nightmare. The best compromise was to build it into an ActiveX (aka COM) dll and use Tlbimp.exe to make it useable in .NET. The only way to go pure C# is to either use regex (which is possible, but its harder to debug *and* effectively cripples many features that TaHoGen has over CodeSmith, such as multiple templates in one assembly), or write your own recursive descent ASP.NET parser in C#. Either way, it's not pretty. So far, I don't see any compelling need to make the entire project in C# (including the parser). It's much easier to just use the existing parser rather than rewrite another one from scratch.

However, if you're willing to write one on your own, I certainly won't stop you. Smile

-Philip Laureano

GeneralGr8 tool and code.....
ab13122k
5:58 23 May '05  
Big Grin

ashish agrawal
GeneralRe: Gr8 tool and code.....
Philip Laureano
21:02 30 May '05  
Thanks. Smile
GeneralDoes this project have a regular site
Benk
20:51 20 May '05  
Hi Phillip,

Let's start with thankyou for what promises to be a really useful tool.

That said, a few questions.

i) Does the project have a regular site (eg Sourceforge or something where we could discuss, colloborate, suggest things).

ii) What's your schedule for upcoming articles.

iii) What are your documentation plans (maybe I could help out ?? Smile

iv) Or have i just missed the update and am cluelessly wandering around while all this stuff exists or is about to be implemented :P,

Cheers and thanks again,

Ben.


Last Updated 14 Mar 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010