<#
/*
http://i...content-available-to-author-only...t.com
use envDte to retrieve interface info, create an class for that interface
*/
#>
<#@ template language="C#" debug="true" hostspecific="true" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="Microsoft.CSharp" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
<# // To debug, uncomment the next two lines !!
//System.Diagnostics.Debugger.Launch();
//System.Diagnostics.Debugger.Break();
#>
<#PrepareDataToRender(this); #>
<#var manager = Manager.Create(Host, GenerationEnvironment); #>
<#manager.StartHeader(); #>// <auto-generated />
// This file was generated by a T4 template.
// Don't change it directly as your change would get overwritten. Instead, make changes
// to the .tt file (i.e. the T4 template) and save it to regenerate this file.
// Make sure the compiler doesn't complain about missing Xml comments
#pragma warning disable 1591
using System;
<# if(GenerateMapper) { #>
using System.Collections.Generic;
<# } #>
<# manager.EndBlock();#>
<#
var rootNamespace = Project.Properties.Item("RootNamespace").Value.ToString();
#>
//Project:<#=Project.Name#>
//rootNamespace=<#= rootNamespace #>
<# RecurseProjectItems(manager,Project.ProjectItems);
if(string.IsNullOrEmpty(CurrentNamespaceGenerating)==false)
WriteLine("} //end namespace2");
#>
<#manager.Process(SplitIntoMultipleFiles);#>
<#@ Include File="DTOGenerator.tt.settings.t4" #>
<#@ Include File="GenerateDictionaryMapper.tt" #>
<#+
string CurrentNamespaceGenerating=null;
void RecurseProjectItems(Manager manager,ProjectItems container)
{
foreach(var item in container.Cast<ProjectItem>())
{
if(item.FileCodeModel==null)
{
if(IncludeDiagnostics)WriteLine("//opening container:"+item.Name);
RecurseProjectItems(manager,item.ProjectItems);
continue;
}
DateTime lastWriteTime=File.GetLastWriteTime(item.get_FileNames(0));
if(IncludeDiagnostics)WriteLine("//Found CodeModel:"+item.FileCodeModel.GetType().FullName);
var interfaces=RecurseForInterfaces(manager,item.FileCodeModel.CodeElements,string.Empty)
.OrderBy(ri=>ri.Item2)
.ThenByDescending(ri=>ri.Item1.Name).ToArray();
if(interfaces.Length>1 && interfaces[0].Item1.Name==interfaces[1].Item1.Name+"DTO")
interfaces=interfaces.Except(new[]{interfaces[1]}).ToArray();
foreach(var i in interfaces)
{
if(IncludeDiagnostics)
WriteLine("//Found Interface:"+i.Item1.Name);
var properties=RecurseForProperties(i.Item1);
if(properties.Count()>0)
WriteInterface(manager,i.Item1,properties,i.Item2);
}
}
}
IEnumerable<Tuple<string,Action<bool>>> RecurseForProperties(CodeInterface2 codeInterface)
{
var names=new HashSet<string>();
if(codeInterface.Bases !=null)
foreach(var baseInterface in codeInterface.Bases.Cast<CodeElement>())
{
if(IncludeDiagnostics) WriteLine("//found base:"+baseInterface.Name);
foreach(var item in RecurseForProperties(((CodeInterface2)baseInterface)))
{
if(names.Contains(item.Item1)==false)
{
names.Add(item.Item1);
yield return item;
}
}
}
foreach( CodeProperty item in codeInterface.Members.OfType<CodeProperty>())
{
if(names.Contains(item.Name)==false)
{
names.Add(item.Name);
yield return Tuple.Create<string,Action<bool>>(item.Name,asInterfaceSetter=> WriteMapping(item,asInterfaceSetter));
}
}
}
IEnumerable<Tuple<CodeInterface2,string>> RecurseForInterfaces(Manager manager, CodeElements codeElements,string currentNamespace )
{
var q=codeElements.Cast<CodeElement>( )
//.Where(x =>x is CodeInterface||x is CodeClass || x is CodeNamespace) //&& ((CodeInterface)x).Attributes.Count>0)
//.Where(x => x.Name.StartsWith("System")==false)
//.Where(x => x.Name.StartsWith("Infragistics")==false)
//.Where(x => x.Name.StartsWith("Microsoft")==false)
//.Where(x => x.Name.StartsWith("ICSharpCode")==false)
;
var interfaces=new List<string>();
foreach (CodeElement element in q)
{
if (element is CodeInterface && interfaces.Contains(element.Name)==false)
{
var c = (CodeInterface2)element;
//verify element is a project element not an external
if (c.InfoLocation!=vsCMInfoLocation.vsCMInfoLocationProject)
continue;
yield return Tuple.Create<CodeInterface2,string>(c,currentNamespace);
}
if (element is CodeNamespace)
{
var ns=(CodeNamespace)element;
if(SkippedNamespaces.Contains( ns.Name)==false)
foreach(var find in RecurseForInterfaces(manager,ns.Members,ns.Name))
yield return find;
}
}
}
void WriteMapping(CodeProperty codeProperty,bool asInterfaceSetter)
{
WriteLine("");
WriteLine("///<summary>");
WriteLine("///"+codeProperty.FullName);
WriteLine("///</summary>");
if(codeProperty.Getter==null && codeProperty.Setter==null)
return;
if(codeProperty.Attributes!=null){
foreach(CodeAttribute a in codeProperty.Attributes)
{
Write("["+a.FullName);
if(a.Children!=null && a.Children.Count>0)
{
var start=a.Children.Cast<CodeElement>().First().GetStartPoint();
var finish= a.GetEndPoint();
string wholeAttributeLine=start.CreateEditPoint().GetText(finish);
Write("("+wholeAttributeLine);
}
WriteLine("]");
}
}
if(asInterfaceSetter==false){
Write("public ");
} else {
Write("new ");
}
Write(GetFullName(codeProperty.Type) +" "+codeProperty.Prototype);
Write(" {");
if(asInterfaceSetter==false)
Write("get;");
Write("set;");
WriteLine("}");
}
string GetFullName(CodeTypeRef codeType)
{
string fullName;
if (codeType.TypeKind == vsCMTypeRef.vsCMTypeRefArray)
{
CodeTypeRef arrayType = codeType.ElementType;
fullName = arrayType.AsFullName + "[]";
}
else
{
fullName = codeType.AsFullName;
}
return fullName;
}
void WriteInterface(Manager manager,CodeInterface2 codeInterface, IEnumerable<Tuple<string,Action<bool>>> writeProperties,string parentNamespace)
{
if(IncludeDiagnostics)WriteLine("//CodeInterface parentNamespace:"+parentNamespace);
var xmlBlock="\t//CodeInterface";
var start=codeInterface.GetStartPoint();
var finish=codeInterface.Children!=null && codeInterface.Children.Count>0? codeInterface.Children.Cast<CodeElement>().First().GetStartPoint() : codeInterface.GetEndPoint();//.GetEndPoint();
string wholeInterfaceLine=start.CreateEditPoint().GetText(finish);
var classLine=wholeInterfaceLine.ToString();
var justProto=wholeInterfaceLine.Contains("{")?classLine.Substring(0,classLine.IndexOf("{")):classLine;
if(IncludeDiagnostics && justProto.Length<100 && justProto.Contains("*/")==false ){WriteLine("/* justProto: "+justProto); WriteLine(" */");}
manager.EndBlock();
var fullName=codeInterface.FullName;
var name=fullName.Contains(".")?fullName.Substring(fullName.LastIndexOf('.')+1):fullName;
if(name.StartsWith("I")) name=name.Substring(1);
if(name.EndsWith("DTO")==false)
name=name+"DTO";
manager.StartNewFile(name+".generated.cs");
if(IncludeDiagnostics) WriteLine("//fullName:"+fullName);
if(IncludeDiagnostics)WriteLine("//name:"+name);
var targetNamespace=GetTargetNamespace(codeInterface.Namespace.Name);
if(codeInterface!=null && codeInterface.Namespace!=null && (targetNamespace != CurrentNamespaceGenerating|| SplitIntoMultipleFiles)){
if(string.IsNullOrEmpty(CurrentNamespaceGenerating)==false && SplitIntoMultipleFiles==false)
WriteLine("} //namespace end1");
CurrentNamespaceGenerating=GetTargetNamespace(codeInterface.Namespace.Name);
if(IncludeDiagnostics)WriteLine("//end namespace:"+CurrentNamespaceGenerating+":begin:"+codeInterface.Namespace.Name);
#>
namespace <#=codeInterface.Namespace.Name#> {<#+ }
if(GenerateWriteInterfaces && codeInterface.Name.EndsWith("DTO")==false){ #>
public interface I<#=name#>:<#=codeInterface.Name#>{
<#+ foreach(var item in writeProperties) {
item.Item2(true); } #>
}
<#+ } #>
<#+ if(codeInterface.FullName.EndsWith("DTO")){ return;}#>
///<seealso cref="<#=codeInterface.FullName#>"/>
public partial class <#=name#>:<#=codeInterface.Name #><#=GenerateWriteInterfaces && codeInterface.Name.EndsWith("DTO")==false?"DTO":string.Empty#>{
<#+
PushIndent("\t");
PushIndent("\t");
foreach(var item in writeProperties)
item.Item2(false);
PopIndent();
PopIndent();
if(SplitIntoMultipleFiles)
WriteLine("}");
if(GenerateMapper ){
var dictionary=new Dictionary<string,bool>();
writeProperties.ToList().ForEach(wp=>dictionary.Add(wp.Item1,false));
//WriteLine("#pragma warning disable 0108");
GenerateDictionaryMapper(codeInterface.Name,dictionary
, CopyDictionaryNamespace,false);
//WriteLine("#pragma warning restore 0108");
}
#>
} //end class <#=name #>
<#+
}
string ConvertFullName(CodeModel cm, string fullName)
{
// Convert a .NET type name into a C++ type name.
if ((cm.Language == CodeModelLanguageConstants.vsCMLanguageVC) ||
(cm.Language == CodeModelLanguageConstants.vsCMLanguageMC))
return fullName.Replace(".", "::");
else
return fullName;
}
#>
<#+
static DTE Dte;
static Project Project;
static TextTransformation TT;
static string T4FileName;
static string T4Folder;
static string AppRoot;
static string GeneratedCode = @"GeneratedCode(""InterfaceT4"", ""2.0"")";
static Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
void PrepareDataToRender(TextTransformation tt) {
TT = tt;
T4FileName = Path.GetFileName(Host.TemplateFile);
T4Folder = Path.GetDirectoryName(Host.TemplateFile);
// Get the DTE service from the host
var serviceProvider = Host as IServiceProvider;
if (serviceProvider != null) {
Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
}
// Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
if (Dte == null) {
throw new Exception("InterfaceT4 can only execute through the Visual Studio host");
}
Project = GetProjectContainingT4File(Dte);
if (Project == null) {
Error("Could not find the VS Project containing the T4 file.");
return;
}
// Get the path of the root folder of the app
AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
}
Project GetProjectContainingT4File(DTE dte) {
// Find the .tt file's ProjectItem
ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
// If the .tt file is not opened, open it
if (projectItem.Document == null)
projectItem.Open(Constants.vsViewKindCode);
if (AlwaysKeepTemplateDirty) {
// Mark the .tt file as unsaved. This way it will be saved and update itself next time the
// project is built. Basically, it keeps marking itself as unsaved to make the next build work.
// Note: this is certainly hacky, but is the best I could come up with so far.
projectItem.Document.Saved = false;
}
return projectItem.ContainingProject;
}
#region Manager
/*
Manager.tt from Damien Guard: http://d...content-available-to-author-only...g.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited
*/
// Manager class records the various blocks so it can split them up
class Manager {
private class Block {
public String Name;
public int Start, Length;
}
private Block currentBlock;
private List<Block> files = new List<Block>();
private Block footer = new Block();
private Block header = new Block();
private ITextTemplatingEngineHost host;
private StringBuilder template;
protected List<String> generatedFileNames = new List<String>();
public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
}
public void KeepGeneratedFile(String name) {
name = Path.Combine(Path.GetDirectoryName(host.TemplateFile), name);
generatedFileNames.Add(name);
}
public void StartNewFile(String name) {
if (name == null)
throw new ArgumentNullException("name");
CurrentBlock = new Block { Name = name };
}
public void StartFooter() {
CurrentBlock = footer;
}
public void StartHeader() {
CurrentBlock = header;
}
public void EndBlock() {
if (CurrentBlock == null)
return;
CurrentBlock.Length = template.Length - CurrentBlock.Start;
if (CurrentBlock != header && CurrentBlock != footer)
files.Add(CurrentBlock);
currentBlock = null;
}
public virtual void Process(bool split) {
if (split) {
EndBlock();
String headerText = template.ToString(header.Start, header.Length);
String footerText = template.ToString(footer.Start, footer.Length);
String outputPath = Path.GetDirectoryName(host.TemplateFile);
files.Reverse();
foreach (Block block in files) {
String fileName = Path.Combine(outputPath, block.Name);
String content = headerText + template.ToString(block.Start, block.Length) + footerText;
generatedFileNames.Add(fileName);
CreateFile(fileName, content);
template.Remove(block.Start, block.Length);
}
}
}
protected virtual void CreateFile(String fileName, String content) {
if (IsFileContentDifferent(fileName, content))
File.WriteAllText(fileName, content);
}
public virtual String GetCustomToolNamespace(String fileName) {
return null;
}
public virtual String DefaultProjectNamespace {
get { return null; }
}
protected bool IsFileContentDifferent(String fileName, String newContent) {
return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
}
private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
this.host = host;
this.template = template;
}
private Block CurrentBlock {
get { return currentBlock; }
set {
if (CurrentBlock != null)
EndBlock();
if (value != null)
value.Start = template.Length;
currentBlock = value;
}
}
private class VSManager : Manager {
private EnvDTE.ProjectItem templateProjectItem;
private EnvDTE.DTE dte;
private Action<String> checkOutAction;
private Action<IEnumerable<String>> projectSyncAction;
public override String DefaultProjectNamespace {
get {
return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
}
}
public override String GetCustomToolNamespace(string fileName) {
return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
}
public override void Process(bool split) {
if (templateProjectItem.ProjectItems == null)
return;
base.Process(split);
projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
}
protected override void CreateFile(String fileName, String content) {
if (IsFileContentDifferent(fileName, content)) {
CheckoutFileIfRequired(fileName);
File.WriteAllText(fileName, content);
}
}
internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
: base(host, template) {
var hostServiceProvider = (IServiceProvider)host;
if (hostServiceProvider == null)
throw new ArgumentNullException("Could not obtain IServiceProvider");
dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
if (dte == null)
throw new ArgumentNullException("Could not obtain DTE from host");
templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
}
private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
var keepFileNameSet = new HashSet<String>(keepFileNames);
var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + ".";
foreach (EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
projectFiles.Add(projectItem.get_FileNames(0), projectItem);
// Remove unused items from the project
foreach (var pair in projectFiles)
if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
pair.Value.Delete();
// Add missing files to the project
foreach (String fileName in keepFileNameSet)
if (!projectFiles.ContainsKey(fileName))
templateProjectItem.ProjectItems.AddFromFile(fileName);
}
private void CheckoutFileIfRequired(String fileName) {
var sc = dte.SourceControl;
if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
}
}
}
/*
End of Manager.tt
*/
#endregion
#>
<#+
void GenerateDictionaryMapper(string businessName, IDictionary<string,bool> propertyWithCopyExclusions, string copyDictionaryNamespace,bool useStaticReflection)
{ #>
#region MappingMethods
public static Dictionary<string,Action> GenerateActionDictionary<T>(T dest, <#=businessName #> source, bool includeIdentifier)
where T: <#= businessName #>DTO
{
var result= new Dictionary<string,Action>()
{
<#+
PushIndent("\t");
PushIndent("\t");
PushIndent("\t");
PushIndent("\t");
PushIndent("\t");
PushIndent("\t");
foreach(string item in propertyWithCopyExclusions.Keys)
{
if(propertyWithCopyExclusions[item]==false)
{
if(useStaticReflection){
WriteLine("\t{source.ClassMemberName(x=>x."+item+"),");
WriteLine("()=>dest."+item+"=source."+item+"},");
} else{
Write("\t{\""+item+"\",");
WriteLine("()=>dest."+item+"=source."+item+"},");
}
}
}
PopIndent();
PopIndent();
PopIndent();
PopIndent();
PopIndent();
PopIndent();
#>
};
return result;}
public static T CopyData<T>(Func<T> creator, <#=businessName #> source, bool includeIdentifier,
ICollection<string> excludeList) where T: <#=businessName#>DTO
{
return <#=copyDictionaryNamespace#>.CopyDictionary<T,<#= businessName #>>.CopyData(GenerateActionDictionary,creator,source,includeIdentifier, excludeList);
}
#endregion MappingMethods
<#+
} #>
// CopyDictionary.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project.Shared.CrossCutting
{
public interface IValidationDictionary
{
void AddError(string key, string errorMessage);
bool IsValid { get; }
}
internal static class CopyDictionary<T, TU> where T : TU
{
public static T CopyData(Func<T, TU, bool, Dictionary<string, Action>> actionDictionaryFunc,
Func<T> creator, TU source, bool includeIdentifier, ICollection<string> excludeList)
{
return CopyDataMaster(actionDictionaryFunc, creator, source, includeIdentifier, excludeList,
kvp => kvp.Value());
}
public static T CopyData(Func<T, TU, bool, Dictionary<string, Action>> actionDictionaryFunc,
IValidationDictionary validation, Func<T> creator, TU source, bool includeIdentifier, ICollection<string> excludeList)
{
var result = CopyDataMaster(actionDictionaryFunc, creator, source, includeIdentifier, excludeList,
kvp =>
{
try
{
kvp.Value();
}
catch (Exception exception)
{
validation.AddError(kvp.Key, exception.Message);
}
});
if (validation.IsValid == false)
throw new ArgumentException("Validation contains errors");
return result;
}
private static T CopyDataMaster(Func<T, TU, bool, Dictionary<string, Action>> actionDictionaryFunc,
Func<T> creator, TU source, bool includeIdentifier, ICollection<string> excludeList,
Action<KeyValuePair<string, Action>> action)
{
var result = creator();
foreach (var kvp in actionDictionaryFunc(result, source, includeIdentifier))
{
if (excludeList == null || excludeList.Contains(kvp.Key) == false)
action(kvp);
}
return result;
}
}
}