PageRenderTime 44ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/mvcframework/RatCow.MvcFramework.Tools/ControllerCreationEngine.cs

http://ratcowsoftopensource.googlecode.com/
C# | 428 lines | 252 code | 78 blank | 98 comment | 17 complexity | 937e4ffadfa950c5e9a59cb7aba3dad6 MD5 | raw file
  1. /*
  2. * Copyright 2010 Rat Cow Software and Matt Emson. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without modification, are
  5. * permitted provided that the following conditions are met:
  6. *
  7. * 1. Redistributions of source code must retain the above copyright notice, this list of
  8. * conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright notice, this list
  10. * of conditions and the following disclaimer in the documentation and/or other materials
  11. * provided with the distribution.
  12. * 3. Neither the name of the Rat Cow Software nor the names of its contributors may be used
  13. * to endorse or promote products derived from this software without specific prior written
  14. * permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY RAT COW SOFTWARE "AS IS" AND ANY EXPRESS OR IMPLIED
  17. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  18. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
  19. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  22. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  23. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  24. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. *
  26. * The views and conclusions contained in the software and documentation are those of the
  27. * authors and should not be interpreted as representing official policies, either expressed
  28. * or implied, of Rat Cow Software and Matt Emson.
  29. *
  30. */
  31. using System;
  32. using System.Collections.Generic;
  33. using System.Linq;
  34. using System.Text;
  35. using System.CodeDom.Compiler;
  36. using System.Diagnostics;
  37. using System.IO;
  38. using Microsoft.CSharp;
  39. using System.Reflection;
  40. namespace RatCow.MvcFramework.Tools
  41. {
  42. /// <summary>
  43. /// Moved this in to a class library so that it can be reused
  44. /// </summary>
  45. public class ControllerCreationEngine
  46. {
  47. /// <summary>
  48. /// retain older interface
  49. /// </summary>
  50. /// <param name="className"></param>
  51. public static void Generate(string className)
  52. {
  53. Generate(className, false);
  54. }
  55. const string ABSTRACT_PREFIX = "Abstract";
  56. /// <summary>
  57. /// Split this up in to sections so I can more easliy re-use it in the future.
  58. /// </summary>
  59. /// <param name="className"></param>
  60. public static void Generate(string className, bool isAbstract)
  61. {
  62. string prefix = (isAbstract ? ABSTRACT_PREFIX : String.Empty);
  63. //this *can* generate more than one entry
  64. ControlTree[] trees = GenerateTree(className);
  65. if (trees != null && trees.Length == 1)
  66. {
  67. ControlTree tree = trees[0];
  68. string s = ClassGenerator(tree, isAbstract);
  69. string fileName = String.Format("{1}{0}Controller.cs", tree.ClassName, prefix);
  70. //added a check to see if file exists, otherwise we might get weird streaming issues
  71. //if it does, I delete it for now.
  72. if (File.Exists(fileName))
  73. File.Delete(fileName);
  74. TextWriter writer = new StreamWriter(File.OpenWrite(fileName));
  75. try
  76. {
  77. writer.WriteLine(s);
  78. }
  79. finally
  80. {
  81. writer.Close();
  82. }
  83. }
  84. }
  85. #region Deprecated
  86. /// <summary>
  87. /// This loads the generated assembly and gets the class info
  88. /// </summary>
  89. /// <param name="className"></param>
  90. public static void LegacyGenerate(string className)
  91. {
  92. Assembly a = Assembly.LoadFrom("temp.dll"); //we dictate this name
  93. Type[] ts = a.GetTypes();
  94. foreach (Type t in ts)
  95. {
  96. if (t.Name.Equals(className))
  97. {
  98. System.Console.WriteLine(t.FullName);
  99. System.Console.WriteLine(t.Name);
  100. System.Console.WriteLine(t.Module);
  101. System.Console.WriteLine(t.Assembly.FullName);
  102. //essentially, we only support "Winforms" so we assume it is a form or descends from one
  103. //I might be able to alter this to "component" I gues...
  104. System.Windows.Forms.Form form = (Activator.CreateInstance(t) as System.Windows.Forms.Form);
  105. StringBuilder code = new StringBuilder();
  106. code.AppendLine("/*Auto generated*/ \r\n\t\nusing System; \r\nusing System.Windows.Forms;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\n//3rd Party\r\nusing RatCow.MvcFramework;\r\n");
  107. code.AppendFormat("namespace {0}\r\n", t.Namespace);
  108. code.AppendLine("{");
  109. StringBuilder code_s1 = new StringBuilder();
  110. code_s1.AppendFormat("\tinternal partial class {0}Controller: BaseController<{0}>\r\n", t.Name);
  111. code_s1.AppendLine("\t{");
  112. //constructor
  113. code_s1.AppendFormat("\t\tpublic {0}Controller() : base()\r\n", t.Name);
  114. code_s1.AppendLine("\t\t{\r\n\t\t}\r\n");
  115. StringBuilder code_s2 = new StringBuilder();
  116. code_s2.AppendLine("\r\n#region GUI glue code\r\n");
  117. code_s2.AppendFormat("\tpartial class {0}Controller\r\n", t.Name);
  118. code_s2.AppendLine("\t{");
  119. //we now have access to the controls
  120. foreach (System.Windows.Forms.Control control in form.Controls)
  121. {
  122. //System.Console.WriteLine(" var {0} : {1} ", control.Name, control.GetType().Name);
  123. //add the declaration to code_s2
  124. code_s2.AppendFormat("\t\t[Outlet(\"{1}\")]\r\n\t\tpublic {0} {1} ", control.GetType().Name, control.Name); //add var
  125. code_s2.AppendLine("{ get; set; }");
  126. //add in the click handlers for buttons, coz I'm lazy like that
  127. if (control is System.Windows.Forms.Button)
  128. {
  129. code_s2.AppendFormat("\t\t[Action(\"{0}\", \"Click\")]\r\n\t\tpublic void F{0}_Click(object sender, EventArgs e)\r\n", control.Name);
  130. code_s2.AppendLine("\t\t{\r\n\t\t\t//Auto generated call");
  131. code_s1.AppendFormat("\t\tvoid {0}Click()\r\n", control.Name);
  132. code_s1.AppendLine("\t\t{\r\n");
  133. code_s1.AppendLine("\t\t}\r\n");
  134. code_s2.AppendFormat("\t\t\t{0}Click();\r\n", control.Name);
  135. code_s2.AppendLine("\t\t}\r\n");
  136. }
  137. }
  138. code_s1.AppendLine("\t}"); //end of class declaration
  139. code.AppendLine(code_s1.ToString());
  140. code_s2.AppendLine("\t}"); //end of class declaration
  141. code_s2.AppendLine("#endregion /*GUI glue code*/");
  142. code.AppendLine(code_s2.ToString());
  143. code.AppendLine("}");
  144. TextWriter writer = new StreamWriter(File.OpenWrite(String.Format("{0}Controller.cs", className)));
  145. try
  146. {
  147. writer.WriteLine(code.ToString());
  148. }
  149. finally
  150. {
  151. writer.Close();
  152. }
  153. System.Console.WriteLine(code.ToString());
  154. }
  155. }
  156. return;
  157. }
  158. #endregion
  159. /// <summary>
  160. /// Hacked mini compiler - it does enough to compile *basic* designer classes
  161. /// </summary>
  162. /// <param name="className"></param>
  163. /// <returns></returns>
  164. public static bool Compile(string className)
  165. {
  166. //we attempt to compile the file provided and then read info from it
  167. CSharpCodeProvider compiler = new CSharpCodeProvider();
  168. CompilerParameters compParams = new CompilerParameters();
  169. compParams.ReferencedAssemblies.Add("System.dll");
  170. compParams.ReferencedAssemblies.Add("System.XML.dll");
  171. compParams.ReferencedAssemblies.Add("System.Data.dll");
  172. compParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
  173. compParams.ReferencedAssemblies.Add("System.Drawing.dll");
  174. //we can add more in here from command line in future revision
  175. compParams.GenerateExecutable = false;
  176. compParams.CompilerOptions = "/t:library";
  177. compParams.OutputAssembly = "./temp.dll";
  178. StringBuilder s = new StringBuilder();
  179. string namespaceName = "";
  180. string fileToCompile = String.Format("{0}.Designer.cs", className);
  181. if (!File.Exists(fileToCompile)) return false;
  182. TextReader tr = new StreamReader(File.OpenRead(fileToCompile));
  183. try
  184. {
  185. //read till we find the namespace
  186. while (true)
  187. {
  188. string t = tr.ReadLine();
  189. s.Append(t);
  190. if (t.Contains("namespace"))
  191. {
  192. namespaceName = t.Substring(10).Trim();
  193. break;
  194. }
  195. }
  196. s.Append(tr.ReadToEnd());
  197. }
  198. finally
  199. {
  200. tr.Close();
  201. }
  202. //This is a bit of a hack... we need the designer to be a "Form" so we can compile it and
  203. //then use the Activator to access the contents later on. We *have* to call "InitializeComponents()"
  204. //otherwise, the form is in an uninitialized state and we will not have access to the parts we
  205. //actually *want*.
  206. string code = "{ public " + className + "(): base() { InitializeComponent();} } /*class*/ } /*namespace*/";
  207. string dummy = String.Format("namespace {2} {3} partial class {0} : System.Windows.Forms.Form {1}", className, code, namespaceName, "{");
  208. //The files to compile
  209. string[] files = { dummy, s.ToString() };
  210. CompilerResults res = null;
  211. try
  212. {
  213. res = compiler.CompileAssemblyFromSource(compParams, files);
  214. }
  215. catch (BadImageFormatException ex)
  216. {
  217. System.Console.WriteLine(ex.Message);
  218. return false;
  219. }
  220. catch (Exception ex)
  221. {
  222. System.Console.WriteLine(ex.Message);
  223. return false;
  224. }
  225. if (res.Errors.HasErrors)
  226. {
  227. StringBuilder sb = new StringBuilder();
  228. sb.Append("\nIllegal C# source code generated: ");
  229. sb.Append(res.Errors.Count.ToString());
  230. sb.Append(" Errors:\n");
  231. foreach (CompilerError error in res.Errors)
  232. {
  233. sb.Append("Line: ");
  234. sb.Append(error.Line.ToString());
  235. sb.Append(" - ");
  236. sb.Append(error.ErrorText);
  237. sb.Append('\n');
  238. }
  239. System.Console.WriteLine(sb.ToString());
  240. return false;
  241. }
  242. return true;
  243. }
  244. /// <summary>
  245. /// This is a version that builds a tree of info rather than directly creating the source
  246. /// </summary>
  247. /// <param name="className"></param>
  248. public static ControlTree[] GenerateTree(string className)
  249. {
  250. List<ControlTree> trees = new List<ControlTree>();
  251. Assembly a = Assembly.LoadFrom("temp.dll"); //we dictate this name
  252. Type[] ts = a.GetTypes();
  253. foreach (Type t in ts)
  254. {
  255. if (t.Name.Equals(className))
  256. {
  257. ControlTree tree = new ControlTree();
  258. trees.Add(tree); //just to get it sorted..
  259. //essentially, we only support "Winforms" so we assume it is a form or descends from one
  260. //I might be able to alter this to "component" I gues...
  261. System.Windows.Forms.Form form = (Activator.CreateInstance(t) as System.Windows.Forms.Form);
  262. tree.ClassName = t.Name;
  263. tree.NamespaceName = t.Namespace;
  264. //we now have access to the controls
  265. foreach (System.Windows.Forms.Control control in form.Controls)
  266. {
  267. tree.AddControl(control.Name, control.GetType());
  268. }
  269. }
  270. }
  271. return trees.ToArray();
  272. }
  273. /// <summary>
  274. /// maintain the public inteface.
  275. /// </summary>
  276. /// <param name="tree"></param>
  277. /// <returns></returns>
  278. public static string ClassGenerator(ControlTree tree)
  279. {
  280. return ClassGenerator(tree, false);
  281. }
  282. /// <summary>
  283. ///
  284. /// </summary>
  285. /// <param name="tree"></param>
  286. /// <param name="isAbstract"></param>
  287. /// <returns></returns>
  288. public static string ClassGenerator(ControlTree tree, bool isAbstract)
  289. {
  290. string prefix = (isAbstract ? ABSTRACT_PREFIX : String.Empty);
  291. StringBuilder code = new StringBuilder();
  292. code.AppendLine("/*Auto generated*/ \r\n\r\n\tusing System; \r\nusing System.Windows.Forms;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\n//3rd Party\r\nusing RatCow.MvcFramework;\r\n");
  293. code.AppendFormat("namespace {0}\r\n", tree.NamespaceName);
  294. code.AppendLine("{");
  295. StringBuilder code_s1 = new StringBuilder();
  296. code_s1.AppendFormat("\tinternal partial class {1}{0}Controller: BaseController<{0}>\r\n", tree.ClassName, prefix);
  297. code_s1.AppendLine("\t{");
  298. //constructor
  299. code_s1.AppendFormat("\t\tpublic {1}{0}Controller() : base()\r\n", tree.ClassName, prefix);
  300. code_s1.AppendLine("\t\t{\r\n\t\t}\r\n");
  301. StringBuilder code_s2 = new StringBuilder();
  302. code_s2.AppendLine("\r\n#region GUI glue code\r\n");
  303. code_s2.AppendFormat("\tpartial class {1}{0}Controller\r\n", tree.ClassName, prefix);
  304. code_s2.AppendLine("\t{");
  305. //this is better off hardcoded for now at the BaseController level
  306. ////add in "form_load" event
  307. //code_s2.AppendLine();
  308. //code_s2.AppendLine("\t\t[Action(\"View\", \"Load\")]\r\n\t\tprivate void View_Load(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\tViewLoad();\r\n\t\t}\r\n");
  309. //code_s1.AppendLine("\t\tprotected virtual void ViewLoad()"); //added "protected virtual" so that I can descend and not have to alter this class at all.
  310. //code_s1.AppendLine("\t\t{\r\n");
  311. //code_s1.AppendLine("\t\t}\r\n");
  312. //we now have access to the controls
  313. foreach (var control in tree.Controls)
  314. {
  315. //System.Console.WriteLine(" var {0} : {1} ", control.Name, control.GetType().Name);
  316. //add the declaration to code_s2
  317. code_s2.AppendFormat("\t\t[Outlet(\"{1}\")]\r\n\t\tpublic {0} {1} ", control.Value.Name, control.Key); //add var
  318. code_s2.AppendLine("{ get; set; }");
  319. //add in the click handlers for buttons, coz I'm lazy like that
  320. if (control.Value == typeof(System.Windows.Forms.Button))
  321. {
  322. code_s2.AppendFormat("\t\t[Action(\"{0}\", \"Click\")]\r\n\t\tpublic void F{0}_Click(object sender, EventArgs e)\r\n", control.Key);
  323. code_s2.AppendLine("\t\t{\r\n\t\t\t//Auto generated call");
  324. code_s1.AppendFormat("\t\tprotected virtual void {0}Click()\r\n", control.Key); //added "protected virtual" so that I can descend and not have to alter this class at all.
  325. code_s1.AppendLine("\t\t{\r\n");
  326. code_s1.AppendLine("\t\t}\r\n");
  327. code_s2.AppendFormat("\t\t\t{0}Click();\r\n", control.Key);
  328. code_s2.AppendLine("\t\t}\r\n");
  329. }
  330. else if (control.Value == typeof(System.Windows.Forms.CheckBox))
  331. {
  332. code_s2.AppendFormat("\t\t[Action(\"{0}\", \"Click\")]\r\n\t\tpublic void F{0}_Click(object sender, EventArgs e)\r\n", control.Key);
  333. code_s2.AppendLine("\t\t{\r\n\t\t\t//Auto generated call");
  334. code_s1.AppendFormat("\t\tprotected virtual void {0}Click()\r\n", control.Key); //added "protected virtual" so that I can descend and not have to alter this class at all.
  335. code_s1.AppendLine("\t\t{\r\n");
  336. code_s1.AppendLine("\t\t}\r\n");
  337. code_s2.AppendFormat("\t\t\t{0}Click();\r\n", control.Key);
  338. code_s2.AppendLine("\t\t}\r\n");
  339. }
  340. }
  341. code_s1.AppendLine("\t}"); //end of class declaration
  342. code.AppendLine(code_s1.ToString());
  343. code_s2.AppendLine("\t}"); //end of class declaration
  344. code_s2.AppendLine("#endregion /*GUI glue code*/");
  345. code.AppendLine(code_s2.ToString());
  346. code.AppendLine("}");
  347. return code.ToString();
  348. }
  349. }
  350. }