You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
320 lines
12 KiB
320 lines
12 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Windows.Input; |
|
|
|
using ICSharpCode.AvalonEdit.Snippets; |
|
using ICSharpCode.NRefactory.CSharp; |
|
using ICSharpCode.NRefactory.CSharp.Refactoring; |
|
using ICSharpCode.NRefactory.Editor; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
|
using ICSharpCode.SharpDevelop; |
|
using ICSharpCode.SharpDevelop.Project; |
|
using ICSharpCode.SharpDevelop.Editor; |
|
using ICSharpCode.SharpDevelop.Refactoring; |
|
using CSharpBinding.Parser; |
|
|
|
namespace CSharpBinding.Refactoring |
|
{ |
|
/// <summary> |
|
/// Interaction logic for CreatePropertiesDialog.xaml |
|
/// </summary> |
|
public partial class CreatePropertiesDialog : AbstractInlineRefactorDialog |
|
{ |
|
IList<FieldWrapper> fields; |
|
|
|
public CreatePropertiesDialog(InsertionContext context, ITextEditor editor, ITextAnchor anchor) |
|
: base(context, editor, anchor) |
|
{ |
|
InitializeComponent(); |
|
} |
|
|
|
protected override void Initialize() |
|
{ |
|
base.Initialize(); |
|
|
|
var typeResolveContext = refactoringContext.GetTypeResolveContext(); |
|
if (typeResolveContext == null) { |
|
return; |
|
} |
|
var resolvedCurrent = typeResolveContext.CurrentTypeDefinition; |
|
|
|
fields = FindFields(resolvedCurrent).ToList(); |
|
this.listBox.ItemsSource = fields; |
|
|
|
if (fields.Any()) |
|
Visibility = System.Windows.Visibility.Visible; |
|
|
|
implementInterface.IsChecked = !resolvedCurrent.IsStatic && HasOnPropertyChanged(resolvedCurrent); |
|
if (resolvedCurrent.IsStatic) |
|
implementInterface.Visibility = System.Windows.Visibility.Collapsed; |
|
|
|
SelectAllUnchecked(); |
|
} |
|
|
|
static IEnumerable<FieldWrapper> FindFields(IType sourceClass) |
|
{ |
|
int i = 0; |
|
|
|
foreach (var f in sourceClass.GetFields().Where(field => !field.IsConst |
|
&& field.ReturnType != null)) { |
|
yield return new FieldWrapper(f) { Index = i }; |
|
i++; |
|
} |
|
} |
|
|
|
|
|
protected override string GenerateCode(ITypeDefinition currentClass) |
|
{ |
|
bool implementInterface = this.implementInterface.IsChecked == true; |
|
bool hasOnPropertyChanged = HasOnPropertyChanged(currentClass); |
|
bool useEventArgs = false; |
|
|
|
AstNode insertionAnchorElement = refactoringContext.GetNode(); |
|
if ((insertionAnchorElement == null) || !(insertionAnchorElement.Parent is TypeDeclaration)) { |
|
return null; |
|
} |
|
NewLineNode newLineNode = insertionAnchorElement as NewLineNode; |
|
while (insertionAnchorElement.PrevSibling is NewLineNode) |
|
insertionAnchorElement = insertionAnchorElement.PrevSibling ?? insertionAnchorElement; |
|
|
|
using (Script script = refactoringContext.StartScript()) { |
|
TypeDeclaration currentClassDeclaration = insertionAnchorElement.Parent as TypeDeclaration; |
|
|
|
if (implementInterface && !currentClass.IsStatic) { |
|
if (!hasOnPropertyChanged) { |
|
var nodes = new List<AstNode>(); |
|
if (!currentClass.GetAllBaseTypeDefinitions().Any(bt => bt.FullName == "System.ComponentModel.INotifyPropertyChanged")) { |
|
// int insertion = editor.Document.GetOffset(currentClass.BodyRegion.BeginLine, currentClass.BodyRegion.BeginColumn); |
|
AstNode nodeBeforeClassBlock = currentClassDeclaration.LBraceToken; |
|
if (nodeBeforeClassBlock.PrevSibling is NewLineNode) { |
|
// There's a new line before the brace, insert before it! |
|
nodeBeforeClassBlock = nodeBeforeClassBlock.PrevSibling; |
|
} |
|
int insertion = editor.Document.GetOffset(nodeBeforeClassBlock.StartLocation); |
|
|
|
AstType interfaceTypeNode = refactoringContext.CreateShortType("System.ComponentModel", "INotifyPropertyChanged", 0); |
|
var directBaseTypes = currentClass.DirectBaseTypes.Where(t => t.FullName != "System.Object"); |
|
// if ((directBaseTypes != null) && (directBaseTypes.Count() > 0)) { |
|
if (currentClassDeclaration.BaseTypes.Count > 0) { |
|
script.InsertText(insertion, ", " + interfaceTypeNode.GetText() + " "); |
|
} else { |
|
script.InsertText(insertion, " : " + interfaceTypeNode.GetText() + " "); |
|
} |
|
} |
|
|
|
var rt = new GetClassTypeReference("System.ComponentModel", "INotifyPropertyChanged", 0); |
|
var rtResolved = rt.Resolve(refactoringContext.Compilation); |
|
var ev = rtResolved.GetEvents().First(e => e.Name == "PropertyChanged"); |
|
|
|
EventDeclaration propertyChangedEvent = new EventDeclaration(); |
|
propertyChangedEvent.Variables.Add(new VariableInitializer(ev.Name)); |
|
propertyChangedEvent.Modifiers = Modifiers.Public; |
|
propertyChangedEvent.ReturnType = refactoringContext.CreateShortType(ev.ReturnType); |
|
|
|
nodes.Add(propertyChangedEvent); |
|
|
|
MethodDeclaration onEvent = CreateOnEventMethod(ev, currentClass); |
|
nodes.Add(onEvent); |
|
foreach (var node in nodes) { |
|
script.InsertAfter(insertionAnchorElement, node); |
|
AppendNewLine(script, insertionAnchorElement, newLineNode); |
|
} |
|
useEventArgs = false; |
|
} else { |
|
useEventArgs = currentClass.GetMethods().First(m => m.Name == "OnPropertyChanged").Parameters[0].Type.FullName != "System.String"; |
|
} |
|
} |
|
|
|
foreach (FieldWrapper field in fields.Where(f => f.IsIncluded)) { |
|
var prop = CreateProperty(field.Field, true, field.AddSetter); |
|
if (!field.Field.IsStatic && !currentClass.IsStatic && field.AddSetter && implementInterface) { |
|
var invocation = new ExpressionStatement(CreateInvocation(field.PropertyName, useEventArgs)); |
|
var assignment = prop.Setter.Body.Children.ElementAt(0) as Statement; |
|
prop.Setter.Body = new BlockStatement(); |
|
BlockStatement elseBlock = new BlockStatement(); |
|
elseBlock.Add(assignment.Clone()); |
|
elseBlock.Add(invocation); |
|
prop.Setter.Body.Add( |
|
new IfElseStatement( |
|
new BinaryOperatorExpression(new IdentifierExpression(field.MemberName), BinaryOperatorType.InEquality, new IdentifierExpression("value")), |
|
elseBlock |
|
) |
|
); |
|
} |
|
|
|
script.InsertAfter(insertionAnchorElement, prop); |
|
AppendNewLine(script, insertionAnchorElement, newLineNode); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
MethodDeclaration CreateOnEventMethod(IEvent e, ITypeDefinition currentClass) |
|
{ |
|
List<ParameterDeclaration> parameters = new List<ParameterDeclaration>(); |
|
// bool sender = false; |
|
// if (e.ReturnType != null) { |
|
// IMethod invoke = e.ReturnType.GetMethods().SingleOrDefault(m => m.Name=="Invoke" ); |
|
// if (invoke != null) { |
|
// foreach (IParameter param in invoke.Parameters) { |
|
// parameters.Add(new ParameterDeclaration(ConvertType(param.Type), param.Name)); |
|
// } |
|
// if (parameters.Count > 0 && string.Equals(parameters[0].Name, "sender", StringComparison.InvariantCultureIgnoreCase)) { |
|
// sender = true; |
|
// parameters.RemoveAt(0); |
|
// } |
|
// } |
|
// } |
|
parameters.Add(new ParameterDeclaration(ConvertType(KnownTypeCode.String), "propertyName")); |
|
|
|
Modifiers modifier; |
|
if (e.IsStatic) |
|
modifier = Modifiers.Private | Modifiers.Static; |
|
else if ((e.DeclaringType.GetDefinition() != null) && e.DeclaringType.GetDefinition().IsSealed) |
|
modifier = Modifiers.Private; |
|
else |
|
modifier = Modifiers.Protected | Modifiers.Virtual; |
|
|
|
MethodDeclaration method = new MethodDeclaration { |
|
Name = "On" + e.Name, |
|
Modifiers = ConvertModifier(modifier, currentClass), |
|
ReturnType = ConvertType(KnownTypeCode.Void) |
|
}; |
|
method.Parameters.AddRange(parameters); |
|
|
|
List<Expression> arguments = new List<Expression>(); |
|
// if (sender) { |
|
if (e.IsStatic) |
|
arguments.Add(new PrimitiveExpression(null, "null")); |
|
else |
|
arguments.Add(new ThisReferenceExpression()); |
|
// } |
|
// foreach (ParameterDeclaration param in parameters) { |
|
// arguments.Add(new IdentifierExpression(param.Name)); |
|
// } |
|
arguments.Add(new ObjectCreateExpression(refactoringContext.CreateShortType("System.ComponentModel", "PropertyChangedEventArgs", 0), |
|
new List<Expression> { new IdentifierExpression("propertyName") })); |
|
method.Body = new BlockStatement(); |
|
// method.Body.Add(new RaiseEventStatement(e.Name, arguments)); |
|
method.Body.Add(new ExpressionStatement(new InvocationExpression(new MemberReferenceExpression(new ThisReferenceExpression(), e.Name), arguments))); |
|
|
|
return method; |
|
} |
|
|
|
PropertyDeclaration CreateProperty(IField field, bool createGetter, bool createSetter) |
|
{ |
|
IProject project = field.Compilation.GetProject(); |
|
if (project == null) |
|
return null; |
|
|
|
CodeGenerator codeGenerator = project.LanguageBinding.CodeGenerator; |
|
string name = codeGenerator.GetPropertyName(field.Name); |
|
CSharpFullParseInformation tempParseInformation; |
|
PropertyDeclaration property = new PropertyDeclaration() |
|
{ |
|
Modifiers = ConvertModifier(field.GetDeclaration(out tempParseInformation).Modifiers, field.DeclaringTypeDefinition), |
|
Name = name |
|
}; |
|
property.ReturnType = ConvertType(field.ReturnType); |
|
if (createGetter) { |
|
property.Getter = new Accessor() |
|
{ |
|
Body = new BlockStatement() |
|
}; |
|
property.Getter.Body.Add(new ReturnStatement(new IdentifierExpression(field.Name))); |
|
} |
|
if (createSetter) { |
|
property.Setter = new Accessor() |
|
{ |
|
Body = new BlockStatement() |
|
}; |
|
property.Setter.Body.Add(new AssignmentExpression(new IdentifierExpression(field.Name), new IdentifierExpression("value"))); |
|
} |
|
|
|
property.Modifiers = Modifiers.Public | (property.Modifiers & Modifiers.Static); |
|
return property; |
|
} |
|
|
|
public static Modifiers ConvertModifier(Modifiers modifiers, ITypeDefinition targetContext) |
|
{ |
|
IProject project = targetContext.ParentAssembly.GetProject(); |
|
if (targetContext != null && project != null && targetContext.DeclaringType != null) { |
|
// if (project.LanguageBinding.IsClassWithImplicitlyStaticMembers(targetContext.CallingClass)) { |
|
return modifiers & ~Modifiers.Static; |
|
// } |
|
} |
|
if (modifiers.HasFlag(Modifiers.Static)) |
|
modifiers &= ~(Modifiers.Abstract | Modifiers.Sealed); |
|
return modifiers; |
|
} |
|
|
|
string GetCodeFromRegion(DomRegion region) |
|
{ |
|
int startOffset = editor.Document.PositionToOffset(region.BeginLine, region.BeginColumn); |
|
int endOffset = editor.Document.PositionToOffset(region.EndLine, region.EndColumn); |
|
return editor.Document.GetText(startOffset, endOffset - startOffset); |
|
} |
|
|
|
bool HasOnPropertyChanged(ITypeDefinition currentClass) |
|
{ |
|
return currentClass.GetMethods().Any(m => m.Name == "OnPropertyChanged"); |
|
} |
|
|
|
InvocationExpression CreateInvocation(string name, bool useEventArgs) |
|
{ |
|
Expression arg = useEventArgs |
|
? (Expression)new ObjectCreateExpression(refactoringContext.CreateShortType("System.ComponentModel", "PropertyChangedEventArgs", 0), new List<Expression> { new PrimitiveExpression(name) }) |
|
: (Expression)new PrimitiveExpression(name); |
|
return new InvocationExpression(new IdentifierExpression("OnPropertyChanged"), new List<Expression> { arg }); |
|
} |
|
|
|
void SelectAllChecked() |
|
{ |
|
foreach (var param in fields) { |
|
param.IsIncluded = true; |
|
} |
|
} |
|
|
|
void SelectAllChecked(object sender, System.Windows.RoutedEventArgs e) |
|
{ |
|
SelectAllChecked(); |
|
} |
|
|
|
void SelectAllUnchecked() |
|
{ |
|
foreach (var param in fields) { |
|
param.IsIncluded = false; |
|
} |
|
} |
|
|
|
void SelectAllUnchecked(object sender, System.Windows.RoutedEventArgs e) |
|
{ |
|
SelectAllUnchecked(); |
|
} |
|
|
|
bool AllSelected { |
|
get { return fields.Count(p => p.IsIncluded) == fields.Count; } |
|
} |
|
|
|
protected override void OnKeyDown(KeyEventArgs e) |
|
{ |
|
Key? allAccessKey = GetAccessKeyFromButton(selectAll); |
|
|
|
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && allAccessKey == e.SystemKey) { |
|
if (AllSelected) |
|
listBox.UnselectAll(); |
|
else |
|
listBox.SelectAll(); |
|
e.Handled = true; |
|
} |
|
|
|
base.OnKeyDown(e); |
|
} |
|
} |
|
} |