PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.CodeGeneration/CodeGenerator.cs

http://github.com/mono/monodevelop
C# | 340 lines | 271 code | 41 blank | 28 comment | 65 complexity | f9ce92e6d484c67302e1ae2cae154991 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0, CC-BY-SA-3.0, MIT, LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. //
  2. // CodeGenerator.cs
  3. //
  4. // Author:
  5. // Mike Kr?ger <mkrueger@novell.com>
  6. //
  7. // Copyright (c) 2010 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. using System;
  27. using MonoDevelop.Projects.Dom.Parser;
  28. using MonoDevelop.Projects.Dom;
  29. using System.Text;
  30. using System.Collections.Generic;
  31. using System.Linq;
  32. using Mono.Addins;
  33. namespace MonoDevelop.Projects.CodeGeneration
  34. {
  35. public abstract class CodeGenerator
  36. {
  37. static Dictionary<string, MimeTypeExtensionNode> generators = new Dictionary<string, MimeTypeExtensionNode> ();
  38. public bool UseSpaceIndent {
  39. get;
  40. set;
  41. }
  42. public string EolMarker {
  43. get;
  44. set;
  45. }
  46. public int TabSize {
  47. get;
  48. set;
  49. }
  50. public static CodeGenerator CreateGenerator (string mimeType, bool useSpaceIndent, int tabSize, string eolMarker)
  51. {
  52. MimeTypeExtensionNode node;
  53. if (!generators.TryGetValue (mimeType, out node))
  54. return null;
  55. if (eolMarker == null)
  56. throw new ArgumentNullException ("eolMarker");
  57. if (eolMarker.Length == 0 || eolMarker.Length > 2)
  58. throw new ArgumentException ("invalid eolMarker");
  59. if (tabSize <= 0)
  60. throw new ArgumentException ("tabSize <= 0");
  61. var result = (CodeGenerator)node.CreateInstance ();
  62. result.UseSpaceIndent = useSpaceIndent;
  63. result.EolMarker = eolMarker;
  64. result.TabSize = tabSize;
  65. return result;
  66. }
  67. protected void AppendLine (StringBuilder sb)
  68. {
  69. sb.Append (EolMarker);
  70. }
  71. protected string GetIndent (int indentLevel)
  72. {
  73. if (UseSpaceIndent)
  74. return new string (' ', indentLevel * TabSize);
  75. return new string ('\t', indentLevel);
  76. }
  77. public static bool HasGenerator (string mimeType)
  78. {
  79. return generators.ContainsKey (mimeType);
  80. }
  81. static CodeGenerator ()
  82. {
  83. AddinManager.AddExtensionNodeHandler ("/MonoDevelop/ProjectModel/CodeGenerators", delegate (object sender, ExtensionNodeEventArgs args) {
  84. var node = (MimeTypeExtensionNode)args.ExtensionNode;
  85. switch (args.Change) {
  86. case ExtensionChange.Add:
  87. AddGenerator (node);
  88. break;
  89. case ExtensionChange.Remove:
  90. RemoveGenerator (node);
  91. break;
  92. }
  93. });
  94. }
  95. public int IndentLevel {
  96. get;
  97. set;
  98. }
  99. public CodeGenerator ()
  100. {
  101. IndentLevel = -1;
  102. }
  103. public static void AddGenerator (MimeTypeExtensionNode node)
  104. {
  105. generators[node.MimeType] = node;
  106. }
  107. public static void RemoveGenerator (MimeTypeExtensionNode node)
  108. {
  109. generators.Remove (node.MimeType);
  110. }
  111. static int CalculateBodyIndentLevel (IType declaringType)
  112. {
  113. int indentLevel = 0;
  114. IType t = declaringType;
  115. do {
  116. indentLevel++;
  117. t = t.DeclaringType;
  118. } while (t != null);
  119. DomLocation lastLoc = DomLocation.Empty;
  120. foreach (IUsing us in declaringType.CompilationUnit.Usings.Where (u => u.IsFromNamespace && u.ValidRegion.Contains (declaringType.Location))) {
  121. if (lastLoc == us.Region.Start)
  122. continue;
  123. lastLoc = us.Region.Start;
  124. indentLevel++;
  125. }
  126. return indentLevel;
  127. }
  128. protected void SetIndentTo (IType implementingType)
  129. {
  130. if (IndentLevel < 0)
  131. IndentLevel = CalculateBodyIndentLevel (implementingType);
  132. }
  133. public string CreateInterfaceImplementation (IType implementingType, IType interfaceType, bool explicitly, bool wrapRegions = true)
  134. {
  135. SetIndentTo (implementingType);
  136. StringBuilder result = new StringBuilder ();
  137. List<IMember> implementedMembers = new List<IMember> ();
  138. foreach (IType baseInterface in interfaceType.SourceProjectDom.GetInheritanceTree (interfaceType)) {
  139. if (baseInterface.ClassType != ClassType.Interface)
  140. continue;
  141. if (result.Length > 0) {
  142. AppendLine (result);
  143. AppendLine (result);
  144. }
  145. string implementation = InternalCreateInterfaceImplementation (implementingType, baseInterface, explicitly, implementedMembers);
  146. if (string.IsNullOrWhiteSpace (implementation))
  147. continue;
  148. if (wrapRegions) {
  149. result.Append (WrapInRegions (baseInterface.Name + " implementation", implementation));
  150. } else {
  151. result.Append (implementation);
  152. }
  153. }
  154. return result.ToString ();
  155. }
  156. static bool CompareParameters (System.Collections.ObjectModel.ReadOnlyCollection<MonoDevelop.Projects.Dom.IParameter> parameters1, System.Collections.ObjectModel.ReadOnlyCollection<MonoDevelop.Projects.Dom.IParameter> parameters2)
  157. {
  158. if (parameters1.Count != parameters2.Count)
  159. return false;
  160. for (int i = 0; i < parameters1.Count; i++) {
  161. var p1 = parameters1 [i];
  162. var p2 = parameters2 [i];
  163. if (p1.ReturnType.ToInvariantString () != p2.ReturnType.ToInvariantString ())
  164. return false;
  165. }
  166. return true;
  167. }
  168. protected string InternalCreateInterfaceImplementation (IType implementingType, IType interfaceType, bool explicitly, List<IMember> implementedMembers)
  169. {
  170. StringBuilder result = new StringBuilder ();
  171. ProjectDom dom = implementingType.SourceProjectDom;
  172. List<KeyValuePair<IMember, bool >> toImplement = new List<KeyValuePair<IMember, bool>> ();
  173. bool alreadyImplemented;
  174. // Stub out non-implemented events defined by @iface
  175. foreach (IEvent ev in interfaceType.Events) {
  176. if (ev.IsSpecialName)
  177. continue;
  178. bool needsExplicitly = explicitly;
  179. alreadyImplemented = dom.GetInheritanceTree (implementingType).Any (x => x.ClassType != ClassType.Interface && x.Events.Any (y => y.Name == ev.Name));
  180. if (!alreadyImplemented)
  181. toImplement.Add (new KeyValuePair<IMember, bool> (ev, needsExplicitly));
  182. }
  183. // Stub out non-implemented methods defined by @iface
  184. foreach (IMethod method in interfaceType.Methods) {
  185. if (method.IsSpecialName)
  186. continue;
  187. bool needsExplicitly = explicitly;
  188. alreadyImplemented = false;
  189. foreach (IType t in dom.GetInheritanceTree (implementingType)) {
  190. if (t.ClassType == ClassType.Interface)
  191. continue;
  192. foreach (IMethod cmet in t.Methods) {
  193. if (cmet.Name == method.Name && CompareParameters (cmet.Parameters, method.Parameters)) {
  194. if (!needsExplicitly && !cmet.ReturnType.Equals (method.ReturnType))
  195. needsExplicitly = true;
  196. else
  197. alreadyImplemented |= !needsExplicitly || (interfaceType.FullName == GetExplicitPrefix (cmet.ExplicitInterfaces));
  198. }
  199. }
  200. }
  201. if (!alreadyImplemented)
  202. toImplement.Add (new KeyValuePair<IMember, bool> (method, needsExplicitly));
  203. }
  204. // Stub out non-implemented properties defined by @iface
  205. foreach (IProperty prop in interfaceType.Properties) {
  206. if (prop.IsSpecialName)
  207. continue;
  208. bool needsExplicitly = explicitly;
  209. alreadyImplemented = false;
  210. foreach (IType t in dom.GetInheritanceTree (implementingType)) {
  211. if (t.ClassType == ClassType.Interface)
  212. continue;
  213. foreach (IProperty cprop in t.Properties) {
  214. if (cprop.Name == prop.Name) {
  215. if (!needsExplicitly && !cprop.ReturnType.Equals (prop.ReturnType))
  216. needsExplicitly = true;
  217. else
  218. alreadyImplemented |= !needsExplicitly || (interfaceType.FullName == GetExplicitPrefix (cprop.ExplicitInterfaces));
  219. }
  220. }
  221. }
  222. if (!alreadyImplemented)
  223. toImplement.Add (new KeyValuePair<IMember, bool> (prop, needsExplicitly));
  224. }
  225. bool first = true;
  226. foreach (var pair in toImplement) {
  227. if (!first) {
  228. AppendLine (result);
  229. AppendLine (result);
  230. } else {
  231. first = false;
  232. }
  233. bool isExplicit = pair.Value;
  234. foreach (IMember member in implementedMembers.Where (m => m.Name == pair.Key.Name && m.MemberType == pair.Key.MemberType)) {
  235. if (member.MemberType == MemberType.Method) {
  236. isExplicit = member.ReturnType.ToInvariantString () != pair.Key.ReturnType.ToInvariantString () && CompareParameters (member.Parameters, pair.Key.Parameters);
  237. } else {
  238. isExplicit = true;
  239. }
  240. }
  241. result.Append (CreateMemberImplementation (implementingType, pair.Key, isExplicit).Code);
  242. implementedMembers.Add (pair.Key);
  243. }
  244. return result.ToString ();
  245. }
  246. static string GetExplicitPrefix (IEnumerable<IReturnType> explicitInterfaces)
  247. {
  248. if (explicitInterfaces != null) {
  249. foreach (IReturnType retType in explicitInterfaces) {
  250. return retType.FullName;
  251. }
  252. }
  253. return null;
  254. }
  255. public abstract string WrapInRegions (string regionName, string text);
  256. public abstract CodeGeneratorMemberResult CreateMemberImplementation (IType implementingType, IMember member, bool explicitDeclaration);
  257. public abstract string CreateFieldEncapsulation (IType implementingType, IField field, string propertyName, Modifiers modifiers, bool readOnly);
  258. }
  259. public class CodeGeneratorMemberResult
  260. {
  261. public CodeGeneratorMemberResult (string code) : this (code, null)
  262. {
  263. }
  264. public CodeGeneratorMemberResult (string code, int bodyStartOffset, int bodyEndOffset)
  265. {
  266. this.Code = code;
  267. this.BodyRegions = new CodeGeneratorBodyRegion[] {
  268. new CodeGeneratorBodyRegion (bodyStartOffset, bodyEndOffset)
  269. };
  270. }
  271. public CodeGeneratorMemberResult (string code, IList<CodeGeneratorBodyRegion> bodyRegions)
  272. {
  273. this.Code = code;
  274. this.BodyRegions = bodyRegions ?? new CodeGeneratorBodyRegion[0];
  275. }
  276. public string Code { get; private set; }
  277. public IList<CodeGeneratorBodyRegion> BodyRegions { get; private set; }
  278. }
  279. public class CodeGeneratorBodyRegion
  280. {
  281. public CodeGeneratorBodyRegion (int startOffset, int endOffset)
  282. {
  283. this.StartOffset = startOffset;
  284. this.EndOffset = endOffset;
  285. }
  286. public int StartOffset { get; private set; }
  287. public int EndOffset { get; private set; }
  288. public int Length {
  289. get {
  290. return EndOffset - StartOffset;
  291. }
  292. }
  293. public bool IsValid {
  294. get {
  295. return StartOffset >= 0 && Length >= 0;
  296. }
  297. }
  298. }
  299. }