PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/CodeBinder.cs

http://github.com/mono/monodevelop
C# | 352 lines | 220 code | 53 blank | 79 comment | 57 complexity | 0185f08312fe5da1d7bf3e50dd9aba69 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. // CodeBinder.cs
  3. //
  4. // Author:
  5. // Lluis Sanchez Gual
  6. //
  7. // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System.CodeDom;
  29. using System.Collections.Generic;
  30. using System.Linq;
  31. using MonoDevelop.Core;
  32. using MonoDevelop.Core.ProgressMonitoring;
  33. using MonoDevelop.Projects;
  34. using MonoDevelop.Projects.Text;
  35. using MonoDevelop.Ide.Gui;
  36. using MonoDevelop.GtkCore.Dialogs;
  37. using MonoDevelop.Ide;
  38. using MonoDevelop.Ide.TypeSystem;
  39. using MonoDevelop.Ide.FindInFiles;
  40. using Microsoft.CodeAnalysis;
  41. using Microsoft.CodeAnalysis.CSharp;
  42. using Microsoft.CodeAnalysis.CSharp.Syntax;
  43. using ICSharpCode.NRefactory6.CSharp;
  44. using MonoDevelop.CSharp.Refactoring;
  45. using MonoDevelop.Refactoring;
  46. using System.Xml.XPath;
  47. using System.IO;
  48. using System.Threading.Tasks;
  49. namespace MonoDevelop.GtkCore.GuiBuilder
  50. {
  51. /// This class provides several methods for managing the relation
  52. /// between an object (e.g. a window) and the source code that will implement the
  53. /// code for that object.
  54. ///
  55. /// Once created, a CodeBinder object will keep track of the class bound to the
  56. /// object. If the class is renamed, it will properly update the object name.
  57. public class CodeBinder
  58. {
  59. ITextFileProvider textFileProvider;
  60. Stetic.Component targetObject;
  61. MonoDevelop.Projects.Project project;
  62. GuiBuilderProject gproject;
  63. string className;
  64. string classFile;
  65. public CodeBinder (MonoDevelop.Projects.Project project, ITextFileProvider textFileProvider, Stetic.Component targetObject)
  66. {
  67. this.project = project;
  68. this.textFileProvider = textFileProvider;
  69. gproject = GtkDesignInfo.FromProject (project).GuiBuilderProject;
  70. TargetObject = targetObject;
  71. }
  72. public Stetic.Component TargetObject {
  73. get { return targetObject; }
  74. set {
  75. this.targetObject = value;
  76. if (targetObject != null) {
  77. var cls = gproject.FindClass (GetClassName (targetObject));
  78. if (cls != null) {
  79. className = cls.GetFullName ();
  80. classFile = cls.Locations.First ().SourceTree.FilePath;
  81. }
  82. }
  83. }
  84. }
  85. /// Synchronizes the bindings between the object and the source code
  86. public void UpdateBindings (string fileName)
  87. {
  88. if (targetObject == null)
  89. return;
  90. classFile = fileName;
  91. var cls = GetClass ();
  92. if (cls != null) {
  93. UpdateBindings (targetObject, cls);
  94. targetObject.GeneratePublic = cls.DeclaredAccessibility == Accessibility.Public;
  95. }
  96. }
  97. void UpdateBindings (Stetic.Component obj, ITypeSymbol cls)
  98. {
  99. if (targetObject == null || cls == null)
  100. return;
  101. // Remove signals for which there isn't a handler in the class
  102. Stetic.SignalCollection objectSignals = obj.GetSignals ();
  103. if (objectSignals != null) {
  104. Stetic.Signal[] signals = new Stetic.Signal [objectSignals.Count];
  105. objectSignals.CopyTo (signals, 0);
  106. foreach (Stetic.Signal signal in signals) {
  107. if (FindSignalHandler (cls, signal) == null) {
  108. obj.RemoveSignal (signal);
  109. }
  110. }
  111. }
  112. // Update children
  113. foreach (Stetic.Component ob in obj.GetChildren ())
  114. UpdateBindings (ob, cls);
  115. }
  116. static IMethodSymbol FindSignalHandler (ITypeSymbol cls, Stetic.Signal signal)
  117. {
  118. return cls.GetMembers (signal.Handler).OfType<IMethodSymbol> ().FirstOrDefault ();
  119. }
  120. public async Task UpdateField (Stetic.Component obj, string oldName)
  121. {
  122. if (targetObject == null)
  123. return;
  124. if (obj == targetObject)
  125. return; // The root widget name can only be changed internally.
  126. var cls = GetClass (false);
  127. string newName = GetObjectName (obj);
  128. if (newName.Length == 0)
  129. return;
  130. if (cls != null) {
  131. var f = ClassUtils.FindWidgetField (cls, oldName);
  132. if (f != null) {
  133. await MonoDevelop.Refactoring.Rename.RenameRefactoring.Rename (f, newName);
  134. }
  135. }
  136. }
  137. /// Adds a signal handler to the class
  138. public void BindSignal (Stetic.Signal signal)
  139. {
  140. if (targetObject == null)
  141. return;
  142. var cls = GetClass ();
  143. if (cls == null)
  144. return;
  145. if (FindSignalHandler (cls, signal) != null)
  146. return;
  147. var met = SyntaxFactory.MethodDeclaration (SyntaxFactory.ParseTypeName (signal.SignalDescriptor.HandlerReturnTypeName),
  148. signal.Handler)
  149. .WithBody (SyntaxFactory.Block ())
  150. .AddModifiers (SyntaxFactory.Token (SyntaxKind.ProtectedKeyword));
  151. var parameters = new List<ParameterSyntax> ();
  152. foreach (Stetic.ParameterDescriptor pinfo in signal.SignalDescriptor.HandlerParameters)
  153. parameters.Add (SyntaxFactory.Parameter (new SyntaxList<AttributeListSyntax> (), new SyntaxTokenList (), SyntaxFactory.ParseTypeName (pinfo.TypeName), SyntaxFactory.Identifier (pinfo.Name), null));
  154. met = met.AddParameterListParameters (parameters.ToArray ());
  155. CodeGenerationService.AddNewMember (project, cls, GetSourceLocation (cls), met);
  156. }
  157. static Location GetSourceLocation (INamedTypeSymbol cls)
  158. {
  159. foreach (var loc in cls.Locations) {
  160. if (loc.IsInSource) {
  161. if (!Path.GetDirectoryName (loc.SourceTree.FilePath).EndsWith ("gtk-gui", FilePath.PathComparison))
  162. return loc;
  163. }
  164. }
  165. return cls.Locations.First ();
  166. }
  167. public async Task UpdateSignal (Stetic.Signal oldSignal, Stetic.Signal newSignal)
  168. {
  169. if (targetObject == null)
  170. return;
  171. if (oldSignal.Handler == newSignal.Handler)
  172. return;
  173. var cls = GetClass ();
  174. if (cls == null)
  175. return;
  176. var met = FindSignalHandler (cls, oldSignal);
  177. if (met == null)
  178. return;
  179. await MonoDevelop.Refactoring.Rename.RenameRefactoring.Rename (met, newSignal.Handler);
  180. }
  181. /// Adds a field to the class
  182. public async Task BindToField (Stetic.Component obj)
  183. {
  184. if (targetObject == null)
  185. return;
  186. string name = GetMemberName (obj);
  187. var cls = GetClass ();
  188. if (FindField (cls, name) != null)
  189. return;
  190. var location = GetSourceLocation(cls);
  191. var doc = await IdeApp.Workbench.OpenDocument (location.SourceTree.FilePath, project, true);
  192. var editor = doc.Editor;
  193. if (editor != null) {
  194. await CodeGenerationService.AddNewMember (project, cls, cls.Locations.First (), GetFieldCode (cls, obj, name));
  195. }
  196. }
  197. FieldDeclarationSyntax GetFieldCode (ITypeSymbol cls, Stetic.Component obj, string name)
  198. {
  199. return SyntaxFactory.FieldDeclaration (
  200. SyntaxFactory.VariableDeclaration (
  201. SyntaxFactory.ParseTypeName (obj.Type.ClassName),
  202. new SeparatedSyntaxList<VariableDeclaratorSyntax> {
  203. SyntaxFactory.VariableDeclarator (name)
  204. }
  205. )
  206. ).AddModifiers (SyntaxFactory.Token (SyntaxKind.ProtectedKeyword));
  207. }
  208. static IFieldSymbol FindField (ITypeSymbol cls, string name)
  209. {
  210. return cls
  211. .GetMembers (name)
  212. .OfType<IFieldSymbol> ()
  213. .FirstOrDefault ();
  214. }
  215. public INamedTypeSymbol GetClass ()
  216. {
  217. return GetClass (true);
  218. }
  219. public INamedTypeSymbol GetClass (bool getUserClass)
  220. {
  221. if (targetObject == null)
  222. return null;
  223. var cls = gproject.FindClass (className, getUserClass);
  224. if (cls != null)
  225. return cls;
  226. // The class name may have changed. Try to guess the new name.
  227. // TODO (roslyn port) - is that really required ?
  228. // var matches = new List<INamedTypeSymbol> ();
  229. // ParsedDocument unit = null;
  230. // var ctx = gproject.GetParserContext ();
  231. // var doc = IdeApp.TypeSystemService.ParseFile (project, classFile);
  232. // if (doc != null) {
  233. // unit = doc;
  234. // foreach (var fcls in unit.TopLevelTypeDefinitions) {
  235. // if (IsValidClass (fcls, targetObject))
  236. // matches.Add (fcls);
  237. // }
  238. // }
  239. //
  240. // // If found the class, just return it
  241. // if (matches.Count == 1) {
  242. // cls = matches [0];
  243. // className = cls.GetFullName ();
  244. // targetObject.Name = className;
  245. // gproject.SaveWindow (true, targetObject.Name);
  246. // return cls;
  247. // }
  248. //
  249. // // If not found, warn the user.
  250. //
  251. // if (unit != null && unit.TopLevelTypeDefinitions.Count > 0) {
  252. // using (var dialog = new SelectRenamedClassDialog (unit.TopLevelTypeDefinitions.Select (c => c.Resolve (project)))) {
  253. // if (dialog.Run ()) {
  254. // className = dialog.SelectedClass;
  255. // if (className == null)
  256. // return null;
  257. // else {
  258. // targetObject.Name = className;
  259. // gproject.SaveWindow (true, targetObject.Name);
  260. // return gproject.FindClass (className);
  261. // }
  262. // }
  263. // }
  264. // } else {
  265. // MessageService.ShowError (GettextCatalog.GetString ("The class bound to the component '{0}' could not be found. This may be due to syntax errors in the source code file.", GetObjectName(targetObject)));
  266. // }
  267. return null;
  268. }
  269. static bool IsValidClass (ITypeSymbol cls, Stetic.Component obj)
  270. {
  271. if (cls.BaseType.SpecialType == SpecialType.System_Object)
  272. return false;
  273. string typeName = obj.Type.ClassName;
  274. if (cls.BaseType.GetFullName () == typeName)
  275. return true;
  276. return IsValidClass (cls.BaseType, obj);
  277. }
  278. internal static string GetClassName (Stetic.Component obj)
  279. {
  280. return GetObjectName (obj);
  281. }
  282. internal static string GetMemberName (Stetic.Component obj)
  283. {
  284. return obj.Name;
  285. }
  286. internal static string GetObjectName (Stetic.Component obj)
  287. {
  288. return obj.Name;
  289. }
  290. internal static string GetClassName (Stetic.ProjectItemInfo obj)
  291. {
  292. return GetObjectName (obj);
  293. }
  294. internal static string GetObjectName (Stetic.ProjectItemInfo obj)
  295. {
  296. return obj.Name;
  297. }
  298. }
  299. }