Click here to Skip to main content
15,884,298 members
Articles / Programming Languages / Visual Basic

Template Based Code Generator

Rate me:
Please Sign up or sign in to vote.
4.67/5 (33 votes)
20 Nov 2007CPOL13 min read 93.2K   4.1K   116  
A template based, command-line oriented .NET code generator
using System;
using System.Collections.Generic;
using System.Text;
using Arebis.CodeGeneration;
using System.Collections.Specialized;
using System.Reflection;
using System.IO;
using System.Globalization;

namespace Arebis.CodeGenerator.Templated
{
	[System.Diagnostics.DebuggerStepThrough]
	public class GenerationHost : MarshalByRefObject, IGenerationHost, IDisposable
	{
		private Dictionary<string, TemplateInfo> templates;
		private List<IFileWriter> fileWriters;
		private NameValueCollection settings;
		private List<string> referencepath;

		private Stack<string> contextDirectory;
		private Stack<TextWriter> contextWriter;
		private string outputDirectory;

		private string logfile;

		public GenerationHost()
		{
			// Install assembly resolver:
			AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.AssemblyResolve);
		}

		public void Initialize(NameValueCollection settings)
		{
			// Store settings:
			this.settings = settings;

			// Setup generation language settings:
			GenerationLanguage.DefaultNameSpace = "Arebis.DynamicAssembly";
			GenerationLanguage.DefaultBaseClass = "Arebis.CodeGeneration.CodeTemplate";
			GenerationLanguage.CodeBuilders["vb"] = typeof(VBCodeBuilder);
			GenerationLanguage.CodeBuilders["c#"] = typeof(CSCodeBuilder);
			GenerationLanguage.DefaultTemplateLanguage = "c#";

			// Default to T3 syntax:
			new Arebis.CodeGenerator.Templated.Syntax.T3Syntax().Setup(this.settings);

			// Run GenerationSetup if any:
			foreach (string generationSetupTypeName in settings.GetValues("generatorsetup") ?? new string[0])
			{
				try
				{
					((IGenerationSetup)Activator.CreateInstance(Type.GetType(generationSetupTypeName)))
					.Setup(this.settings);
				}
				catch (Exception ex)
				{
					throw new ApplicationException(String.Format("Failed to setup generator by '{0}': {1}", generationSetupTypeName, ex.Message));
				}
			}

			// Build base referencepath:
			this.referencepath = new List<string>();
			foreach (string item in this.Settings.GetValues("referencepath") ?? new string[0])
				this.referencepath.Add(item);

			// Build initial context directory:
			this.contextDirectory = new Stack<string>();
			this.contextDirectory.Push(Path.Combine(Environment.CurrentDirectory, (this.Settings["sourcedir"] ?? ".")));

			// Build initial context writer:
			this.contextWriter = new Stack<TextWriter>();
			this.contextWriter.Push(new StringWriter());

			// Set output directory:
			this.outputDirectory = Path.Combine(Environment.CurrentDirectory, (this.Settings["targetdir"] ?? "."));

			// Initialize compiled templates:
			this.templates = new Dictionary<string, TemplateInfo>();

			// Initialize filewriters:
			this.fileWriters = new List<IFileWriter>();
			foreach(string writer in settings.GetValues("filewriter") ?? new string[0])
			{
				try
				{
					Type writerType = Type.GetType(writer);
					IFileWriter instance = (IFileWriter)Activator.CreateInstance(writerType);
					this.fileWriters.Add(instance);
				}
				catch (ArgumentNullException)
				{
					throw new ApplicationException(String.Format("Failed to instantiate the filewriter \"{0}\". Check classname and referencepath.", writer));
				}
			}

			// Add default filewriter:
			this.fileWriters.Add(new DefaultFileWriter());

			// Link & initialize file writers:
			for (int i = 0; i < this.fileWriters.Count; i++)
			{
				// Set host:
				this.fileWriters[i].Host = this;
				// Connect to previous (if any):
				if (i > 0)
				{
					this.fileWriters[i - 1].NextWriter = this.fileWriters[i];
				}
			}

			// Initialize logfile:
			if (settings["logfile"] != null)
			{
				this.logfile = Path.Combine(Environment.CurrentDirectory, settings["logfile"]);
			}
		}

