///////////////////////////////////////////////////////
// Code author: Martin Lapierre, http://devinstinct.com
///////////////////////////////////////////////////////
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using SubSonic.Utilities;
namespace SubSonic
{
/// <summary>
/// Provides the features required for code generation.
/// </summary>
public interface ICompileUnitGenerator
{
#region Methods
/// <summary>
/// Generates the DAL in a single CodeCompileUnit from an input file.
/// </summary>
/// <param name="languageType">The language to generate the DAL for.</param>
/// <param name="inputFile">The path to the input file.</param>
/// <returns>A single CodeCompileUnit.</returns>
CodeCompileUnit GenerateSingleUnit(LanguageType languageType, string inputFile);
/// <summary>
/// Generates the DAL in multiple CodeCompileUnits from an input file.
/// </summary>
/// <param name="languageType">The language to generate the DAL for.</param>
/// <param name="inputFile">The path to the input file.</param>
/// <returns>A dictionary of suggested filenames/CodeCompileUnit pairs.</returns>
IDictionary<string, CodeCompileUnit> GenerateMultipleUnits(LanguageType languageType, string inputFile);
/// <summary>
/// Generates the DAL in multiple CodeCompileUnits for specific tables, views
/// and, optionnaly, all stored procedures.
/// </summary>
/// <param name="languageType">The language to generate the DAL for.</param>
/// <param name="tables">
/// The list of tables to generate the DAL for; first element as "*" for all tables.
/// Can be null or empty. A null value generates no table and no table struct.
/// <param name="views">
/// The list of views to generate the DAL for; first element as "*" for all views.
/// Can be null or empty. A null value generates no view and no view struct.
/// </param>
/// <param name="useSPs">
/// true to generate the Stored Procedure; false not to generate.
/// A null value generates Stored Procedures based on the configuration file option.
/// </param>
/// <param name="providerName">
/// The name of the provider to generate the DAL for.
/// If null, generates code for all the providers.
/// </param>
/// <returns>A dictionary of suggested filenames/CodeCompileUnit pairs.</returns>
IDictionary<string, CodeCompileUnit> GenerateMultipleUnits(LanguageType languageType, string[] tables, string[] views, bool? useSPs, string providerName);
/// <summary>
/// Gets the list of tables.
/// </summary>
/// <param name="providerName">
/// The name of the provider to get the tables for.
/// If null, uses the default provider.
/// </param>
/// <returns>A list of table names.</returns>
string[] GetTableNames(string providerName);
/// <summary>
/// Gets the list of views.
/// </summary>
/// <param name="providerName">
/// The name of the provider to get the views for.
/// If null, uses the default provider.
/// </param>
/// <returns>A list of view names.</returns>
string[] GetViewNames(string providerName);
/// <summary>
/// Gets the list of provider names for the current application context.
/// </summary>
/// <returns>A list of provider names.</returns>
string[] GetProviderNames();
#endregion Methods
}
/// <summary>
/// Hosts the code generation process for any environment.
/// </summary>
/// <remarks>
/// This host allows multiple SubSonic code generators
/// to run simultaneously even if the SubSonic core classes are static.
/// </remarks>
public class CompileUnitGeneratorHost //: ICompileUnitGenerator
{
#region Fields
/// <summary>
/// The type of the internal generator.
/// </summary>
/// <remarks>
/// More reliable than using a hard-coded string.
/// </remarks>
private static Type _generatorType = typeof(CompileUnitGenerator);
/// <summary>
/// The hosted compile unit generator.
/// </summary>
private ICompileUnitGenerator _generator = null;
#endregion Fields
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
/// <param name="path">The path to the project to create the host for.</param>
public CompileUnitGeneratorHost(string path)
{
_generator = CreateGenerator(path);
}
#endregion Constructors
#region Properties
/// <summary>
/// Gets the hosted compile unit generator.
/// </summary>
public ICompileUnitGenerator Generator
{
get { return _generator; }
}
#endregion Properties
#region Methods
/// <summary>
/// Creates a CompileUnitGenerator in its own domain.
/// </summary>
/// <remarks>
/// Assigns the configuration file with the highest priority to the newly created AppDomain.
/// </remarks>
/// <param name="path">The path to the project to create the generator for.</param>
/// <returns>The CompileUnitGenerator.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] // Make sure the method is not inlined by the compiler.
protected static ICompileUnitGenerator CreateGenerator(string path)
{
AppDomainSetup setup = new AppDomainSetup();
string[] configFiles = ConfigurationProvider.FindProjectConfigFiles(path);
setup.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
setup.ConfigurationFile = configFiles[0]; // Make System.Configuration.ConfigurationManager use this file in the new AppDomain.
AppDomain domain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, setup);
return domain.CreateInstanceAndUnwrap(_generatorType.Assembly.FullName, _generatorType.FullName, false, BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public, null, new object[] { configFiles }, null, null, null) as ICompileUnitGenerator;
}
#endregion Methods
#region Classes
/// <summary>
/// Configuration-wise code generator.
/// </summary>
/// <remarks>An instance of this class is not thread safe.</remarks>
private class CompileUnitGenerator : MarshalByRefObject, ICompileUnitGenerator
{
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
/// <param name="configFiles">The configuration files to use as override.</param>
public CompileUnitGenerator(string[] configFiles)
{
// Use the config files as overrides of the hosting process.
ConfigurationProvider.CurrentInstance.ConfigFileOverrides = configFiles;
// Hack to support relative path for code templates.
// Only support current directory relativity (starting with "." or ".\").
DataService.LoadProviders(); // This is where SubSonic init its configuration... load it now.
if (!string.IsNullOrEmpty(SubSonicConfig.TemplateDirectory) && (SubSonicConfig.TemplateDirectory == "." || SubSonicConfig.TemplateDirectory.StartsWith(@".\")))
{
string directory = Path.GetDirectoryName(ConfigurationProvider.CurrentInstance.ConfigFileOverrides[0]);
if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString()))
directory += Path.DirectorySeparatorChar;
if (SubSonicConfig.TemplateDirectory == ".")
SubSonicConfig.TemplateDirectory = directory;
else
SubSonicConfig.TemplateDirectory = directory + SubSonicConfig.TemplateDirectory.Substring(2);
}
}
#endregion Constructors
#region Methods
/// <summary>
/// Generates the DAL in a single CodeCompileUnit from an input file.
/// </summary>
/// <param name="languageType">The language to generate the DAL for.</param>
/// <param name="inputFile">The path to the input file.</param>
/// <returns>A single CodeCompileUnit.</returns>
public CodeCompileUnit GenerateSingleUnit(LanguageType languageType, string inputFile)
{
BuildProvider builder = new BuildProvider();
string tableText = Utility.GetFileText(inputFile);
return builder.GenerateSingleUnit(languageType, tableText);
}
/// <summary>
/// Generates the DAL in multiple CodeCompileUnits for specific tables, views
/// and, optionnaly, all stored procedures.
/// </summary>
/// <param name="languageType">The language to generate the DAL for.</param>
/// <param name="tables">
/// The list of tables to generate the DAL for; first element as "*" for all tables.
/// Can be null or empty. A null value generates no table and no table struct.
/// <param name="views">
/// The list of views to generate the DAL for; first element as "*" for all views.
/// Can be null or empty. A null value generates no view and no view struct.
/// </param>
/// <param name="useSPs">
/// true to generate the Stored Procedure; false not to generate.
/// A null value generates Stored Procedures based on the configuration file option.
/// </param>
/// <param name="providerName">
/// The name of the provider to generate the DAL for.
/// If null, generates code for all the providers.
/// </param>
/// <returns>A dictionary of suggested filenames/CodeCompileUnit pairs.</returns>
public IDictionary<string, CodeCompileUnit> GenerateMultipleUnits(LanguageType languageType, string[] tables, string[] views, bool? useSPs, string providerName)
{
BuildProvider builder = new BuildProvider();
return builder.GenerateMultipleUnits(languageType, tables, views, useSPs, providerName);
}
/// <summary>
/// Generates the DAL in multiple CodeCompileUnits from an input file.
/// </summary>
/// <param name="languageType">The language to generate the DAL for.</param>
/// <param name="inputFile">The path to the input file.</param>
/// <returns>A dictionary of suggested filenames/CodeCompileUnit pairs.</returns>
public IDictionary<string, CodeCompileUnit> GenerateMultipleUnits(LanguageType languageType, string inputFile)
{
string tableText = Utility.GetFileText(inputFile);
string[] tables = tableText.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
string[] views = new string[] { "*" };
// Use input file for tables; always generate views; generate SP based on config file.
return GenerateMultipleUnits(languageType, tables, views, null, null);
}
/// <summary>
/// Gets the list of tables.
/// </summary>
/// <param name="providerName">
/// The name of the provider to get the tables for.
/// If null, uses the default provider.
/// </param>
/// <returns>A list of table names.</returns>
public string[] GetTableNames(string providerName)
{
//if (String.IsNullOrEmpty(providerName))
// return DataService.GetTableNames(providerName);
//else
return DataService.GetTableNames(providerName);
}
/// <summary>
/// Gets the list of views.
/// </summary>
/// <param name="providerName">
/// The name of the provider to get the views for.
/// If null, uses the default provider.
/// </param>
/// <returns>A list of view names.</returns>
public string[] GetViewNames(string providerName)
{
//if (String.IsNullOrEmpty(providerName))
// return DataService.GetViewNames();
//else
return DataService.GetViewNames(providerName);
}
/// <summary>
/// Gets the list of provider names for the current application context.
/// </summary>
/// <returns>A list of provider names.</returns>
public string[] GetProviderNames()
{
List<string> providerNames = new List<string>();
foreach(DataProvider provider in DataService.Providers)
providerNames.Add(provider.Name);
return providerNames.ToArray();
}
#endregion Methods
}
#endregion Classes
}
}