PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/AddIns/Misc/SharpRefactoring/Project/Src/PropertyRefactoringMenuBuilder.cs

http://github.com/icsharpcode/SharpDevelop
C# | 268 lines | 212 code | 54 blank | 2 comment | 55 complexity | 8a53b73969eb8dfd88f66d9b1ef12288 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CPL-1.0, LGPL-2.1
  1. // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2. // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Windows.Documents;
  8. using ICSharpCode.AvalonEdit.Document;
  9. using ICSharpCode.Core.Presentation;
  10. using ICSharpCode.Core.WinForms;
  11. using ICSharpCode.NRefactory;
  12. using ICSharpCode.NRefactory.Ast;
  13. using ICSharpCode.SharpDevelop;
  14. using ICSharpCode.SharpDevelop.Bookmarks;
  15. using ICSharpCode.SharpDevelop.Dom;
  16. using ICSharpCode.SharpDevelop.Dom.Refactoring;
  17. using ICSharpCode.SharpDevelop.Editor;
  18. using ICSharpCode.SharpDevelop.Gui;
  19. using ICSharpCode.SharpDevelop.Refactoring;
  20. using Ast = ICSharpCode.NRefactory.Ast;
  21. namespace SharpRefactoring
  22. {
  23. public class PropertyRefactoringMenuBuilder : ISubmenuBuilder, IMenuItemBuilder
  24. {
  25. public System.Collections.ICollection BuildItems(ICSharpCode.Core.Codon codon, object owner)
  26. {
  27. return BuildSubmenu(codon, owner).TranslateToWpf();
  28. }
  29. public System.Windows.Forms.ToolStripItem[] BuildSubmenu(ICSharpCode.Core.Codon codon, object owner)
  30. {
  31. List<System.Windows.Forms.ToolStripItem> items = new List<System.Windows.Forms.ToolStripItem>();
  32. MenuCommand cmd;
  33. IProperty property;
  34. if (owner is ICSharpCode.SharpDevelop.Gui.ClassBrowser.MemberNode)
  35. property = ((ICSharpCode.SharpDevelop.Gui.ClassBrowser.MemberNode)owner).Member as IProperty;
  36. else if (owner is ClassMemberBookmark)
  37. property = ((ClassMemberBookmark)owner).Member as IProperty;
  38. else
  39. property = null;
  40. if (property == null)
  41. return items.ToArray();
  42. ITextEditor editor = FindReferencesAndRenameHelper.OpenDefinitionFile(property, false);
  43. string field = null;
  44. PropertyDeclaration astProp = null;
  45. if (property.IsAutoImplemented()) {
  46. cmd = new MenuCommand("${res:SharpDevelop.Refactoring.ExpandAutomaticProperty}", ExpandAutomaticProperty);
  47. cmd.Tag = property;
  48. items.Add(cmd);
  49. } else if (IsSimpleProperty(editor, property, out astProp, out field)) {
  50. cmd = new MenuCommand("${res:SharpDevelop.Refactoring.ConvertToAutomaticProperty}",
  51. delegate {
  52. IField fieldDef = property.DeclaringType.Fields.FirstOrDefault(f => f.Name == field);
  53. if (fieldDef == null)
  54. return;
  55. ConvertToAutomaticProperty(editor, property, fieldDef, astProp);
  56. }
  57. );
  58. cmd.Tag = property;
  59. items.Add(cmd);
  60. }
  61. return items.ToArray();
  62. }
  63. void ExpandAutomaticProperty(object sender, EventArgs e)
  64. {
  65. MenuCommand item = (MenuCommand)sender;
  66. IProperty member = (IProperty)item.Tag;
  67. ITextEditor textEditor = FindReferencesAndRenameHelper.JumpToDefinition(member);
  68. if (textEditor != null) {
  69. CodeGenerator codeGen = member.DeclaringType.ProjectContent.Language.CodeGenerator;
  70. IField field = new DefaultField(member.ReturnType, codeGen.GetFieldName(member.Name),
  71. member.Modifiers & ModifierEnum.Static, DomRegion.Empty, member.DeclaringType);
  72. ClassFinder finder = new ClassFinder(member);
  73. Ast.PropertyDeclaration p = codeGen.CreateProperty(field, member.CanGet, member.CanSet);
  74. p.Modifier = CodeGenerator.ConvertModifier(member.Modifiers, finder);
  75. PropertyDeclaration oldProp = ParseMember<PropertyDeclaration>(Path.GetExtension(textEditor.FileName), GetMemberText(member, textEditor));
  76. if (member.CanGet)
  77. p.GetRegion.Modifier = CodeGenerator.ConvertModifier(member.GetterModifiers, finder);
  78. if (member.CanSet)
  79. p.SetRegion.Modifier = CodeGenerator.ConvertModifier(member.SetterModifiers, finder);
  80. int startOffset = textEditor.Document.PositionToOffset(member.Region.BeginLine, member.Region.BeginColumn);
  81. int endOffset = textEditor.Document.PositionToOffset(member.Region.EndLine, member.Region.EndColumn);
  82. if (!member.BodyRegion.IsEmpty)
  83. endOffset = textEditor.Document.PositionToOffset(member.BodyRegion.EndLine, member.BodyRegion.EndColumn);
  84. FieldDeclaration f = CodeGenerator.ConvertMember(field, finder);
  85. f.Fields[0].Initializer = oldProp.Initializer;
  86. using (textEditor.Document.OpenUndoGroup()) {
  87. textEditor.Document.Remove(startOffset, endOffset - startOffset);
  88. if (codeGen.Options.EmptyLinesBetweenMembers) {
  89. var line = textEditor.Document.GetLine(member.Region.BeginLine);
  90. textEditor.Document.Remove(line.Offset, line.TotalLength);
  91. }
  92. codeGen.InsertCodeInClass(member.DeclaringType, new RefactoringDocumentAdapter(textEditor.Document), member.Region.BeginLine - 1, f, p);
  93. }
  94. ParserService.ParseCurrentViewContent();
  95. }
  96. }
  97. #region ConvertToAutomaticProperty
  98. bool IsSimpleProperty(ITextEditor editor, IProperty property, out Ast.PropertyDeclaration astProperty, out string fieldName)
  99. {
  100. astProperty = null;
  101. fieldName = null;
  102. if (editor == null)
  103. return false;
  104. string ext = Path.GetExtension(editor.FileName);
  105. string code = GetMemberText(property, editor);
  106. Ast.PropertyDeclaration p = ParseMember<Ast.PropertyDeclaration>(ext, code);
  107. if (p == null)
  108. return false;
  109. bool getResult = true;
  110. bool setResult = true;
  111. string identifier = null;
  112. if (p.HasGetRegion) {
  113. getResult = false;
  114. var ret = p.GetRegion.Block.Children.FirstOrDefault() as Ast.ReturnStatement;
  115. if (ret != null) {
  116. getResult = ret.Expression is Ast.IdentifierExpression;
  117. identifier = getResult ? (ret.Expression as Ast.IdentifierExpression).Identifier : null;
  118. }
  119. }
  120. if (p.HasSetRegion) {
  121. setResult = false;
  122. var ret = p.SetRegion.Block.Children.FirstOrDefault() as Ast.ExpressionStatement;
  123. if (ret != null && p.SetRegion.Block.Children.Count == 1 && ret.Expression is Ast.AssignmentExpression) {
  124. var assign = ret.Expression as Ast.AssignmentExpression;
  125. setResult = assign.Op == Ast.AssignmentOperatorType.Assign &&
  126. IsValidMemberAssignment(assign.Left, ref identifier) &&
  127. (assign.Right is Ast.IdentifierExpression && (assign.Right as Ast.IdentifierExpression).Identifier == "value");
  128. }
  129. }
  130. astProperty = p;
  131. fieldName = identifier;
  132. return getResult && setResult;
  133. }
  134. T ParseMember<T>(string ext, string code) where T : INode
  135. {
  136. IParser parser = null;
  137. try {
  138. if (ext.Equals(".cs", StringComparison.OrdinalIgnoreCase))
  139. parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(code));
  140. if (ext.Equals(".vb", StringComparison.OrdinalIgnoreCase))
  141. parser = ParserFactory.CreateParser(SupportedLanguage.VBNet, new StringReader(code));
  142. if (parser == null)
  143. return default(T);
  144. var nodes = parser.ParseTypeMembers();
  145. if (parser.Errors.Count > 0)
  146. return default(T);
  147. return (T)nodes.FirstOrDefault();
  148. } finally {
  149. if (parser != null)
  150. parser.Dispose();
  151. }
  152. }
  153. string GetMemberText(IMember member, ITextEditor editor)
  154. {
  155. int startOffset = editor.Document.PositionToOffset(member.Region.BeginLine, member.Region.BeginColumn);
  156. int endOffset;
  157. if (member.BodyRegion.IsEmpty)
  158. endOffset = editor.Document.PositionToOffset(member.Region.EndLine, member.Region.EndColumn);
  159. else
  160. endOffset = editor.Document.PositionToOffset(member.BodyRegion.EndLine, member.BodyRegion.EndColumn);
  161. return editor.Document.GetText(startOffset, endOffset - startOffset);
  162. }
  163. bool IsValidMemberAssignment(Ast.Expression left, ref string identifier)
  164. {
  165. if (left is Ast.MemberReferenceExpression) {
  166. var e = left as Ast.MemberReferenceExpression;
  167. if (identifier == null) {
  168. identifier = e.MemberName;
  169. return e.TargetObject is Ast.ThisReferenceExpression;
  170. } else
  171. return e.TargetObject is Ast.ThisReferenceExpression && e.MemberName == identifier;
  172. }
  173. if (identifier == null) {
  174. if (left is Ast.IdentifierExpression) {
  175. identifier = (left as Ast.IdentifierExpression).Identifier;
  176. return true;
  177. }
  178. return false;
  179. } else
  180. return (left is Ast.IdentifierExpression) && (left as Ast.IdentifierExpression).Identifier == identifier;
  181. }
  182. void ConvertToAutomaticProperty(ITextEditor editor, IProperty property, IField fieldDef, Ast.PropertyDeclaration astProp)
  183. {
  184. CodeGenerator codeGen = property.DeclaringType.ProjectContent.Language.CodeGenerator;
  185. int fieldStartOffset = editor.Document.PositionToOffset(fieldDef.Region.BeginLine, fieldDef.Region.BeginColumn);
  186. int fieldEndOffset = editor.Document.PositionToOffset(fieldDef.Region.EndLine, fieldDef.Region.EndColumn);
  187. int startOffset = editor.Document.PositionToOffset(property.Region.BeginLine, property.Region.BeginColumn);
  188. int endOffset = editor.Document.PositionToOffset(property.BodyRegion.EndLine, property.BodyRegion.EndColumn);
  189. ITextAnchor startAnchor = editor.Document.CreateAnchor(startOffset);
  190. ITextAnchor endAnchor = editor.Document.CreateAnchor(endOffset);
  191. if (astProp.HasGetRegion)
  192. astProp.GetRegion.Block = null;
  193. if (!astProp.HasSetRegion) {
  194. astProp.SetRegion = new Ast.PropertySetRegion(null, null);
  195. astProp.SetRegion.Modifier = CodeGenerator.ConvertModifier(fieldDef.Modifiers, new ClassFinder(fieldDef))
  196. & (Ast.Modifiers.Private | Ast.Modifiers.Internal | Ast.Modifiers.Protected | Ast.Modifiers.Public);
  197. }
  198. Ast.FieldDeclaration f = ParseMember<Ast.FieldDeclaration>(Path.GetExtension(editor.FileName),
  199. GetMemberText(fieldDef, editor));
  200. astProp.Initializer = f.Fields.First().Initializer;
  201. if (astProp.HasSetRegion)
  202. astProp.SetRegion.Block = null;
  203. using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("${res:SharpDevelop.Refactoring.ConvertToAutomaticProperty}")) {
  204. var refs = RefactoringService.FindReferences(fieldDef, monitor);
  205. using (editor.Document.OpenUndoGroup()) {
  206. FindReferencesAndRenameHelper.RenameReferences(refs, property.Name);
  207. editor.Document.Remove(fieldStartOffset, fieldEndOffset - fieldStartOffset);
  208. editor.Document.Replace(startAnchor.Offset, endAnchor.Offset - startAnchor.Offset, codeGen.GenerateCode(astProp, ""));
  209. }
  210. }
  211. }
  212. #endregion
  213. }
  214. }