		/// <summary>
		/// Returns a writable copy of the base reference path.
		/// </summary>
		public List<string> ReferencePath
		{
			get { return new List<string>(this.referencepath); }
		}

		public void CallTemplate(string templatefile, params object[] parameters)
		{
			// Get full template filename:
			string fulltemplatefile = Path.Combine(this.contextDirectory.Peek(), templatefile);

			// Call template:
			this.CallTemplateToContext(fulltemplatefile, parameters);
		}

		public void CallTemplateToFile(string templatefile, string outputfile, params object[] parameters)
		{
			// Get full template filename:
			string fulltemplatefile = Path.Combine(this.contextDirectory.Peek(), templatefile);

			try
			{
				// Push new writer as contexts writer:
				this.contextWriter.Push(new StringWriter());

				this.CallTemplateToContext(fulltemplatefile, parameters);
			}
			finally
			{
				// Restore previous writer context:
				TextWriter writer = this.contextWriter.Pop();

				// Write the file:
				if (outputfile != null)
					this.WriteFile(Path.Combine(this.outputDirectory, outputfile), writer.ToString());
			}
		}

		private void CallTemplateToContext(string fulltemplatefile, params object[] parameters)
		{
			try
			{
				// Push template's directory as current directory context:
				this.contextDirectory.Push(new FileInfo(fulltemplatefile).Directory.FullName);

				// Create & cache template:
				if (this.templates.ContainsKey(fulltemplatefile) == false)
				{
					this.templates[fulltemplatefile] = new TemplateInfo(fulltemplatefile, this);
				}

				// Invoke template:
				this.templates[fulltemplatefile].Invoke(parameters);
			}
			finally
			{
				// Restore previous directory context:
				this.contextDirectory.Pop();
			}
		}

		public virtual void WriteFile(string filename, string content)
		{
			this.fileWriters[0].WriteFile(filename, content);
		}

		public NameValueCollection Settings
		{
			get { return this.settings; }
		}

		public void WriteOutput(string str)
		{
			this.contextWriter.Peek().Write(str);
		}

		public string NewLineString
		{
			get { return Environment.NewLine; }
		}

		public void Log(string fmt, params object[] args)
		{
			string timestamp = DateTime.Now.ToString();
			try
			{
				if (logfile == null)
					Console.WriteLine(String.Format(timestamp + " " + fmt, args));
				else
				{
					StreamWriter writer = File.AppendText(this.logfile);
					try
					{
						writer.WriteLine(String.Format(timestamp + " " + fmt, args));
					}
					finally
					{
						writer.Close();
					}
				}
			}
			catch (Exception ex)
			{
				try
				{
					Console.WriteLine(timestamp + " Attempt to log message \"" + fmt + "\" failed: " + ex.Message);
				}
				catch (Exception) { }
			}
		}

		public void Dispose()
		{
			// Uninstall assembly resolver:
			AppDomain.CurrentDomain.AssemblyResolve -=new ResolveEventHandler(this.AssemblyResolve);

			// Dispose templates:
			if (this.templates != null)
			{
				foreach (TemplateInfo item in this.templates.Values)
				{
					item.Dispose();
				}
				this.templates = null;
			}
		}

		/// <summary>
		/// Attempts to resolve assemblies.
		/// </summary>
		Assembly AssemblyResolve(object sender, ResolveEventArgs args)
		{
			if (this.referencepath != null)
			{
				string filename = args.Name.Split(',')[0];
				foreach (string path in this.referencepath)
				{
					if (File.Exists(Path.Combine(path, filename + ".dll")))
					{
						return Assembly.LoadFile(Path.Combine(path, filename + ".dll"));
					}
					else if (File.Exists(Path.Combine(path, filename + ".exe")))
					{
						return Assembly.LoadFile(Path.Combine(path, filename + ".exe"));
					}
				}
			}
			return null;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect AREBIS
Belgium Belgium
Senior Software Architect and independent consultant.

Comments and Discussions