PageRenderTime 63ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/Web/T4MVC.tt

https://bitbucket.org/mgreuel/noblog
Unknown | 2473 lines | 2129 code | 344 blank | 0 comment | 0 complexity | 04e577062b8954c3c566f7e74749498f MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <#
  2. /*
  3. T4MVC Version 3.8.2
  4. Find latest version and documentation at http://mvccontrib.codeplex.com/wikipage?title=T4MVC
  5. Discuss on StackOverflow or on Codeplex (https://t4mvc.codeplex.com/discussions)
  6. T4MVC is part of the MvcContrib project, but in a different Codeplex location (http://t4mvc.codeplex.com)
  7. Maintained by David Ebbo, with much feedback from the MVC community (thanks all!)
  8. david.ebbo@microsoft.com
  9. http://twitter.com/davidebbo
  10. http://blog.davidebbo.com/ (previously: http://blogs.msdn.com/davidebb)
  11. Related blog posts: http://blogs.msdn.com/davidebb/archive/tags/T4MVC/default.aspx
  12. Please use in accordance to the MvcContrib license (http://mvccontrib.codeplex.com/license)
  13. */
  14. #>
  15. <#@ template language="C#" debug="true" hostspecific="true" #>
  16. <#@ assembly name="System.Core" #>
  17. <#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #>
  18. <#@ assembly name="EnvDTE" #>
  19. <#@ assembly name="EnvDTE80" #>
  20. <#@ assembly name="VSLangProj" #>
  21. <#@ assembly name="System.Xml" #>
  22. <#@ assembly name="System.Xml.Linq" #>
  23. <#@ import namespace="System.Collections.Generic" #>
  24. <#@ import namespace="System.IO" #>
  25. <#@ import namespace="System.Linq" #>
  26. <#@ import namespace="System.Text" #>
  27. <#@ import namespace="System.Text.RegularExpressions" #>
  28. <#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
  29. <#@ import namespace="EnvDTE" #>
  30. <#@ import namespace="EnvDTE80" #>
  31. <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
  32. <# // To debug, uncomment the next two lines !!
  33. // System.Diagnostics.Debugger.Launch();
  34. // System.Diagnostics.Debugger.Break();
  35. #>
  36. <#settings=MvcSettings.Load(Host);#>
  37. <#PrepareDataToRender(this); #>
  38. <#var manager = Manager.Create(Host, GenerationEnvironment); #>
  39. <#manager.StartHeader(); #>// <auto-generated />
  40. // This file was generated by a T4 template.
  41. // Don't change it directly as your change would get overwritten. Instead, make changes
  42. // to the .tt file (i.e. the T4 template) and save it to regenerate this file.
  43. // Make sure the compiler doesn't complain about missing Xml comments
  44. #pragma warning disable 1591
  45. #region T4MVC
  46. using System;
  47. using System.Diagnostics;
  48. using System.CodeDom.Compiler;
  49. using System.Collections.Generic;
  50. using System.Linq;
  51. using System.Runtime.CompilerServices;
  52. using System.Threading.Tasks;
  53. using System.Web;
  54. using System.Web.Hosting;
  55. using System.Web.Mvc;
  56. using System.Web.Mvc.Ajax;
  57. using System.Web.Mvc.Html;
  58. using System.Web.Routing;
  59. using <#=settings.T4MVCNamespace #>;
  60. <#foreach (var referencedNamespace in settings.ReferencedNamespaces) { #>
  61. using <#=referencedNamespace #>;
  62. <#} #>
  63. <#manager.EndBlock(); #>
  64. [<#= GeneratedCode #>, DebuggerNonUserCode]
  65. public static partial class <#=settings.HelpersPrefix #>
  66. {
  67. <#if (settings.IncludeAreasToken) { #>
  68. public static class Areas
  69. {
  70. <#} #>
  71. <#foreach (var area in Areas.Where(a => !string.IsNullOrEmpty(a.Name))) { #>
  72. static readonly <#=area.Name #>Class s_<#=area.Name #> = new <#=area.Name #>Class();
  73. public static <#=area.Name #>Class <#=EscapeID(area.Namespace) #> { get { return s_<#=area.Name #>; } }
  74. <#} #>
  75. <#if (settings.IncludeAreasToken) { #>
  76. }
  77. <#} #>
  78. <#foreach (var controller in DefaultArea.GetControllers()) { #>
  79. public static <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
  80. <#} #>
  81. }
  82. namespace <#=settings.T4MVCNamespace #>
  83. {
  84. <#foreach (var area in Areas.Where(a => !string.IsNullOrEmpty(a.Name))) { #>
  85. [<#= GeneratedCode #>, DebuggerNonUserCode]
  86. public class <#=area.Name #>Class
  87. {
  88. public readonly string Name = "<#=ProcessAreaOrControllerName(area.Name) #>";
  89. <#foreach (var controller in area.GetControllers()) { #>
  90. public <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
  91. <#} #>
  92. }
  93. <#} #>
  94. }
  95. namespace <#=settings.T4MVCNamespace #>
  96. {
  97. [<#= GeneratedCode #>, DebuggerNonUserCode]
  98. public class Dummy
  99. {
  100. private Dummy() { }
  101. public static Dummy Instance = new Dummy();
  102. }
  103. }
  104. <#foreach (var resultType in ResultTypes.Values) { #>
  105. [<#= GeneratedCode #>, DebuggerNonUserCode]
  106. internal partial class T4MVC_<#=resultType.UniqueName #> : <#=resultType.FullName #>, IT4MVCActionResult
  107. {
  108. public T4MVC_<#=resultType.UniqueName #>(string area, string controller, string action, string protocol = null): base(<#resultType.Constructor.WriteNonEmptyParameterValues(true); #>)
  109. {
  110. this.InitMVCT4Result(area, controller, action, protocol);
  111. }
  112. <#foreach (var method in resultType.AbstractMethods) { #>
  113. <#=method.IsPublic ? "public" : "protected" #> override <#=method.ReturnType#> <#=method.Name #>(<#method.WriteFormalParameters(true); #>) {<# if(method.ReturnType != "void") {#> return default(<#=method.ReturnType#>); <#} #> }
  114. <#} #>
  115. public string Controller { get; set; }
  116. public string Action { get; set; }
  117. public string Protocol { get; set; }
  118. public RouteValueDictionary RouteValueDictionary { get; set; }
  119. }
  120. <#} #>
  121. namespace <#=settings.LinksNamespace #>
  122. {
  123. <#
  124. foreach (string folder in settings.StaticFilesFolders.Concat(GetStaticFilesViewFolders())) {
  125. ProcessStaticFiles(Project, folder);
  126. }
  127. #>
  128. [<#= GeneratedCode #>, DebuggerNonUserCode]
  129. public static partial class Bundles
  130. {
  131. [<#= GeneratedCode #>, DebuggerNonUserCode]
  132. public static partial class Scripts {}
  133. [<#= GeneratedCode #>, DebuggerNonUserCode]
  134. public static partial class Styles {}
  135. }
  136. }
  137. <#
  138. RenderAdditionalCode();
  139. #>
  140. <#foreach (var controller in GetAbstractControllers().Where(c => !c.HasDefaultConstructor)) { #>
  141. <#manager.StartNewFile(controller.GeneratedFileName); #>
  142. namespace <#=controller.Namespace #>
  143. {
  144. [<#= GeneratedCode #>, DebuggerNonUserCode]
  145. public partial class <#=controller.ClassName #>
  146. {
  147. protected <#=controller.ClassName #>() { }
  148. }
  149. }
  150. <#manager.EndBlock(); #>
  151. <#} #>
  152. <#foreach (var controller in GetControllers()) { #>
  153. <#
  154. // Don't generate the file at all if the existing one is up to date
  155. // NOTE: disable this optimization since it doesn't catch view changes! It can be re-enabled later if smarter change detection is added
  156. //if (controller.GeneratedCodeIsUpToDate) {
  157. // manager.KeepGeneratedFile(controller.GeneratedFileName);
  158. // continue;
  159. //}
  160. #>
  161. <#manager.StartNewFile(controller.GeneratedFileName); #>
  162. <#if (!String.IsNullOrEmpty(controller.Namespace)) { #>
  163. namespace <#=controller.Namespace #>
  164. {
  165. <#} #>
  166. public <#if (!controller.NotRealController) { #>partial <#} #>class <#=controller.ClassName #>
  167. {
  168. <#if (!controller.NotRealController) { #>
  169. <#if (!controller.HasExplicitConstructor) { #>
  170. [<#= GeneratedCode #>, DebuggerNonUserCode]
  171. public <#=controller.ClassName #>() { }
  172. <#} #>
  173. [<#= GeneratedCode #>, DebuggerNonUserCode]
  174. protected <#=controller.ClassName #>(Dummy d) { }
  175. [<#= GeneratedCode #>, DebuggerNonUserCode]
  176. protected RedirectToRouteResult RedirectToAction(ActionResult result)
  177. {
  178. var callInfo = result.GetT4MVCResult();
  179. return RedirectToRoute(callInfo.RouteValueDictionary);
  180. }
  181. [<#= GeneratedCode #>, DebuggerNonUserCode]
  182. protected RedirectToRouteResult RedirectToAction(Task<ActionResult> taskResult)
  183. {
  184. return RedirectToAction(taskResult.Result);
  185. }
  186. [<#= GeneratedCode #>, DebuggerNonUserCode]
  187. protected RedirectToRouteResult RedirectToActionPermanent(ActionResult result)
  188. {
  189. var callInfo = result.GetT4MVCResult();
  190. return RedirectToRoutePermanent(callInfo.RouteValueDictionary);
  191. }
  192. [<#= GeneratedCode #>, DebuggerNonUserCode]
  193. protected RedirectToRouteResult RedirectToActionPermanent(Task<ActionResult> taskResult)
  194. {
  195. return RedirectToActionPermanent(taskResult.Result);
  196. }
  197. <#foreach (var method in controller.ActionMethodsUniqueWithoutParameterlessOverload) { #>
  198. [NonAction]
  199. [<#= GeneratedCode #>, DebuggerNonUserCode]
  200. public virtual <#=method.ReturnTypeFullName #> <#=method.Name #>()
  201. {
  202. <#if (method.ReturnTypeFullName == "System.Threading.Tasks.Task<System.Web.Mvc.ActionResult>") { #>
  203. var callInfo = new T4MVC_<#=method.ReturnTypeUniqueName #>(Area, Name, ActionNames.<#=method.ActionName #><# if (method.ActionUrlHttps) {#>, "https"<#}#>);
  204. return System.Threading.Tasks.Task.FromResult(callInfo as ActionResult);
  205. <#} else { #>
  206. return new T4MVC_<#=method.ReturnTypeUniqueName #>(Area, Name, ActionNames.<#=method.ActionName #><# if (method.ActionUrlHttps) {#>, "https"<#}#>);
  207. <#} #>
  208. }
  209. <#} #>
  210. <#foreach (var method in controller.CustomActionMethodsUniqueWithoutParameterlessOverload) { #>
  211. [NonAction]
  212. [<#= GeneratedCode #>, DebuggerNonUserCode]
  213. public virtual <#=method.ReturnTypeFullName #> <#=method.ActionName #>()
  214. {
  215. return new T4MVC_<#=method.ReturnTypeUniqueName #>(Area, Name, ActionNames.<#=method.ActionName #><# if (method.ActionUrlHttps) {#>, "https"<#}#>);
  216. }
  217. <#} #>
  218. <#foreach (var method in controller.CustomActionMethods) { #>
  219. [NonAction]
  220. [<#= GeneratedCode #>, DebuggerNonUserCode]
  221. public virtual <#=method.ReturnTypeFullName #> <#=method.ActionName #>(<#method.WriteFormalParameters(true, true); #>)
  222. {
  223. var callInfo = new T4MVC_<#=method.ReturnTypeUniqueName #>(Area, Name, ActionNames.<#=method.ActionName #><# if (method.ActionUrlHttps) {#>, "https"<#}#>);
  224. <#if (method.Parameters.Count > 0) { #>
  225. <#foreach (var p in method.Parameters) { #>
  226. ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, <#=p.RouteNameExpression #>, <#=p.Name #>);
  227. <#} #>
  228. <#}#>
  229. <#if (method.ReturnTypeFullName == "System.Threading.Tasks.Task<System.Web.Mvc.ActionResult>") { #>
  230. return System.Threading.Tasks.Task.FromResult(callInfo as ActionResult);
  231. <#} else { #>
  232. return callInfo;
  233. <#} #>
  234. }
  235. <#} #>
  236. [<#= GeneratedCode #>, DebuggerNonUserCode]
  237. public <#=controller.ClassName #> Actions { get { return <#=controller.T4MVCControllerFullName #>; } }
  238. [<#= GeneratedCode #>]
  239. public readonly string Area = "<#=ProcessAreaOrControllerName(controller.AreaName) #>";
  240. [<#= GeneratedCode #>]
  241. public readonly string Name = "<#=ProcessAreaOrControllerName(controller.Name) #>";
  242. [<#= GeneratedCode #>]
  243. public const string NameConst = "<#=ProcessAreaOrControllerName(controller.Name) #>";
  244. static readonly ActionNamesClass s_actions = new ActionNamesClass();
  245. [<#= GeneratedCode #>, DebuggerNonUserCode]
  246. public ActionNamesClass ActionNames { get { return s_actions; } }
  247. [<#= GeneratedCode #>, DebuggerNonUserCode]
  248. public class ActionNamesClass
  249. {
  250. <#foreach (var method in controller.ActionMethodsWithUniqueNames) { #>
  251. <# if (settings.UseLowercaseRoutes) { #>
  252. public readonly string <#=method.ActionName #> = (<#=method.ActionNameValueExpression #>).ToLowerInvariant();
  253. <# } else { #>
  254. public readonly string <#=method.ActionName #> = <#=method.ActionNameValueExpression #>;
  255. <# }
  256. } #>
  257. }
  258. <#
  259. // Issue: we can't honor UseLowercaseRoutes here because ToLowerInvariant() is not valid in constants!
  260. if (!settings.UseLowercaseRoutes) { #>
  261. [<#= GeneratedCode #>, DebuggerNonUserCode]
  262. public class ActionNameConstants
  263. {
  264. <#foreach (var method in controller.ActionMethodsWithUniqueNames) { #>
  265. public const string <#=method.ActionName #> = <#=method.ActionNameValueExpression #>;
  266. <#
  267. } #>
  268. }
  269. <#}
  270. } #>
  271. <#if (settings.GenerateParamsForActionMethods && !settings.GenerateParamsAsConstantsForActionMethods){
  272. foreach (var group in controller.UniqueParameterNamesGroupedByActionName) if (group.Any()) { #>
  273. static readonly ActionParamsClass_<#=group.Key #> s_params_<#=group.Key #> = new ActionParamsClass_<#=group.Key #>();
  274. [<#= GeneratedCode #>, DebuggerNonUserCode]
  275. public ActionParamsClass_<#=group.Key #> <#=group.Key + settings.ParamsPropertySuffix #> { get { return s_params_<#=group.Key #>; } }
  276. [<#= GeneratedCode #>, DebuggerNonUserCode]
  277. public class ActionParamsClass_<#=group.Key #>
  278. {
  279. <#foreach (var param in group) { #>
  280. <# if (settings.UseLowercaseRoutes) { #>
  281. public readonly string <#=param.Name #> = (<#=param.RouteNameExpression #>).ToLowerInvariant();
  282. <# } else { #>
  283. public readonly string <#=param.Name #> = <#=param.RouteNameExpression #>;
  284. <# }
  285. } #>
  286. }
  287. <# } #>
  288. <#} #>
  289. <#if (settings.GenerateParamsAsConstantsForActionMethods){
  290. foreach (var group in controller.UniqueParameterNamesGroupedByActionName) if (group.Any()) { #>
  291. [<#= GeneratedCode #>, DebuggerNonUserCode]
  292. public class <#=group.Key + settings.ParamsPropertySuffix#>
  293. {
  294. <#foreach (var param in group) { #>
  295. <# if (settings.UseLowercaseRoutes) { #>
  296. public const string <#=param.Name #> = (<#=param.RouteNameExpression #>).ToLowerInvariant();
  297. <# } else { #>
  298. public const string <#=param.Name #> = <#=param.RouteNameExpression #>;
  299. <# }
  300. } #>
  301. }
  302. <# } #>
  303. <#} #>
  304. static readonly ViewsClass s_views = new ViewsClass();
  305. [<#= GeneratedCode #>, DebuggerNonUserCode]
  306. public ViewsClass Views { get { return s_views; } }
  307. [<#= GeneratedCode #>, DebuggerNonUserCode]
  308. public class ViewsClass
  309. {
  310. <#RenderControllerViews(controller);#>
  311. }
  312. }
  313. <#if (!controller.NotRealController) { #>
  314. [<#= GeneratedCode #>, DebuggerNonUserCode]
  315. public partial class <#=controller.DerivedClassName #> : <#=controller.FullClassName #>
  316. {
  317. public <#=controller.DerivedClassName #>() : base(Dummy.Instance) { }
  318. <#foreach (var method in controller.ActionMethods.Where(m => !m.IsCustomReturnType)) { #>
  319. [NonAction]
  320. partial void <#=method.Name #>Override(T4MVC_<#=method.ReturnTypeUniqueName #> callInfo<#if (method.Parameters.Count > 0) { #>, <#method.WriteFormalParameters(true); #><#}#>);
  321. [NonAction]
  322. public override <#=method.ReturnTypeFullName #> <#=method.Name #>(<#method.WriteFormalParameters(true); #>)
  323. {
  324. var callInfo = new T4MVC_<#=method.ReturnTypeUniqueName #>(Area, Name, ActionNames.<#=method.ActionName #><# if (method.ActionUrlHttps) {#>, "https"<#}#>);
  325. <#if (method.Parameters.Count > 0) { #>
  326. <#foreach (var p in method.Parameters) { #>
  327. ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, <#=p.RouteNameExpression #>, <#=p.Name #>);
  328. <#} #>
  329. <#}#>
  330. <#=method.Name #>Override(callInfo<#if (method.Parameters.Count > 0) { #><#foreach (var p in method.Parameters) { #>, <#=p.Name #><#}}#>);
  331. <#if (method.ReturnTypeFullName == "System.Threading.Tasks.Task<System.Web.Mvc.ActionResult>") { #>
  332. return System.Threading.Tasks.Task.FromResult(callInfo as ActionResult);
  333. <#} else { #>
  334. return callInfo;
  335. <#} #>
  336. }
  337. <#} #>
  338. }
  339. <#} #>
  340. <#if (!String.IsNullOrEmpty(controller.Namespace)) { #>
  341. }
  342. <#} #>
  343. <#manager.EndBlock(); #>
  344. <#} #>
  345. <# if (settings.ExplicitHtmlHelpersForPartials) {
  346. manager.StartNewFile("T4MVC.ExplicitExtensions.cs"); #>
  347. namespace System.Web.Mvc {
  348. [<#= GeneratedCode #>]
  349. public static class HtmlHelpersForExplicitPartials
  350. {
  351. <#
  352. foreach(var partialView in GetPartials()) {
  353. string partialName = partialView.Key;
  354. string partialPath = partialView.Value;
  355. string partialRenderMethod = string.Format(settings.ExplicitHtmlHelpersForPartialsFormat, partialName);
  356. #>
  357. ///<summary>
  358. ///Render the <b><#= partialName #></b> partial.
  359. ///</summary>
  360. public static void <#= partialRenderMethod #>(this HtmlHelper html) {
  361. html.RenderPartial("<#= partialPath #>");
  362. }
  363. ///<summary>
  364. ///Render the <b><#= partialName #></b> partial.
  365. ///</summary>
  366. public static void <#= partialRenderMethod #>(this HtmlHelper html, object model) {
  367. html.RenderPartial("<#= partialPath #>", model);
  368. }
  369. <# } #>
  370. }
  371. }
  372. <# manager.EndBlock(); #>
  373. <# } #>
  374. <#manager.StartFooter(); #>
  375. #endregion T4MVC
  376. #pragma warning restore 1591
  377. <#manager.EndBlock(); #>
  378. <#settings.SaveChanges(manager); #>
  379. <#manager.Process(settings.SplitIntoMultipleFiles); #>
  380. <#@ Include File="T4MVC.tt.hooks.t4" #>
  381. <#+
  382. static MvcSettings settings;
  383. const string ControllerSuffix = "Controller";
  384. static DTE Dte;
  385. static Project Project;
  386. static string AppRoot;
  387. static HashSet<AreaInfo> Areas;
  388. static AreaInfo DefaultArea;
  389. static Dictionary<string, ResultTypeInfo> ResultTypes;
  390. static TextTransformation TT;
  391. static string T4FileName;
  392. static string T4Folder;
  393. static string GeneratedCode = @"GeneratedCode(""T4MVC"", ""2.0"")";
  394. static Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
  395. List<string> virtualPathesForStaticFiles = new List<string>();
  396. IEnumerable<ControllerInfo> GetControllers()
  397. {
  398. var controllers = new List<ControllerInfo>();
  399. foreach (var area in Areas)
  400. {
  401. controllers.AddRange(area.GetControllers());
  402. }
  403. return controllers;
  404. }
  405. IEnumerable<ControllerInfo> GetAbstractControllers()
  406. {
  407. var controllers = new List<ControllerInfo>();
  408. foreach (var area in Areas)
  409. {
  410. controllers.AddRange(area.GetAbstractControllers());
  411. }
  412. return controllers;
  413. }
  414. IDictionary<string, string> GetPartials()
  415. {
  416. var parts = GetControllers()
  417. .Select(m => m.ViewsFolder)
  418. .SelectMany(m => m.Views)
  419. .Where(m => IsPartialView(m.Value));
  420. var partsDic = new Dictionary<string, KeyValuePair<string, string>>();
  421. foreach(var part in parts)
  422. {
  423. string viewName = Sanitize(part.Key);
  424. // Check if we already have a partial view by that name (e.g. if two Views folders have the same ascx)
  425. int keyCollisionCount = partsDic.Where(m => m.Key == viewName || m.Value.Key == viewName).Count();
  426. if (keyCollisionCount > 0)
  427. {
  428. // Append a numbered suffix to avoid the conflict
  429. partsDic.Add(viewName + keyCollisionCount.ToString(), part);
  430. }
  431. else
  432. {
  433. partsDic.Add(viewName, part);
  434. }
  435. }
  436. return partsDic.ToDictionary(k => k.Key, v => v.Value.Value);
  437. }
  438. bool IsPartialView(string viewFilePath)
  439. {
  440. string viewFileName = Path.GetFileName(viewFilePath);
  441. if (viewFileName.EndsWith(".ascx")) return true;
  442. if ((viewFileName.EndsWith(".cshtml") || viewFileName.EndsWith(".vbhtml")) && viewFileName.StartsWith("_"))
  443. {
  444. return true;
  445. }
  446. return false;
  447. }
  448. void PrepareDataToRender(TextTransformation tt)
  449. {
  450. TT = tt;
  451. T4FileName = Path.GetFileName(Host.TemplateFile);
  452. T4Folder = Path.GetDirectoryName(Host.TemplateFile);
  453. Areas = new HashSet<AreaInfo>();
  454. ResultTypes = new Dictionary<string, ResultTypeInfo>();
  455. // Get the DTE service from the host
  456. var serviceProvider = Host as IServiceProvider;
  457. if (serviceProvider != null)
  458. {
  459. Dte = (EnvDTE.DTE)serviceProvider.GetService(typeof(EnvDTE.DTE));
  460. }
  461. // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
  462. if (Dte == null)
  463. {
  464. throw new Exception("T4MVC can only execute through the Visual Studio host");
  465. }
  466. Project = GetProjectContainingT4File(Dte);
  467. if (Project == null)
  468. {
  469. Error("Could not find the VS Project containing the T4 file.");
  470. return;
  471. }
  472. // Get the path of the root folder of the app
  473. AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
  474. ProcessAreas(Project);
  475. }
  476. Project GetProjectContainingT4File(DTE dte)
  477. {
  478. // Find the .tt file's ProjectItem
  479. ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
  480. // If the .tt file is not opened, open it
  481. if (projectItem.Document == null)
  482. projectItem.Open(EnvDTE.Constants.vsViewKindCode);
  483. return projectItem.ContainingProject;
  484. }
  485. void ProcessAreas(Project project)
  486. {
  487. // Process the default area
  488. ProcessArea(project.ProjectItems, null);
  489. // Get the Areas folder
  490. ProjectItem areaProjectItem = GetProjectItem(project, settings.AreasFolder);
  491. // Process areas folder
  492. if (areaProjectItem != null)
  493. {
  494. foreach (ProjectItem item in areaProjectItem.ProjectItems)
  495. {
  496. if (IsFolder(item))
  497. {
  498. ProcessArea(item.ProjectItems, item.Name);
  499. }
  500. }
  501. }
  502. // Process portable areas
  503. foreach (string portableArea in settings.PortableAreas)
  504. {
  505. ProjectItem portableAreaProjectItem = GetProjectItem(project, portableArea);
  506. if (portableAreaProjectItem == null)
  507. return;
  508. if (IsFolder(portableAreaProjectItem))
  509. {
  510. ProcessArea(portableAreaProjectItem.ProjectItems, portableAreaProjectItem.Name);
  511. }
  512. }
  513. }
  514. void ProcessArea(ProjectItems areaFolderItems, string name)
  515. {
  516. var area = new AreaInfo() { Name = name };
  517. ProcessAreaControllers(areaFolderItems, area);
  518. ProcessAreaViews(areaFolderItems, area);
  519. Areas.Add(area);
  520. if (String.IsNullOrEmpty(name))
  521. DefaultArea = area;
  522. }
  523. void ProcessAreaControllers(ProjectItems areaFolderItems, AreaInfo area)
  524. {
  525. // Get area Controllers folder
  526. ProjectItem controllerProjectItem = GetProjectItem(areaFolderItems, settings.ControllersFolder);
  527. if (controllerProjectItem == null)
  528. return;
  529. ProcessControllersRecursive(controllerProjectItem, area);
  530. }
  531. void ProcessAreaViews(ProjectItems areaFolderItems, AreaInfo area)
  532. {
  533. // Get area Views folder
  534. ProjectItem viewsProjectItem = GetProjectItem(areaFolderItems, settings.ViewsRootFolder);
  535. if (viewsProjectItem == null)
  536. return;
  537. ProcessAllViews(viewsProjectItem, area);
  538. }
  539. void ProcessControllersRecursive(ProjectItem projectItem, AreaInfo area)
  540. {
  541. // Recurse into all the sub-items (both files and folder can have some - e.g. .tt files)
  542. foreach (ProjectItem item in projectItem.ProjectItems)
  543. {
  544. ProcessControllersRecursive(item, area);
  545. }
  546. if (projectItem.FileCodeModel != null)
  547. {
  548. DateTime controllerLastWriteTime = File.GetLastWriteTime(projectItem.get_FileNames(0));
  549. foreach (var type in projectItem.FileCodeModel.CodeElements.OfType<CodeClass2>())
  550. {
  551. ProcessControllerType(type, area, controllerLastWriteTime);
  552. }
  553. // Process all the elements that are namespaces
  554. foreach (var ns in projectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>())
  555. {
  556. foreach (var type in ns.Members.OfType<CodeClass2>())
  557. {
  558. ProcessControllerType(type, area, controllerLastWriteTime);
  559. }
  560. }
  561. }
  562. }
  563. void ProcessControllerType(CodeClass2 type, AreaInfo area, DateTime controllerLastWriteTime)
  564. {
  565. // Only process controllers
  566. if (!IsController(type))
  567. return;
  568. // Don't process generic classes (their concrete derived classes will be processed)
  569. if (type.IsGeneric)
  570. return;
  571. //Ignore references to controllers we create
  572. if(area.Controllers.Any(c => c.DerivedClassName == type.Name))
  573. return;
  574. // Make sure the class is partial
  575. if (type.ClassKind != vsCMClassKind.vsCMClassKindPartialClass)
  576. {
  577. try
  578. {
  579. type.ClassKind = vsCMClassKind.vsCMClassKindPartialClass;
  580. }
  581. catch
  582. {
  583. // If we couldn't make it partial, give a warning and skip it
  584. Warning(String.Format("{0} was not able to make the class {1} partial. Please change it manually if possible", T4FileName, type.Name));
  585. return;
  586. }
  587. Warning(String.Format("{0} changed the class {1} to be partial", T4FileName, type.Name));
  588. }
  589. // Collect misc info about the controller class and add it to the collection
  590. var controllerInfo = new ControllerInfo
  591. {
  592. Area = area,
  593. Namespace = type.Namespace != null ? type.Namespace.Name : String.Empty,
  594. ClassName = type.Name
  595. };
  596. //Filter references to controllers we create
  597. foreach(var derived in area.Controllers.Where(c => c.ClassName == controllerInfo.DerivedClassName).ToArray())
  598. area.Controllers.Remove(derived);
  599. // Check if the controller has changed since the generated file was last created
  600. DateTime lastGenerationTime = File.GetLastWriteTime(controllerInfo.GeneratedFileFullPath);
  601. if (lastGenerationTime > controllerLastWriteTime)
  602. {
  603. controllerInfo.GeneratedCodeIsUpToDate = true;
  604. }
  605. // Either process new ControllerInfo or integrate results into existing object for partially defined controllers
  606. var target = area.Controllers.Add(controllerInfo) ? controllerInfo : area.Controllers.First(c => c.Equals(controllerInfo));
  607. target.HasExplicitConstructor |= HasExplicitConstructor(type);
  608. target.HasExplicitDefaultConstructor |= HasExplicitDefaultConstructor(type);
  609. if (type.IsAbstract)
  610. {
  611. // If it's abstract, set a flag and don't process action methods (derived classes will)
  612. target.IsAbstract = true;
  613. }
  614. else
  615. {
  616. // Process all the action methods in the controller
  617. ProcessControllerActionMethods(target, type);
  618. }
  619. }
  620. void ProcessControllerActionMethods(ControllerInfo controllerInfo, CodeClass2 current)
  621. {
  622. bool isAsyncController = IsAsyncController(current);
  623. // We want to process not just the controller class itself, but also its parents, as they
  624. // may themselves define actions
  625. for (CodeClass2 type = current; type != null && type.FullName != "System.Web.Mvc.Controller"; type = (CodeClass2)type.Bases.Item(1))
  626. {
  627. // If the type doesn't come from this project, some actions on it will fail. Try to get a real project type if possible.
  628. if (type.InfoLocation != vsCMInfoLocation.vsCMInfoLocationProject)
  629. {
  630. // Go through all the projects in the solution
  631. for (int i = 1; i <= Dte.Solution.Projects.Count; i++)
  632. {
  633. Project prj = null;
  634. try
  635. {
  636. prj = Dte.Solution.Projects.Item(i);
  637. }
  638. catch (System.Runtime.Serialization.SerializationException)
  639. {
  640. // Some project types (that we don't care about) cause a strange exception, so ingore it
  641. continue;
  642. }
  643. // Skip it if it's the current project or doesn't have a code model
  644. try
  645. {
  646. if (prj == Project || prj.CodeModel == null)
  647. continue;
  648. }
  649. catch (System.NotImplementedException)
  650. {
  651. // Installer project does not implement CodeModel property
  652. continue;
  653. }
  654. try
  655. {
  656. // If we can get a local project type, use it instead of the original
  657. var codeType = prj.CodeModel.CodeTypeFromFullName(type.FullName);
  658. if (codeType != null && codeType.InfoLocation == vsCMInfoLocation.vsCMInfoLocationProject)
  659. {
  660. type = (CodeClass2)codeType;
  661. break;
  662. }
  663. }
  664. catch (System.ArgumentException)
  665. {
  666. // CodeTypeFromFullName throws when called on VB projects with a type it doesn't know
  667. // (instead of returning null), so ignore those exceptions (See http://t4mvc.codeplex.com/workitem/7)
  668. }
  669. }
  670. }
  671. foreach (CodeFunction2 method in GetMethods(type))
  672. {
  673. // Ignore non-public methods
  674. if (method.Access != vsCMAccess.vsCMAccessPublic)
  675. continue;
  676. // Ignore methods that are marked as not being actions
  677. if (GetAttribute(method.Attributes, "System.Web.Mvc.NonActionAttribute") != null)
  678. continue;
  679. // Ignore methods that are marked as Obsolete
  680. if (GetAttribute(method.Attributes, "System.ObsoleteAttribute") != null)
  681. continue;
  682. // Ignore generic methods
  683. if (method.IsGeneric)
  684. continue;
  685. if(isAsyncController && settings.SupportAsyncActions && (method.Type.TypeKind == vsCMTypeRef.vsCMTypeRefVoid) && method.Name.EndsWith("Async"))
  686. {
  687. //Async methods return void and there could be multiple matching Completed methods, so we will use
  688. //the generic ActionResult as the return type for the method.
  689. var resultType = Project.CodeModel.CreateCodeTypeRef("System.Web.Mvc.ActionResult");
  690. // If we haven't yet seen this return type, keep track of it
  691. if (!ResultTypes.ContainsKey(resultType.AsFullName))
  692. {
  693. var resTypeInfo = new ResultTypeInfo(resultType);
  694. ResultTypes[resultType.AsFullName] = resTypeInfo;
  695. }
  696. // Collect misc info about the action method and add it to the collection
  697. controllerInfo.ActionMethods.Add(new ActionMethodInfo(method, current, resultType));
  698. continue;
  699. }
  700. // This takes care of avoiding generic types which cause method.Type.CodeType to blow up
  701. if (method.Type.TypeKind != vsCMTypeRef.vsCMTypeRefCodeType || !(method.Type.CodeType is CodeClass2))
  702. continue;
  703. // We only support action methods that return an ActionResult and Task<ActionResult> derived types
  704. if (!method.Type.CodeType.get_IsDerivedFrom("System.Web.Mvc.ActionResult") && method.Type.CodeType.FullName !="System.Threading.Tasks.Task<System.Web.Mvc.ActionResult>")
  705. {
  706. Warning(String.Format("{0} doesn't support {1}.{2} because it doesn't return a supported {3} type", T4FileName, type.Name, method.Name, method.Type.CodeType.FullName));
  707. continue;
  708. }
  709. // Ignore async completion methods as they can't really be used in T4MVC, and can cause issues.
  710. // See http://stackoverflow.com/questions/5419173/t4mvc-asynccontroller
  711. if (isAsyncController && method.Name.EndsWith("Completed", StringComparison.OrdinalIgnoreCase))
  712. continue;
  713. var methodType = method.Type;
  714. if(method.Type.CodeType.FullName == "System.Threading.Tasks.Task<System.Web.Mvc.ActionResult>")
  715. methodType = Project.CodeModel.CreateCodeTypeRef("System.Web.Mvc.ActionResult");
  716. // If we haven't yet seen this return type, keep track of it
  717. var resTypeInfo2 = new ResultTypeInfo(methodType);
  718. if (!ResultTypes.ContainsKey(resTypeInfo2.FullName))
  719. {
  720. ResultTypes[resTypeInfo2.FullName] = resTypeInfo2;
  721. }
  722. // Make sure the method is virtual
  723. if (!method.CanOverride && method.OverrideKind != vsCMOverrideKind.vsCMOverrideKindOverride)
  724. {
  725. try
  726. {
  727. method.CanOverride = true;
  728. }
  729. catch
  730. {
  731. // If we couldn't make it virtual, give a warning and skip it
  732. Warning(String.Format("{0} was not able to make the action method {1}.{2} virtual. Please change it manually if possible", T4FileName, type.Name, method.Name));
  733. continue;
  734. }
  735. Warning(String.Format("{0} changed the action method {1}.{2} to be virtual", T4FileName, type.Name, method.Name));
  736. }
  737. // Collect misc info about the action method and add it to the collection
  738. controllerInfo.ActionMethods.Add(new ActionMethodInfo(method, current));
  739. }
  740. }
  741. }
  742. void ProcessAllViews(ProjectItem viewsProjectItem, AreaInfo area)
  743. {
  744. // Go through all the sub-folders in the Views folder
  745. foreach (ProjectItem item in viewsProjectItem.ProjectItems)
  746. {
  747. // We only care about sub-folders, not files
  748. if (!IsFolder(item))
  749. continue;
  750. // Find the controller for this view folder
  751. ControllerInfo controller = area.Controllers.SingleOrDefault(c => c.Name.Equals(item.Name, StringComparison.OrdinalIgnoreCase));
  752. if (controller == null)
  753. {
  754. // If it doesn't match a controller, treat as a pseudo-controller for consistency
  755. controller = new ControllerInfo
  756. {
  757. Area = area,
  758. NotRealController = true,
  759. Namespace = MakeClassName(settings.T4MVCNamespace, area.Name),
  760. ClassName = item.Name + ControllerSuffix
  761. };
  762. area.Controllers.Add(controller);
  763. }
  764. AddViewsRecursive(item.ProjectItems, controller.ViewsFolder);
  765. }
  766. }
  767. void AddViewsRecursive(ProjectItems items, ViewsFolderInfo viewsFolder)
  768. {
  769. AddViewsRecursive(items, viewsFolder, false);
  770. }
  771. void AddViewsRecursive(ProjectItems items, ViewsFolderInfo viewsFolder, bool useNonQualifiedViewNames)
  772. {
  773. // Go through all the files in the subfolder to get the view names
  774. foreach (ProjectItem item in items)
  775. {
  776. if (item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile)
  777. {
  778. // Ignore some extensions that are normally not views
  779. if (settings.ExcludedViewExtensions.Any(extension => Path.GetExtension(item.Name).Equals(extension, StringComparison.OrdinalIgnoreCase)))
  780. continue;
  781. viewsFolder.AddView(item, useNonQualifiedViewNames);
  782. }
  783. else if (item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder)
  784. {
  785. string folderName = Path.GetFileName(item.Name);
  786. if (folderName.Equals("App_LocalResources", StringComparison.OrdinalIgnoreCase))
  787. continue;
  788. // Use simple view names if we're already in that mode, or if the folder name is in the collection
  789. bool folderShouldUseNonQualifiedViewNames = useNonQualifiedViewNames || settings.NonQualifiedViewFolders.Contains(folderName, StringComparer.OrdinalIgnoreCase);
  790. var subViewFolder = new ViewsFolderInfo() { Name = folderName };
  791. viewsFolder.SubFolders.Add(subViewFolder);
  792. AddViewsRecursive(item.ProjectItems, subViewFolder, folderShouldUseNonQualifiedViewNames);
  793. }
  794. }
  795. }
  796. void RenderControllerViews(ControllerInfo controller)
  797. {
  798. PushIndent(" ");
  799. RenderViewsRecursive(controller.ViewsFolder, controller);
  800. PopIndent();
  801. }
  802. void RenderViewsRecursive(ViewsFolderInfo viewsFolder, ControllerInfo controller)
  803. {
  804. if(!viewsFolder.HasNonQualifiedViewNames)
  805. {
  806. #>
  807. static readonly _ViewNamesClass s_ViewNames = new _ViewNamesClass();
  808. public _ViewNamesClass ViewNames { get { return s_ViewNames; } }
  809. public class _ViewNamesClass
  810. {
  811. <#+
  812. PushIndent(" ");
  813. foreach (var viewPair in viewsFolder.Views)
  814. {
  815. WriteLine("public readonly string " + EscapeID(Sanitize(viewPair.Key)) + " = \"" + viewPair.Key + "\";");
  816. }
  817. PopIndent();
  818. #>
  819. }
  820. <#+}
  821. // For each view, generate a readonly string
  822. foreach (var viewPair in viewsFolder.Views)
  823. {
  824. WriteLine("public readonly string " + EscapeID(Sanitize(viewPair.Key)) + " = \"" + viewPair.Value + "\";");
  825. }
  826. // For each sub folder, generate a class and recurse
  827. foreach (var subFolder in viewsFolder.SubFolders)
  828. {
  829. string name = Sanitize(subFolder.Name);
  830. string className = "_" + name;
  831. // If the folder name is the same as the parent, add a modifier to avoid class name conflicts
  832. // http://mvccontrib.codeplex.com/workitem/7153
  833. if (name == Sanitize(viewsFolder.Name))
  834. {
  835. className += "_";
  836. }#>
  837. static readonly <#=className#>Class s_<#=name#> = new <#=className#>Class();
  838. public <#=className#>Class <#=EscapeID(name)#> { get { return s_<#=name#>; } }
  839. [<#= GeneratedCode #>, DebuggerNonUserCode]
  840. public partial class <#=className#>Class
  841. {
  842. <#+
  843. PushIndent(" ");
  844. RenderViewsRecursive(subFolder, controller);
  845. PopIndent();
  846. WriteLine("}");
  847. }
  848. }
  849. IEnumerable<string> GetStaticFilesViewFolders()
  850. {
  851. if (settings.AddAllViewsFoldersToStaticFilesFolders)
  852. {
  853. foreach (var area in Areas)
  854. {
  855. yield return area.Name == null ?
  856. settings.ViewsRootFolder :
  857. settings.AreasFolder + "\\" + area.Name + "\\" + settings.ViewsRootFolder;
  858. }
  859. }
  860. }
  861. void ProcessStaticFiles(Project project, string folder)
  862. {
  863. ProjectItem folderProjectItem = GetProjectItem(project, folder);
  864. if (folderProjectItem != null)
  865. {
  866. var rootPath = "~";
  867. if (folder.Contains("\\"))
  868. {
  869. rootPath += "/" + folder.Replace("\\", "/");
  870. rootPath = rootPath.Substring(0, rootPath.LastIndexOf("/"));
  871. }
  872. ProcessStaticFilesRecursive(folderProjectItem, rootPath);
  873. }
  874. }
  875. void ProcessStaticFilesRecursive(ProjectItem projectItem, string path)
  876. {
  877. int nestedLevel = BuildClassStructureForProvidedPath(path);
  878. ProcessStaticFilesRecursive(projectItem, path, new HashSet<String>());
  879. for(int i = 0; i < nestedLevel; ++i) {#>
  880. }
  881. <#+
  882. PopIndent();
  883. }
  884. }
  885. void ProcessStaticFilesRecursive(ProjectItem projectItem, string path, HashSet<String> nameSet)
  886. {
  887. // The passed in HashSet is to guarantee uniqueness with our parent and siblings
  888. string name = SanitizeWithNoConflicts(projectItem.Name, nameSet);
  889. // This HashSet is to guarantee uniqueness of our direct children
  890. // We add our own name to it to avoid class name conflicts (http://mvccontrib.codeplex.com/workitem/7153)
  891. var childrenNameSet = new HashSet<String>();
  892. childrenNameSet.Add(name);
  893. if (IsFolder(projectItem))
  894. {
  895. #>
  896. [<#= GeneratedCode #>, DebuggerNonUserCode]
  897. public static class <#=EscapeID(name)#> {
  898. private const string URLPATH = "<#=path#>/<#=projectItem.Name#>";
  899. public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
  900. public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
  901. <#+
  902. PushIndent(" ");
  903. // Recurse into all the items in the folder
  904. foreach (ProjectItem item in projectItem.ProjectItems)
  905. {
  906. ProcessStaticFilesRecursive(
  907. item,
  908. path + "/" + projectItem.Name,
  909. childrenNameSet);
  910. }
  911. PopIndent();
  912. #>
  913. }
  914. <#+
  915. }
  916. else { #>
  917. <#+
  918. if (!settings.ExcludedStaticFileExtensions.Any(extension => projectItem.Name.EndsWith(extension, StringComparison.OrdinalIgnoreCase))) {
  919. // if it's a Typescript file
  920. if (projectItem.Name.EndsWith(".ts")) {
  921. string tsJavascriptName = projectItem.Name.Replace(".ts", ".js");
  922. string minifiedName = projectItem.Name.Replace(".ts", ".min.js");
  923. if (AddTimestampToStaticLink(projectItem)) { #>
  924. public static readonly string <#=name#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=minifiedName#>") ? Url("<#=minifiedName#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=minifiedName#>") : Url("<#=tsJavascriptName#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=tsJavascriptName#>");
  925. <#+} else {#>
  926. public static readonly string <#=name#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=minifiedName#>") ? Url("<#=minifiedName#>") : Url("<#=tsJavascriptName#>");
  927. <#+} #>
  928. <#+}
  929. // if it's a non-minified javascript file
  930. else if (projectItem.Name.EndsWith(".js") && !projectItem.Name.EndsWith(".min.js")) {
  931. string minifiedName = projectItem.Name.Replace(".js", ".min.js");
  932. if (AddTimestampToStaticLink(projectItem)) { #>
  933. public static readonly string <#=name#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=minifiedName#>") ? Url("<#=minifiedName#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=minifiedName#>") : Url("<#=projectItem.Name#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=projectItem.Name#>");
  934. <#+} else {#>
  935. public static readonly string <#=name#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=minifiedName#>") ? Url("<#=minifiedName#>") : Url("<#=projectItem.Name#>");
  936. <#+} #>
  937. <#+}
  938. else if (projectItem.Name.EndsWith(".css") && !projectItem.Name.EndsWith(".min.css")) {
  939. string minifiedName = projectItem.Name.Replace(".css", ".min.css");
  940. if (AddTimestampToStaticLink(projectItem)) { #>
  941. public static readonly string <#=name#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=minifiedName#>") ? Url("<#=minifiedName#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=minifiedName#>") : Url("<#=projectItem.Name#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=projectItem.Name#>");
  942. <#+} else {#>
  943. public static readonly string <#=name#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=minifiedName#>") ? Url("<#=minifiedName#>") : Url("<#=projectItem.Name#>");
  944. <#+} #>
  945. <#+}
  946. else if (AddTimestampToStaticLink(projectItem)) { #>
  947. public static readonly string <#=name#> = Url("<#=projectItem.Name#>")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/<#=projectItem.Name#>");
  948. <#+}
  949. else { #>
  950. public static readonly string <#=name#> = Url("<#=projectItem.Name#>");
  951. <#+}
  952. } #>
  953. <#+
  954. // Non folder items may also have children (virtual folders, Class.cs -> Class.Designer.cs, template output)
  955. // Just register them on the same path as their parent item
  956. foreach (ProjectItem item in projectItem.ProjectItems)
  957. {
  958. ProcessStaticFilesRecursive(item, path, childrenNameSet);
  959. }
  960. }
  961. }
  962. int BuildClassStructureForProvidedPath(string path)
  963. {
  964. var folders = path.Split(new char[] {'/', '~'}, StringSplitOptions.RemoveEmptyEntries);
  965. var parentFolder = String.Empty;
  966. var currentPath = "~";
  967. foreach(var folder in folders)
  968. {
  969. currentPath += "/" + folder;
  970. string className = EscapeID(Sanitize(folder));
  971. // If the folder name is the same as the parent, add a modifier to avoid class name conflicts
  972. // http://mvccontrib.codeplex.com/workitem/7153
  973. if (parentFolder == folder)
  974. {
  975. className += "_";
  976. }
  977. if(!virtualPathesForStaticFiles.Contains(currentPath))
  978. {
  979. virtualPathesForStaticFiles.Add(currentPath);#>
  980. [<#= GeneratedCode #>, DebuggerNonUserCode]
  981. public static partial class <#=className #> {
  982. private const string URLPATH = "<#=currentPath#>";
  983. public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
  984. public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
  985. <#+ } else {
  986. #>
  987. public static partial class <#=className #> {
  988. <#+ }
  989. PushIndent(" ");
  990. parentFolder = folder;
  991. }
  992. return folders.Length;
  993. }
  994. ProjectItem GetProjectItem(Project project, string name)
  995. {
  996. return GetProjectItem(project.ProjectItems, name);
  997. }
  998. ProjectItem GetProjectItem(ProjectItems items, string subPath)
  999. {
  1000. ProjectItem current = null;
  1001. foreach (string name in subPath.Split('\\'))
  1002. {
  1003. try
  1004. {
  1005. // ProjectItems.Item() throws when it doesn't exist, so catch the exception
  1006. // to return null instead.
  1007. current = items.Item(name);
  1008. }
  1009. catch
  1010. {
  1011. // If any chunk couldn't be found, fail
  1012. return null;
  1013. }
  1014. items = current.ProjectItems;
  1015. }
  1016. return current;
  1017. }
  1018. static bool IsController(CodeClass2 type)
  1019. {
  1020. // Ignore any class which name doesn't end with "Controller"
  1021. if (!type.FullName.EndsWith(ControllerSuffix)) return false;
  1022. for (; type.FullName != "System.Web.Mvc.Controller"; type = (CodeClass2)type.Bases.Item(1))
  1023. {
  1024. if (type.Bases.Count == 0)
  1025. return false;
  1026. }
  1027. return true;
  1028. }
  1029. static bool IsAsyncController(CodeClass2 type)
  1030. {
  1031. for (; type.FullName != "System.Web.Mvc.AsyncController"; type = (CodeClass2)type.Bases.Item(1))
  1032. {
  1033. if (type.Bases.Count == 0)
  1034. return false;
  1035. }
  1036. return true;
  1037. }
  1038. static string GetVirtualPath(ProjectItem item)
  1039. {
  1040. string fileFullPath = item.get_FileNames(0);
  1041. // Ignore files that are not under the app root (e.g. they could be linked files)
  1042. if (!fileFullPath.StartsWith(AppRoot, StringComparison.OrdinalIgnoreCase))
  1043. return null;
  1044. // Make a virtual path from the physical path
  1045. return "~/" + fileFullPath.Substring(AppRoot.Length).Replace('\\', '/');
  1046. }
  1047. static string ProcessAreaOrControllerName(string name)
  1048. {
  1049. return settings.UseLowercaseRoutes ? name.ToLowerInvariant() : name;
  1050. }
  1051. // Return all the CodeFunction2 in the CodeElements collection
  1052. static IEnumerable<CodeFunction2> GetMethods(CodeClass2 codeClass)
  1053. {
  1054. // Only look at regular method (e.g. ignore things like contructors)
  1055. return codeClass.Members.OfType<CodeFunction2>()
  1056. .Where(f => f.FunctionKind == vsCMFunction.vsCMFunctionFunction);
  1057. }
  1058. // Check if the class has any explicit constructor
  1059. static bool HasExplicitConstructor(CodeClass2 codeClass)
  1060. {
  1061. return codeClass.Members.OfType<CodeFunction2>().Any(
  1062. f => !f.IsShared && f.FunctionKind == vsCMFunction.vsCMFunctionConstructor);
  1063. }
  1064. // Check if the class has a default (i.e. no params) constructor
  1065. static bool HasExplicitDefaultConstructor(CodeClass2 codeClass)
  1066. {
  1067. return codeClass.Members.OfType<CodeFunction2>().Any(
  1068. f => !f.IsShared && f.FunctionKind == vsCMFunction.vsCMFunctionConstructor && f.Parameters.Count == 0);
  1069. }
  1070. // Find a method with a given name
  1071. static CodeFunction2 GetMethod(CodeClass2 codeClass, string name)
  1072. {
  1073. return GetMethods(codeClass).FirstOrDefault(f => f.Name == name);
  1074. }
  1075. // Find an attribute of a given type on an attribute collection
  1076. static CodeAttribute2 GetAttribute(CodeElements attributes, string attributeType)
  1077. {
  1078. for (int i = 1; i <= attributes.Count; i++)
  1079. {
  1080. try
  1081. {
  1082. var attrib = (CodeAttribute2)attributes.Item(i);
  1083. if (attributeType.Split(',').Contains(attrib.FullName, StringComparer.OrdinalIgnoreCase))
  1084. {
  1085. return attrib;
  1086. }
  1087. }
  1088. catch
  1089. {
  1090. // FullName can throw in some cases, so just ignore those attributes
  1091. continue;
  1092. }
  1093. }
  1094. return null;
  1095. }
  1096. static CodeAttribute2 GetAttribute(CodeClass2 type, string attributeType)
  1097. {
  1098. while(type != null) {
  1099. var attribute = GetAttribute(type.Attributes, attributeType);
  1100. if(attribute != null)
  1101. return attribute;
  1102. if (type.Bases.Count == 0)
  1103. return null;
  1104. type = (CodeClass2)type.Bases.Item(1);
  1105. }
  1106. return null;
  1107. }
  1108. static string UniqueFullName(CodeTypeRef codeType)
  1109. {
  1110. return UniqueFullName(codeType.CodeType);
  1111. }
  1112. static string UniqueFullName(CodeType codeType)
  1113. {
  1114. var uniqueName = codeType.FullName;
  1115. // Match characters not allowed in class names.
  1116. uniqueName = Regex.Replace(uniqueName, @"[^\p{Ll}\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\d]", "_");
  1117. // Remove duplicate '_' characters
  1118. uniqueName = Regex.Replace(uniqueName, @"__+", "_");
  1119. // Remove trailing '_' characters
  1120. uniqueName = uniqueName.TrimEnd('_');
  1121. return uniqueName;
  1122. }
  1123. // Return whether a ProjectItem is a folder and not a file
  1124. static bool IsFolder(ProjectItem item)
  1125. {
  1126. return (item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder);
  1127. }
  1128. static string MakeClassName(string ns, string classname)
  1129. {
  1130. return String.IsNullOrEmpty(ns) ? classname :
  1131. String.IsNullOrEmpty(classname) ? ns : ns + "." + codeProvider.CreateEscapedIdentifier(classname);
  1132. }
  1133. static string SanitizeWithNoConflicts(string token, HashSet<string> names)
  1134. {
  1135. string name = Sanitize(token);
  1136. while (names.Contains(name))
  1137. {
  1138. name += "_";
  1139. }
  1140. names.Add(name);
  1141. return name;
  1142. }
  1143. static string Sanitize(string token)
  1144. {
  1145. if (token == null) return null;
  1146. // Replace all invalid chars by underscores
  1147. token = Regex.Replace(token, @"[\W\b]", "_", RegexOptions.IgnoreCase);
  1148. // If it starts with a digit, prefix it with an underscore
  1149. token = Regex.Replace(token, @"^\d", @"_$0");
  1150. // Check for reserved…

Large files files are truncated, but you can click here to view the full file