fork download
  1. <#
  2. /*
  3. use envDte to retrieve class info, create an interface for that class
  4.   ?consider updating class to implement the interface?
  5.   ?and mapper for that interface?
  6. */
  7. #>
  8. <#@ template language="C#" debug="true" hostspecific="true" #>
  9. <#@ assembly name="System.Core" #>
  10. <#@ assembly name="System.Xml" #>
  11. <#@ assembly name="Microsoft.CSharp" #>
  12. <#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
  13. <#@ assembly name="EnvDTE" #>
  14. <#@ assembly name="EnvDTE80" #>
  15. <#@ assembly name="VSLangProj" #>
  16. <#@ import namespace="System.Collections.Generic" #>
  17. <#@ import namespace="System.IO" #>
  18. <#@ import namespace="System.Linq" #>
  19. <#@ import namespace="System.Text" #>
  20. <#@ import namespace="System.Text.RegularExpressions" #>
  21. <#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
  22. <#@ import namespace="EnvDTE" #>
  23. <#@ import namespace="EnvDTE80" #>
  24. <#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
  25. <# // To debug, uncomment the next two lines !!
  26. //System.Diagnostics.Debugger.Launch();
  27. //System.Diagnostics.Debugger.Break();
  28. #>
  29. <#PrepareDataToRender(this); #>
  30. <#var manager = Manager.Create(Host, GenerationEnvironment); #>
  31. <#manager.StartHeader(); #>// <auto-generated />
  32. // This file was generated by a T4 template.
  33. // Don't change it directly as your change would get overwritten. Instead, make changes
  34. // to the .tt file (i.e. the T4 template) and save it to regenerate this file.
  35.  
  36. // Make sure the compiler doesn't complain about missing Xml comments
  37. #pragma warning disable 1591
  38. using System;
  39. <# manager.EndBlock();#>
  40. <#
  41.  
  42.  
  43. var rootNamespace = Project.Properties.Item("RootNamespace").Value.ToString();
  44. #>
  45. //Project:<#=Project.Name#>
  46. //rootNamespace=<#= rootNamespace #>
  47. <# RecurseProjectItems(manager,Project.ProjectItems);
  48.  
  49. if(string.IsNullOrEmpty(CurrentNamespaceGenerating)==false)
  50. WriteLine("} //end namespace2");
  51.  
  52.  
  53. #>
  54.  
  55. <#manager.Process(SplitIntoMultipleFiles);#>
  56. <#@ Include File="DTOGenerator.tt.settings.t4" #>
  57. <#+
  58. string CurrentNamespaceGenerating=null;
  59. void RecurseProjectItems(Manager manager,ProjectItems container)
  60. {
  61. foreach(var item in container.Cast<ProjectItem>())
  62. {
  63. if(item.FileCodeModel==null)
  64. {
  65. if(IncludeDiagnostics)WriteLine("//opening container:"+item.Name);
  66. RecurseProjectItems(manager,item.ProjectItems);
  67. continue;
  68. }
  69. DateTime lastWriteTime=File.GetLastWriteTime(item.get_FileNames(0));
  70.  
  71. if(IncludeDiagnostics)WriteLine("//Found CodeModel:"+item.FileCodeModel.GetType().FullName);
  72. var interfaces=RecurseForInterfaces(manager,item.FileCodeModel.CodeElements,string.Empty)
  73. .OrderBy(ri=>ri.Item2)
  74. .ThenByDescending(ri=>ri.Item1.Name).ToArray();
  75. if(interfaces.Length>1 && interfaces[0].Item1.Name==interfaces[1].Item1.Name+"DTO")
  76. interfaces=interfaces.Except(new[]{interfaces[1]}).ToArray();
  77. foreach(var i in interfaces)
  78. {
  79. if(IncludeDiagnostics)
  80. WriteLine("//Found Interface:"+i.Item1.Name);
  81. var properties=RecurseForProperties(i.Item1);
  82. if(properties.Count()>0)
  83. WriteInterface(manager,i.Item1,properties,i.Item2);
  84. }
  85.  
  86.  
  87. }
  88. }
  89.  
  90.  
  91. IEnumerable<Tuple<string,Action>> RecurseForProperties(CodeInterface2 codeInterface)
  92. {
  93. var names=new HashSet<string>();
  94. if(codeInterface.Bases !=null)
  95. foreach(var baseInterface in codeInterface.Bases.Cast<CodeElement>())
  96. {
  97. if(IncludeDiagnostics) WriteLine("//found base:"+baseInterface.Name);
  98. foreach(var item in RecurseForProperties(((CodeInterface2)baseInterface)))
  99. {
  100. if(names.Contains(item.Item1)==false)
  101. {
  102. names.Add(item.Item1);
  103. yield return item;
  104. }
  105. }
  106. }
  107. foreach( CodeProperty item in codeInterface.Members.OfType<CodeProperty>())
  108. {
  109. if(names.Contains(item.Name)==false)
  110. {
  111. names.Add(item.Name);
  112. yield return Tuple.Create<string,Action>(item.Name,()=> WriteMapping(item));
  113. }
  114. }
  115. }
  116.  
  117. IEnumerable<Tuple<CodeInterface2,string>> RecurseForInterfaces(Manager manager, CodeElements codeElements,string currentNamespace )
  118. {
  119.  
  120. var q=codeElements.Cast<CodeElement>( )
  121. //.Where(x =>x is CodeInterface||x is CodeClass || x is CodeNamespace) //&& ((CodeInterface)x).Attributes.Count>0)
  122. //.Where(x => x.Name.StartsWith("System")==false)
  123. //.Where(x => x.Name.StartsWith("Infragistics")==false)
  124. //.Where(x => x.Name.StartsWith("Microsoft")==false)
  125. //.Where(x => x.Name.StartsWith("ICSharpCode")==false)
  126. ;
  127. var interfaces=new List<string>();
  128.  
  129. foreach (CodeElement element in q)
  130. {
  131.  
  132. if (element is CodeInterface && interfaces.Contains(element.Name)==false)
  133. {
  134. var c = (CodeInterface2)element;
  135. //verify element is a project element not an external
  136. if (c.InfoLocation!=vsCMInfoLocation.vsCMInfoLocationProject)
  137. continue;
  138. yield return Tuple.Create<CodeInterface2,string>(c,currentNamespace);
  139.  
  140. }
  141.  
  142. if (element is CodeNamespace)
  143. {
  144. var ns=(CodeNamespace)element;
  145. if(SkippedNamespaces.Contains( ns.Name)==false)
  146. foreach(var find in RecurseForInterfaces(manager,ns.Members,ns.Name))
  147. yield return find;
  148. }
  149. }
  150.  
  151.  
  152. }
  153.  
  154. void WriteMapping(CodeProperty codeProperty)
  155. {
  156. WriteLine("");
  157. WriteLine("///CodeProperty");
  158. WriteLine("///<summary>");
  159. WriteLine("///"+codeProperty.FullName);
  160. WriteLine("///</summary>");
  161. if(codeProperty.Getter==null && codeProperty.Setter==null)
  162. return;
  163. if(codeProperty.Attributes!=null){
  164. foreach(CodeAttribute a in codeProperty.Attributes)
  165. {
  166. Write("["+a.FullName);
  167. if(a.Children!=null && a.Children.Count>0)
  168. {
  169. var start=a.Children.Cast<CodeElement>().First().GetStartPoint();
  170. var finish= a.GetEndPoint();
  171. string wholeAttributeLine=start.CreateEditPoint().GetText(finish);
  172.  
  173. Write("("+wholeAttributeLine);
  174. }
  175. WriteLine("]");
  176. }
  177. }
  178. Write("public "+GetFullName(codeProperty.Type) +" "+codeProperty.Prototype);
  179.  
  180. Write(" {");
  181. //if(codeProperty.Getter!=null && codeProperty.Getter.Access!=vsCMAccess.vsCMAccessPrivate)
  182. Write("get;");
  183. //if(codeProperty.Setter!=null)
  184. Write("set;");
  185. WriteLine("}");
  186.  
  187. //WriteLine("public static string MetaBinding"+MakeIntoValidIdentifier(codeProperty.Name)+"{ get{ return \""
  188. //+codeProperty.Parent.Name+"."+codeProperty.Name+"\";}}");
  189. }
  190. string GetFullName(CodeTypeRef codeType)
  191. {
  192. string fullName;
  193.  
  194. if (codeType.TypeKind == vsCMTypeRef.vsCMTypeRefArray)
  195. {
  196. CodeTypeRef arrayType = codeType.ElementType;
  197. fullName = arrayType.AsFullName + "[]";
  198. }
  199. else
  200. {
  201. fullName = codeType.AsFullName;
  202. }
  203. return fullName;
  204. }
  205. void WriteInterface(Manager manager,CodeInterface2 codeInterface, IEnumerable<Tuple<string,Action>> writeProperties,string parentNamespace)
  206. {
  207.  
  208. if(IncludeDiagnostics)WriteLine("//CodeInterface parentNamespace:"+parentNamespace);
  209. var xmlBlock="\t//CodeInterface";
  210.  
  211. var start=codeInterface.GetStartPoint();
  212.  
  213. var finish=codeInterface.Children!=null && codeInterface.Children.Count>0? codeInterface.Children.Cast<CodeElement>().First().GetStartPoint() : codeInterface.GetEndPoint();//.GetEndPoint();
  214. string wholeInterfaceLine=start.CreateEditPoint().GetText(finish);
  215. var classLine=wholeInterfaceLine.ToString();
  216. var justProto=wholeInterfaceLine.Contains("{")?classLine.Substring(0,classLine.IndexOf("{")):classLine;
  217.  
  218. if(IncludeDiagnostics && justProto.Length<100 && justProto.Contains("*/")==false ){WriteLine("/* justProto: "+justProto); WriteLine(" */");}
  219.  
  220. manager.EndBlock();
  221.  
  222. var fullName=codeInterface.FullName;
  223. var name=fullName.Contains(".")?fullName.Substring(fullName.LastIndexOf('.')+1):fullName;
  224. if(name.StartsWith("I")) name=name.Substring(1);
  225. if(name.EndsWith("DTO")==false)
  226. name=name+"DTO";
  227. manager.StartNewFile(name+".generated.cs");
  228. if(IncludeDiagnostics) WriteLine("//fullName:"+fullName);
  229. if(IncludeDiagnostics)WriteLine("//name:"+name);
  230. var targetNamespace=GetTargetNamespace(codeInterface.Namespace.Name);
  231. if(codeInterface!=null && codeInterface.Namespace!=null && (targetNamespace != CurrentNamespaceGenerating|| SplitIntoMultipleFiles)){
  232.  
  233. if(string.IsNullOrEmpty(CurrentNamespaceGenerating)==false && SplitIntoMultipleFiles==false)
  234. WriteLine("} //namespace end1");
  235. CurrentNamespaceGenerating=GetTargetNamespace(codeInterface.Namespace.Name);
  236.  
  237. if(IncludeDiagnostics)WriteLine("//end namespace:"+CurrentNamespaceGenerating+":begin:"+codeInterface.Namespace.Name);
  238. #>
  239.  
  240. namespace <#=codeInterface.Namespace.Name#> {<#+} #>
  241.  
  242. ///<seealso cref="<#=codeInterface.FullName#>"/>
  243. public partial class <#=name#>:<#=codeInterface.Name #>{
  244. <#+
  245. PushIndent("\t");
  246. PushIndent("\t");
  247. foreach(var item in writeProperties)
  248. item.Item2();
  249. PopIndent();
  250. PopIndent();
  251. if(SplitIntoMultipleFiles)
  252. WriteLine("}");
  253. #>
  254. } //end class <#=name #>
  255.  
  256. <#+
  257.  
  258. }
  259. string ConvertFullName(CodeModel cm, string fullName)
  260. {
  261. // Convert a .NET type name into a C++ type name.
  262. if ((cm.Language == CodeModelLanguageConstants.vsCMLanguageVC) ||
  263. (cm.Language == CodeModelLanguageConstants.vsCMLanguageMC))
  264. return fullName.Replace(".", "::");
  265. else
  266. return fullName;
  267. }
  268. #>
  269. <#+
  270.  
  271. static DTE Dte;
  272. static Project Project;
  273.  
  274. static TextTransformation TT;
  275. static string T4FileName;
  276. static string T4Folder;
  277. static string AppRoot;
  278. static string GeneratedCode = @"GeneratedCode(""InterfaceT4"", ""2.0"")";
  279. static Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
  280.  
  281. void PrepareDataToRender(TextTransformation tt) {
  282. TT = tt;
  283. T4FileName = Path.GetFileName(Host.TemplateFile);
  284. T4Folder = Path.GetDirectoryName(Host.TemplateFile);
  285.  
  286. // Get the DTE service from the host
  287. var serviceProvider = Host as IServiceProvider;
  288. if (serviceProvider != null) {
  289. Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
  290. }
  291.  
  292. // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
  293. if (Dte == null) {
  294. throw new Exception("InterfaceT4 can only execute through the Visual Studio host");
  295. }
  296.  
  297. Project = GetProjectContainingT4File(Dte);
  298.  
  299. if (Project == null) {
  300. Error("Could not find the VS Project containing the T4 file.");
  301. return;
  302. }
  303.  
  304. // Get the path of the root folder of the app
  305. AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
  306.  
  307. }
  308.  
  309. Project GetProjectContainingT4File(DTE dte) {
  310.  
  311. // Find the .tt file's ProjectItem
  312. ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
  313.  
  314. // If the .tt file is not opened, open it
  315. if (projectItem.Document == null)
  316. projectItem.Open(Constants.vsViewKindCode);
  317.  
  318. if (AlwaysKeepTemplateDirty) {
  319. // Mark the .tt file as unsaved. This way it will be saved and update itself next time the
  320. // project is built. Basically, it keeps marking itself as unsaved to make the next build work.
  321. // Note: this is certainly hacky, but is the best I could come up with so far.
  322. projectItem.Document.Saved = false;
  323. }
  324.  
  325. return projectItem.ContainingProject;
  326. }
  327.  
  328.  
  329. #region Manager
  330. /*
  331.   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
  332. */
  333.  
  334.  
  335. // Manager class records the various blocks so it can split them up
  336. class Manager {
  337. private class Block {
  338. public String Name;
  339. public int Start, Length;
  340. }
  341.  
  342. private Block currentBlock;
  343. private List<Block> files = new List<Block>();
  344. private Block footer = new Block();
  345. private Block header = new Block();
  346. private ITextTemplatingEngineHost host;
  347. private StringBuilder template;
  348. protected List<String> generatedFileNames = new List<String>();
  349.  
  350. public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
  351. return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
  352. }
  353.  
  354. public void KeepGeneratedFile(String name) {
  355. name = Path.Combine(Path.GetDirectoryName(host.TemplateFile), name);
  356. generatedFileNames.Add(name);
  357. }
  358.  
  359. public void StartNewFile(String name) {
  360. if (name == null)
  361. throw new ArgumentNullException("name");
  362. CurrentBlock = new Block { Name = name };
  363. }
  364.  
  365. public void StartFooter() {
  366. CurrentBlock = footer;
  367. }
  368.  
  369. public void StartHeader() {
  370. CurrentBlock = header;
  371. }
  372.  
  373. public void EndBlock() {
  374. if (CurrentBlock == null)
  375. return;
  376. CurrentBlock.Length = template.Length - CurrentBlock.Start;
  377. if (CurrentBlock != header && CurrentBlock != footer)
  378. files.Add(CurrentBlock);
  379. currentBlock = null;
  380. }
  381.  
  382. public virtual void Process(bool split) {
  383. if (split) {
  384. EndBlock();
  385. String headerText = template.ToString(header.Start, header.Length);
  386. String footerText = template.ToString(footer.Start, footer.Length);
  387. String outputPath = Path.GetDirectoryName(host.TemplateFile);
  388. files.Reverse();
  389. foreach (Block block in files) {
  390. String fileName = Path.Combine(outputPath, block.Name);
  391. String content = headerText + template.ToString(block.Start, block.Length) + footerText;
  392. generatedFileNames.Add(fileName);
  393. CreateFile(fileName, content);
  394. template.Remove(block.Start, block.Length);
  395. }
  396. }
  397. }
  398.  
  399. protected virtual void CreateFile(String fileName, String content) {
  400. if (IsFileContentDifferent(fileName, content))
  401. File.WriteAllText(fileName, content);
  402. }
  403.  
  404. public virtual String GetCustomToolNamespace(String fileName) {
  405. return null;
  406. }
  407.  
  408. public virtual String DefaultProjectNamespace {
  409. get { return null; }
  410. }
  411.  
  412. protected bool IsFileContentDifferent(String fileName, String newContent) {
  413. return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
  414. }
  415.  
  416. private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
  417. this.host = host;
  418. this.template = template;
  419. }
  420.  
  421. private Block CurrentBlock {
  422. get { return currentBlock; }
  423. set {
  424. if (CurrentBlock != null)
  425. EndBlock();
  426. if (value != null)
  427. value.Start = template.Length;
  428. currentBlock = value;
  429. }
  430. }
  431.  
  432. private class VSManager : Manager {
  433. private EnvDTE.ProjectItem templateProjectItem;
  434. private EnvDTE.DTE dte;
  435. private Action<String> checkOutAction;
  436. private Action<IEnumerable<String>> projectSyncAction;
  437.  
  438. public override String DefaultProjectNamespace {
  439. get {
  440. return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
  441. }
  442. }
  443.  
  444. public override String GetCustomToolNamespace(string fileName) {
  445. return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
  446. }
  447.  
  448. public override void Process(bool split) {
  449. if (templateProjectItem.ProjectItems == null)
  450. return;
  451. base.Process(split);
  452. projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
  453. }
  454.  
  455. protected override void CreateFile(String fileName, String content) {
  456. if (IsFileContentDifferent(fileName, content)) {
  457. CheckoutFileIfRequired(fileName);
  458. File.WriteAllText(fileName, content);
  459. }
  460. }
  461.  
  462. internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
  463. : base(host, template) {
  464. var hostServiceProvider = (IServiceProvider)host;
  465. if (hostServiceProvider == null)
  466. throw new ArgumentNullException("Could not obtain IServiceProvider");
  467. dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
  468. if (dte == null)
  469. throw new ArgumentNullException("Could not obtain DTE from host");
  470. templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
  471. checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
  472. projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
  473. }
  474.  
  475. private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
  476. var keepFileNameSet = new HashSet<String>(keepFileNames);
  477. var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
  478. var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + ".";
  479. foreach (EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
  480. projectFiles.Add(projectItem.get_FileNames(0), projectItem);
  481.  
  482. // Remove unused items from the project
  483. foreach (var pair in projectFiles)
  484. if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
  485. pair.Value.Delete();
  486.  
  487. // Add missing files to the project
  488. foreach (String fileName in keepFileNameSet)
  489. if (!projectFiles.ContainsKey(fileName))
  490. templateProjectItem.ProjectItems.AddFromFile(fileName);
  491. }
  492.  
  493. private void CheckoutFileIfRequired(String fileName) {
  494. var sc = dte.SourceControl;
  495. if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
  496. checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
  497. }
  498. }
  499. }
  500.  
  501. /*
  502.   End of Manager.tt
  503. */
  504. #endregion
  505. #>
Not running #stdin #stdout 0s 0KB
stdin
<#+
/*

This file contains settings used by DTOGenerator.tt The main goal is to avoid the need for users
to fork the 'official' template in order to achieve what they want.

*/

IEnumerable<string> SkippedNamespaces=new[]{"Shared.CrossCutting","Shared.Behaviors"};

/// <remarks>Must be static so that GetTargetNamespace can reference it</remarks>
static bool IncludeDiagnostics=false;

Func<string,string> GetTargetNamespace=sourceNamespace=> {
	if(string.IsNullOrEmpty(sourceNamespace))
	{
		if(IncludeDiagnostics)
			System.Diagnostics.Debug.WriteLine("No Sourcenamespace found");
		return sourceNamespace;
	}
	if(sourceNamespace.Contains(".")==false){
		if(IncludeDiagnostics)
			System.Diagnostics.Debug.WriteLine("Sourcenamespace found");
		return sourceNamespace+".DTO";}
	return sourceNamespace.Substring(0,sourceNamespace.LastIndexOf('.'))+"DTO";
};
bool SplitIntoMultipleFiles = false;
bool AlwaysKeepTemplateDirty = false;

void RenderAdditionalCode() {
}
#>
stdout
Standard output is empty