using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Collections;
using System.CodeDom;
using SubSonic.CodeGenerator;
using System.Text.RegularExpressions;
using System.IO;
using SubSonic.Utilities;
using System.Collections.Specialized;
namespace SubSonic
{
public static class CodeService
{
#region Helpers
private static string templateDirectory = string.Empty;
public static string TemplateDirectory
{
get { return templateDirectory; }
set { templateDirectory = value; }
}
public enum TemplateType
{
Class,
ODSController,
ReadOnly,
SP,
Structs,
DynamicScaffold,
GeneratedScaffoldCodeBehind,
GeneratedScaffoldMarkup
}
public enum ReplacementVariable
{
Table,
Provider,
View,
StoredProcedure
}
public class Replacement
{
public Replacement(ReplacementVariable variable, string replace)
{
Variable = variable;
ReplaceWith = replace;
}
private ReplacementVariable replaceVar;
public ReplacementVariable Variable
{
get { return replaceVar; }
set { replaceVar = value; }
}
private string replaceWith;
public string ReplaceWith
{
get { return replaceWith; }
set { replaceWith = value; }
}
}
#endregion
/// <summary>
/// Not currently used, but will be the basis for user defined templates. Please don't remove!
/// </summary>
/// <param name="templateFile"></param>
/// <param name="values"></param>
/// <returns></returns>
public static string RunTemplate(string templateFile, NameValueCollection values)
{
string result;
string templatePath = Path.Combine(TemplateDirectory, templateFile);
string templateText = Sugar.Files.GetFileText(templatePath);
for (int i = 0; i < values.Count; i++)
{
templateText = templateText.Replace(values.GetKey(i), values.Get(i));
}
Template t = new Template(templateText);
result = t.Render();
if (String.IsNullOrEmpty(result))
throw new Exception("There is no code generated. Houston, we have a problem! Check to make sure there is a primary key defined. " +
"Also, this could be a naming issue. " +
"Check that your columns do not contain reserved words or punctuation/numbers. Also make sure that no columns have " +
"the same name as their containing table." + Environment.NewLine + t.Error);
//must run AFTER Template.Render()
result = ScrubOutput(result);
if (String.IsNullOrEmpty(result))
{
throw new Exception(templateFile + ": " + t.Error);
}
return result;
}
public static string RunTemplate(TemplateType templateType, NameValueCollection values, ICodeLanguage language)
{
string result;
string templateText = GetTemplateText(templateType, language);
for (int i = 0; i < values.Count; i++)
{
templateText = templateText.Replace(values.GetKey(i), values.Get(i));
}
Template t = new Template(templateText);
t.Language = language;
Utility.WriteTrace("Rendering template");
result = t.Render();
if (!String.IsNullOrEmpty(result))
{
Utility.WriteTrace("Fixing output...");
result = ScrubOutput(result);
Utility.WriteTrace("Finished :)");
}
return result;
}
static string RunTemplate(TemplateType templateType, List<Replacement> settings, ICodeLanguage language)
{
Utility.WriteTrace("Getting Template");
string templateText = GetTemplateText(templateType, language);
//set the provider and tablename
Utility.WriteTrace("Replacing values in template");
foreach (Replacement var in settings)
{
string replaceHolder = "#" + Enum.GetName(typeof(ReplacementVariable), var.Variable).ToUpper() + "#";
templateText = Utility.FastReplace(templateText, replaceHolder, var.ReplaceWith, StringComparison.InvariantCultureIgnoreCase);
//templateText = templateText.Replace(replaceHolder, var.ReplaceWith);
}
string result;
Template t = new Template(templateText);
t.Language = language;
Utility.WriteTrace("Rendering template");
result = t.Render();
if (!String.IsNullOrEmpty(result))
{
Utility.WriteTrace("Fixing output...");
result = ScrubOutput(result);
Utility.WriteTrace("Finished :)");
}
return result;
}
public static bool ShouldGenerate(string objectName, string[] includeList, string[] excludeList, DataProvider provider)
{
bool result = true;
bool generateAll = false;
//first, check to see if the includeList says to include all tables
//this is a default
if (includeList.Length == 1)
{
if (includeList[0] == "*")
{
generateAll = true;
}
}
//if we need to generate all tables, then we need to check the excludeList
if (generateAll)
{
foreach (string s in excludeList)
{
if (Utility.IsRegexMatch(objectName, s.Trim()))
{
result = false;
break;
}
}
}
else
{
//IncludeList TRUMPs excludeList in case of confusion
//what this means is that if there is an includeList,
//be definition there's an excludeList of all tables not included
//yep, confusing.
//this means that tables were specifically requested in the includeList
//need to make them prove themselves
result = false;
foreach (string s in includeList)
{
if (Utility.IsRegexMatch(objectName, s.Trim()))
{
result = true;
break;
}
}
}
return result;
}
public static bool ShouldGenerate(TableSchema.Table tbl)
{
return ShouldGenerate(tbl.TableName, tbl.Provider.Name);
}
public static bool ShouldGenerate(string tableName, string providerName)
{
DataProvider provider = DataService.Providers[providerName];
if (provider == null)
throw new Exception("There is no provider with the name " + providerName);
return ShouldGenerate(tableName, provider.IncludeTables, provider.ExcludeTables, provider);
}
public static string RunODS(string tableName, string providerName, ICodeLanguage language)
{
//make sure the providers are loaded
DataService.LoadProviders();
string result = string.Empty;
if (ShouldGenerate(tableName, providerName) && DataService.Providers[providerName].GenerateODSControllers)
{
Utility.WriteTrace("####### Creating ODS Controller for " + tableName + " ####### ");
List<Replacement> list = new List<Replacement>();
list.Add(new Replacement(ReplacementVariable.Table, tableName));
list.Add(new Replacement(ReplacementVariable.Provider, providerName));
result = RunTemplate(TemplateType.ODSController, list, language);
}
else
{
Utility.WriteTrace(tableName + " is Excluded from generation");
}
return result;
}
public static string RunClass(string tableName, string providerName, ICodeLanguage language)
{
//make sure the providers are loaded
DataService.LoadProviders();
string result = string.Empty;
if (ShouldGenerate(tableName, providerName))
{
Utility.WriteTrace("####### Creating class for " + tableName + " ####### ");
List<Replacement> list = new List<Replacement>();
list.Add(new Replacement(ReplacementVariable.Table, tableName));
list.Add(new Replacement(ReplacementVariable.Provider, providerName));
result = RunTemplate(TemplateType.Class, list, language);
}
else
{
Utility.WriteTrace(tableName + " is Excluded from generation");
}
return result;
}
public static string RunReadOnly(string viewName, string providerName, ICodeLanguage language)
{
//make sure the providers are loaded
DataService.LoadProviders();
string result = string.Empty;
if (ShouldGenerate(viewName, providerName))
{
Utility.WriteTrace("####### Creating ReadOnly class for View " + viewName + " ####### ");
List<Replacement> list = new List<Replacement>();
list.Add(new Replacement(ReplacementVariable.View, viewName));
list.Add(new Replacement(ReplacementVariable.Provider, providerName));
result = RunTemplate(TemplateType.ReadOnly, list, language);
}
else
{
Utility.WriteTrace(viewName + " is Excluded from generation");
}
return result;
}
public static string RunSPs(string providerName, ICodeLanguage language)
{
//make sure the providers are loaded
DataService.LoadProviders();
Utility.WriteTrace("####### Creating SP class ####### ");
List<Replacement> list = new List<Replacement>();
list.Add(new Replacement(ReplacementVariable.Provider, providerName));
return RunTemplate(TemplateType.SP, list, language);
}
public static string RunStructs(ICodeLanguage language)
{
//make sure the providers are loaded
DataService.LoadProviders();
Utility.WriteTrace("####### Creating Structs ####### ");
List<Replacement> list = new List<Replacement>();
return RunTemplate(TemplateType.Structs, list, language);
}
static string ScrubOutput(string sIn)
{
string result = sIn;
if (!String.IsNullOrEmpty(result))
{
//the generator has an issue with adding extra lines. Trim them out
Regex reg = new Regex(@"[\r\n]+");
result = reg.Replace(result, "\r\n");
//now, for readability, add a space after the end of the method/class
result = Utility.FastReplace(result, "}", "}\r\n", StringComparison.InvariantCultureIgnoreCase);
result = Utility.FastReplace(result, "namespace", "\r\nnamespace", StringComparison.InvariantCulture); //Must be case-sensitive, or it will cause VB End Namespace to wrap
//these must remain as two separate procedures, or bad things will happen in VB
result = Utility.FastReplace(result, "public class ", "\r\npublic class ", StringComparison.InvariantCulture); //trailing space need to address class names that begin with "class"
result = Utility.FastReplace(result, "Public Class ", "\r\nPublic Class ", StringComparison.InvariantCulture); //trailing space need to address class names that begin with "Class"
result = Utility.FastReplace(result, "[<]", "<", StringComparison.InvariantCultureIgnoreCase);
result = Utility.FastReplace(result, "[>]", ">", StringComparison.InvariantCultureIgnoreCase);
//This is should be executed last. While this value will ultimately be removed, it can be inserted in a template to keep an earlier operation from executing.
//For example: <System.ComponentModel.DataObject()> Public Class MyController would normally wrap to a second line due upstream processing, which would
//result in VB code that won't compile. However, <System.ComponentModel.DataObject()> Public [MONKEY_WRENCH]Class MyController, would not.
//Nice Eric... :P
result = Utility.FastReplace(result, "[MONKEY_WRENCH]", String.Empty, StringComparison.InvariantCultureIgnoreCase);
return result;
}
else
{
throw new Exception("There is no code to scrub: " + sIn);
}
}
public static CodeCompileUnit BuildCompilUnit(string code)
{
return new CodeSnippetCompileUnit(code);
}
static string GetTemplateText(TemplateType t, ICodeLanguage language)
{
string template = language.TemplatePrefix;
string templateText = null;
switch (t)
{
case TemplateType.Class:
template += TemplateName.CLASS;
break;
case TemplateType.ReadOnly:
template += TemplateName.VIEW;
break;
case TemplateType.SP:
template += TemplateName.STORED_PROCEDURE;
break;
case TemplateType.Structs:
template += TemplateName.STRUCTS;
break;
case TemplateType.ODSController:
template += TemplateName.ODS_CONTROLLER;
break;
case TemplateType.DynamicScaffold:
template += TemplateName.DYNAMIC_SCAFFOLD;
break;
case TemplateType.GeneratedScaffoldCodeBehind:
template += TemplateName.GENERATED_SCAFFOLD_CODE_BEHIND;
break;
case TemplateType.GeneratedScaffoldMarkup:
template += TemplateName.GENERATED_SCAFFOLD_MARKUP;
break;
default:
template += TemplateName.CLASS;
break;
}
template += FileExtension.DOT_ASPX;
//decide where to pull the text from
if (!String.IsNullOrEmpty(templateDirectory))
{
Utility.WriteTrace("Looking for template " + template + " in " + templateDirectory);
//make sure the template exists
string templatePath = Path.Combine(templateDirectory, template);
if (File.Exists(templatePath))
{
//pull the text from there
templateText = Sugar.Files.GetFileText(templatePath);
}
else
{
Utility.WriteTrace("Template " + template + " NOT FOUND in directory " + templateDirectory + "; using embedded resource template instead...");
}
}
if (String.IsNullOrEmpty(templateText))
{
Utility.WriteTrace("Loading template from Resource for " + template);
templateText = Template.LoadTextFromManifest(template);
}
if (String.IsNullOrEmpty(templateText))
throw new Exception("The template \"" + template + "\" is empty or cannot be found.");
return templateText;
}
}
}