|
|
@ -2,6 +2,7 @@ |
|
|
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|
|
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|
|
|
|
|
|
|
|
|
|
|
using System; |
|
|
|
using System; |
|
|
|
|
|
|
|
using System.CodeDom; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Linq; |
|
|
|
using System.Linq; |
|
|
|
using System.Text; |
|
|
|
using System.Text; |
|
|
@ -9,11 +10,9 @@ using System.Text; |
|
|
|
using ICSharpCode.AvalonEdit.Snippets; |
|
|
|
using ICSharpCode.AvalonEdit.Snippets; |
|
|
|
using ICSharpCode.Core; |
|
|
|
using ICSharpCode.Core; |
|
|
|
using ICSharpCode.NRefactory.CSharp; |
|
|
|
using ICSharpCode.NRefactory.CSharp; |
|
|
|
|
|
|
|
using ICSharpCode.NRefactory.CSharp.Refactoring; |
|
|
|
using ICSharpCode.NRefactory.Editor; |
|
|
|
using ICSharpCode.NRefactory.Editor; |
|
|
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
using ICSharpCode.NRefactory.Ast; |
|
|
|
|
|
|
|
using ICSharpCode.SharpDevelop.Dom; |
|
|
|
|
|
|
|
using ICSharpCode.SharpDevelop.Dom.Refactoring; |
|
|
|
|
|
|
|
using ICSharpCode.SharpDevelop.Editor; |
|
|
|
using ICSharpCode.SharpDevelop.Editor; |
|
|
|
using Dom = ICSharpCode.SharpDevelop.Dom; |
|
|
|
using Dom = ICSharpCode.SharpDevelop.Dom; |
|
|
|
|
|
|
|
|
|
|
@ -24,13 +23,13 @@ namespace CSharpBinding.Refactoring |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public partial class OverrideEqualsGetHashCodeMethodsDialog : AbstractInlineRefactorDialog |
|
|
|
public partial class OverrideEqualsGetHashCodeMethodsDialog : AbstractInlineRefactorDialog |
|
|
|
{ |
|
|
|
{ |
|
|
|
IType selectedClass; |
|
|
|
ITypeDefinition selectedClass; |
|
|
|
ITextAnchor startAnchor; |
|
|
|
ITextAnchor startAnchor; |
|
|
|
IMethod selectedMethod; |
|
|
|
IMethod selectedMethod; |
|
|
|
AstNode baseCallNode; |
|
|
|
AstNode baseCallNode; |
|
|
|
|
|
|
|
|
|
|
|
public OverrideEqualsGetHashCodeMethodsDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor endAnchor, |
|
|
|
public OverrideEqualsGetHashCodeMethodsDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor endAnchor, |
|
|
|
ITextAnchor insertionPosition, IType selectedClass, IMethod selectedMethod, AstNode baseCallNode) |
|
|
|
ITextAnchor insertionPosition, ITypeDefinition selectedClass, IMethod selectedMethod, AstNode baseCallNode) |
|
|
|
: base(context, editor, insertionPosition) |
|
|
|
: base(context, editor, insertionPosition) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (selectedClass == null) |
|
|
|
if (selectedClass == null) |
|
|
@ -51,15 +50,19 @@ namespace CSharpBinding.Refactoring |
|
|
|
|
|
|
|
|
|
|
|
addOtherMethod.Content = StringParser.Parse("${res:AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddOtherMethod}", new StringTagPair("otherMethod", otherMethod)); |
|
|
|
addOtherMethod.Content = StringParser.Parse("${res:AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddOtherMethod}", new StringTagPair("otherMethod", otherMethod)); |
|
|
|
|
|
|
|
|
|
|
|
addIEquatable.IsEnabled = !selectedClass.BaseTypes.Any( |
|
|
|
addIEquatable.IsEnabled = !selectedClass.GetAllBaseTypes().Any( |
|
|
|
type => { |
|
|
|
type => { |
|
|
|
if (!type.IsGenericReturnType) |
|
|
|
if (!type.IsParameterized || (type.TypeParameterCount != 1)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
var genericType = type.CastToGenericReturnType(); |
|
|
|
if (type.FullName != "System.IEquatable") |
|
|
|
var boundTo = genericType.TypeParameter.BoundTo; |
|
|
|
|
|
|
|
if (boundTo == null) |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
return boundTo.Name == selectedClass.Name; |
|
|
|
return type.TypeArguments.First().FullName == selectedClass.FullName; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// var genericType = type.CastToGenericReturnType();
|
|
|
|
|
|
|
|
// var boundTo = genericType.TypeParameter.BoundTo;
|
|
|
|
|
|
|
|
// if (boundTo == null)
|
|
|
|
|
|
|
|
// return false;
|
|
|
|
|
|
|
|
// return boundTo.Name == selectedClass.Name;
|
|
|
|
} |
|
|
|
} |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
@ -82,11 +85,11 @@ namespace CSharpBinding.Refactoring |
|
|
|
return type != null |
|
|
|
return type != null |
|
|
|
&& type.FullName != "System.Single" |
|
|
|
&& type.FullName != "System.Single" |
|
|
|
&& type.FullName != "System.Double" |
|
|
|
&& type.FullName != "System.Double" |
|
|
|
&& (!type.IsReferenceType |
|
|
|
&& (!IsReferenceType(type) |
|
|
|
|| type.FullName == "System.String"); |
|
|
|
|| type.FullName == "System.String"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static Expression TestEquality(string other, IField field) |
|
|
|
Expression TestEquality(string other, IField field) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (CanCompareEqualityWithOperator(field.ReturnType)) { |
|
|
|
if (CanCompareEqualityWithOperator(field.ReturnType)) { |
|
|
|
return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name), |
|
|
|
return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name), |
|
|
@ -94,7 +97,7 @@ namespace CSharpBinding.Refactoring |
|
|
|
new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); |
|
|
|
new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
InvocationExpression ie = new InvocationExpression( |
|
|
|
InvocationExpression ie = new InvocationExpression( |
|
|
|
new MemberReferenceExpression(new TypeReferenceExpression(new TypeReference("System.Object", true)), "Equals") |
|
|
|
new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(KnownTypeCode.Object)), "Equals") |
|
|
|
); |
|
|
|
); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name)); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name)); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); |
|
|
@ -102,7 +105,7 @@ namespace CSharpBinding.Refactoring |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static Expression TestEquality(string other, IProperty property) |
|
|
|
Expression TestEquality(string other, IProperty property) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (CanCompareEqualityWithOperator(property.ReturnType)) { |
|
|
|
if (CanCompareEqualityWithOperator(property.ReturnType)) { |
|
|
|
return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name), |
|
|
|
return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name), |
|
|
@ -110,7 +113,7 @@ namespace CSharpBinding.Refactoring |
|
|
|
new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); |
|
|
|
new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
InvocationExpression ie = new InvocationExpression( |
|
|
|
InvocationExpression ie = new InvocationExpression( |
|
|
|
new MemberReferenceExpression(new TypeReferenceExpression(new SimpleType("System.Object")), "Equals") |
|
|
|
new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(KnownTypeCode.Object)), "Equals") |
|
|
|
); |
|
|
|
); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name)); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name)); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); |
|
|
|
ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); |
|
|
@ -118,6 +121,15 @@ namespace CSharpBinding.Refactoring |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool IsReferenceType(IType type) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (type.IsReferenceType.HasValue) { |
|
|
|
|
|
|
|
return type.IsReferenceType.Value; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected override string GenerateCode(ITypeDefinition currentClass) |
|
|
|
protected override string GenerateCode(ITypeDefinition currentClass) |
|
|
|
{ |
|
|
|
{ |
|
|
|
StringBuilder code = new StringBuilder(); |
|
|
|
StringBuilder code = new StringBuilder(); |
|
|
@ -126,7 +138,17 @@ namespace CSharpBinding.Refactoring |
|
|
|
|
|
|
|
|
|
|
|
string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset); |
|
|
|
string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset); |
|
|
|
|
|
|
|
|
|
|
|
CodeGenerator generator = language.CodeGenerator; |
|
|
|
// CodeGenerator generator = language.CodeGenerator;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; |
|
|
|
|
|
|
|
if (insertedOverrideMethod == null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// We are not inside of a method declaration
|
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using (Script script = refactoringContext.StartScript()) { |
|
|
|
|
|
|
|
NewLineNode nextNewLineNode = insertedOverrideMethod.NextSibling as NewLineNode; |
|
|
|
|
|
|
|
|
|
|
|
// if (Options.AddIEquatableInterface) {
|
|
|
|
// if (Options.AddIEquatableInterface) {
|
|
|
|
// TODO : add IEquatable<T> to class
|
|
|
|
// TODO : add IEquatable<T> to class
|
|
|
@ -154,40 +176,44 @@ namespace CSharpBinding.Refactoring |
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
if (Options.SurroundWithRegion) { |
|
|
|
if (Options.SurroundWithRegion) { |
|
|
|
editor.Document.InsertNormalized(startAnchor.Offset, "#region Equals and GetHashCode implementation\n" + indent); |
|
|
|
script.InsertBefore(insertedOverrideMethod, new PreProcessorDirective(PreProcessorDirectiveType.Region, "Equals and GetHashCode implementation")); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
string codeForMethodBody; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ("Equals".Equals(selectedMethod.Name, StringComparison.Ordinal)) { |
|
|
|
if ("Equals".Equals(selectedMethod.Name, StringComparison.Ordinal)) { |
|
|
|
IList<MethodDeclaration> equalsOverrides = CreateEqualsOverrides(currentClass); |
|
|
|
IList<MethodDeclaration> equalsOverrides = CreateEqualsOverrides(currentClass); |
|
|
|
MethodDeclaration defaultOverride = equalsOverrides.First(); |
|
|
|
MethodDeclaration defaultOverride = equalsOverrides.First(); |
|
|
|
equalsOverrides = equalsOverrides.Skip(1).ToList(); |
|
|
|
equalsOverrides = equalsOverrides.Skip(1).ToList(); |
|
|
|
|
|
|
|
|
|
|
|
StringBuilder builder = new StringBuilder(); |
|
|
|
// Insert children of default Equals method into
|
|
|
|
|
|
|
|
foreach (AstNode element in defaultOverride.Body.Children) { |
|
|
|
foreach (AbstractNode element in defaultOverride.Body.Children.OfType<AbstractNode>()) { |
|
|
|
script.AddTo(insertedOverrideMethod.Body, element.Clone()); |
|
|
|
builder.Append(language.CodeGenerator.GenerateCode(element, indent + "\t")); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
codeForMethodBody = builder.ToString().Trim(); |
|
|
|
// Add other Equals() overrides after our main inserted method
|
|
|
|
|
|
|
|
|
|
|
|
if (addOtherMethod.IsChecked == true) { |
|
|
|
if (addOtherMethod.IsChecked == true) { |
|
|
|
if (equalsOverrides.Any()) |
|
|
|
if (equalsOverrides.Any()) { |
|
|
|
code.Append(indent + "\n" + string.Join("\n", equalsOverrides.Select(item => generator.GenerateCode(item, indent)))); |
|
|
|
foreach (var equalsMethod in equalsOverrides) { |
|
|
|
code.Append(indent + "\n" + generator.GenerateCode(CreateGetHashCodeOverride(currentClass), indent)); |
|
|
|
AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); |
|
|
|
|
|
|
|
script.InsertAfter(insertedOverrideMethod, equalsMethod); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); |
|
|
|
|
|
|
|
script.InsertAfter(insertedOverrideMethod, CreateGetHashCodeOverride(currentClass)); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
StringBuilder builder = new StringBuilder(); |
|
|
|
StringBuilder builder = new StringBuilder(); |
|
|
|
|
|
|
|
|
|
|
|
foreach (AbstractNode element in CreateGetHashCodeOverride(currentClass).Body.Children.OfType<AbstractNode>()) { |
|
|
|
foreach (AstNode element in CreateGetHashCodeOverride(currentClass).Body.Children) { |
|
|
|
builder.Append(language.CodeGenerator.GenerateCode(element, indent + "\t")); |
|
|
|
script.AddTo(insertedOverrideMethod.Body, element.Clone()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
codeForMethodBody = builder.ToString().Trim(); |
|
|
|
if (addOtherMethod.IsChecked == true) { |
|
|
|
|
|
|
|
foreach (var equalsMethod in CreateEqualsOverrides(currentClass)) { |
|
|
|
if (addOtherMethod.IsChecked == true) |
|
|
|
AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); |
|
|
|
code.Append(indent + "\n" + string.Join("\n", CreateEqualsOverrides(currentClass).Select(item => generator.GenerateCode(item, indent)))); |
|
|
|
script.InsertAfter(insertedOverrideMethod, equalsMethod); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (Options.AddOperatorOverloads) { |
|
|
|
if (Options.AddOperatorOverloads) { |
|
|
@ -205,7 +231,7 @@ namespace CSharpBinding.Refactoring |
|
|
|
new IdentifierExpression("ReferenceEquals"), |
|
|
|
new IdentifierExpression("ReferenceEquals"), |
|
|
|
new List<Expression>() { new IdentifierExpression("lhs"), new PrimitiveExpression(null) } |
|
|
|
new List<Expression>() { new IdentifierExpression("lhs"), new PrimitiveExpression(null) } |
|
|
|
), |
|
|
|
), |
|
|
|
BinaryOperatorType.LogicalOr, |
|
|
|
BinaryOperatorType.ConditionalOr, |
|
|
|
new InvocationExpression( |
|
|
|
new InvocationExpression( |
|
|
|
new IdentifierExpression("ReferenceEquals"), |
|
|
|
new IdentifierExpression("ReferenceEquals"), |
|
|
|
new List<Expression>() { new IdentifierExpression("rhs"), new PrimitiveExpression(null) } |
|
|
|
new List<Expression>() { new IdentifierExpression("rhs"), new PrimitiveExpression(null) } |
|
|
@ -215,57 +241,65 @@ namespace CSharpBinding.Refactoring |
|
|
|
) |
|
|
|
) |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
BlockStatement equalsOpBody = new BlockStatement() { |
|
|
|
BlockStatement equalsOpBody = new BlockStatement(); |
|
|
|
Children = { |
|
|
|
|
|
|
|
|
|
|
|
if (currentClass.Kind == TypeKind.Class) { |
|
|
|
|
|
|
|
foreach (var statement in checkStatements) { |
|
|
|
|
|
|
|
equalsOpBody.Add(statement); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
equalsOpBody.Add( |
|
|
|
new ReturnStatement( |
|
|
|
new ReturnStatement( |
|
|
|
new InvocationExpression( |
|
|
|
new InvocationExpression( |
|
|
|
new MemberReferenceExpression(new IdentifierExpression("lhs"), "Equals"), |
|
|
|
new MemberReferenceExpression(new IdentifierExpression("lhs"), "Equals"), |
|
|
|
new List<Expression>() { new IdentifierExpression("rhs") } |
|
|
|
new List<Expression>() { new IdentifierExpression("rhs") } |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (currentClass.ClassType == Dom.ClassType.Class) { |
|
|
|
|
|
|
|
equalsOpBody.Children.InsertRange(0, checkStatements); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BlockStatement notEqualsOpBody = new BlockStatement() { |
|
|
|
BlockStatement notEqualsOpBody = new BlockStatement(); |
|
|
|
Children = { |
|
|
|
notEqualsOpBody.Add(new ReturnStatement( |
|
|
|
new ReturnStatement( |
|
|
|
|
|
|
|
new UnaryOperatorExpression( |
|
|
|
new UnaryOperatorExpression( |
|
|
|
|
|
|
|
UnaryOperatorType.Not, |
|
|
|
new ParenthesizedExpression( |
|
|
|
new ParenthesizedExpression( |
|
|
|
new BinaryOperatorExpression( |
|
|
|
new BinaryOperatorExpression( |
|
|
|
new IdentifierExpression("lhs"), |
|
|
|
new IdentifierExpression("lhs"), |
|
|
|
BinaryOperatorType.Equality, |
|
|
|
BinaryOperatorType.Equality, |
|
|
|
new IdentifierExpression("rhs") |
|
|
|
new IdentifierExpression("rhs") |
|
|
|
) |
|
|
|
) |
|
|
|
), |
|
|
|
|
|
|
|
UnaryOperatorType.Not |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
) |
|
|
|
}; |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
code.Append(indent + "\n" + generator.GenerateCode(CreateOperatorOverload(OverloadableOperatorType.Equality, currentClass, equalsOpBody), indent)); |
|
|
|
AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); |
|
|
|
code.Append(indent + "\n" + generator.GenerateCode(CreateOperatorOverload(OverloadableOperatorType.InEquality, currentClass, notEqualsOpBody), indent)); |
|
|
|
script.InsertAfter(insertedOverrideMethod, CreateOperatorOverload(OperatorType.Equality, currentClass, equalsOpBody)); |
|
|
|
|
|
|
|
AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); |
|
|
|
|
|
|
|
script.InsertAfter(insertedOverrideMethod, CreateOperatorOverload(OperatorType.Inequality, currentClass, notEqualsOpBody)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (Options.SurroundWithRegion) { |
|
|
|
if (Options.SurroundWithRegion) { |
|
|
|
code.AppendLine(indent + "#endregion"); |
|
|
|
AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); |
|
|
|
|
|
|
|
script.InsertAfter(insertedOverrideMethod, new PreProcessorDirective(PreProcessorDirectiveType.Endregion)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
editor.Document.InsertNormalized(insertionEndAnchor.Offset, code.ToString()); |
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return codeForMethodBody; |
|
|
|
void AppendNewLine(Script script, AstNode afterNode, NewLineNode newLineNode) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (newLineNode != null) |
|
|
|
|
|
|
|
script.InsertAfter(afterNode, newLineNode.Clone()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
List<MethodDeclaration> CreateEqualsOverrides(IType currentClass) |
|
|
|
List<MethodDeclaration> CreateEqualsOverrides(IType currentClass) |
|
|
|
{ |
|
|
|
{ |
|
|
|
List<MethodDeclaration> methods = new List<MethodDeclaration>(); |
|
|
|
List<MethodDeclaration> methods = new List<MethodDeclaration>(); |
|
|
|
|
|
|
|
|
|
|
|
AstType boolReference = new SimpleType("System.Boolean"); |
|
|
|
AstType boolReference = ConvertType(KnownTypeCode.Boolean); |
|
|
|
AstType objectReference = new SimpleType("System.Object", true); |
|
|
|
AstType objectReference = ConvertType(KnownTypeCode.Object); |
|
|
|
|
|
|
|
|
|
|
|
MethodDeclaration method = new MethodDeclaration { |
|
|
|
MethodDeclaration method = new MethodDeclaration { |
|
|
|
Name = "Equals", |
|
|
|
Name = "Equals", |
|
|
@ -275,22 +309,24 @@ namespace CSharpBinding.Refactoring |
|
|
|
method.Parameters.Add(new ParameterDeclaration(objectReference, "obj")); |
|
|
|
method.Parameters.Add(new ParameterDeclaration(objectReference, "obj")); |
|
|
|
method.Body = new BlockStatement(); |
|
|
|
method.Body = new BlockStatement(); |
|
|
|
|
|
|
|
|
|
|
|
AstType currentType = ConvertType(currentClass.DefaultReturnType); |
|
|
|
AstType currentType = ConvertType(currentClass); |
|
|
|
|
|
|
|
|
|
|
|
Expression expr = null; |
|
|
|
Expression expr = null; |
|
|
|
|
|
|
|
|
|
|
|
if (currentClass.ClassType == Dom.ClassType.Struct) { |
|
|
|
if (currentClass.Kind == TypeKind.Struct) { |
|
|
|
// return obj is CurrentType && Equals((CurrentType)obj);
|
|
|
|
// return obj is CurrentType && Equals((CurrentType)obj);
|
|
|
|
expr = new IsExpression(new IdentifierExpression("obj"), currentType); |
|
|
|
expr = new IsExpression() { |
|
|
|
|
|
|
|
Expression = new IdentifierExpression("obj"), |
|
|
|
|
|
|
|
Type = currentType.Clone() |
|
|
|
|
|
|
|
}; |
|
|
|
expr = new ParenthesizedExpression(expr); |
|
|
|
expr = new ParenthesizedExpression(expr); |
|
|
|
expr = new BinaryOperatorExpression( |
|
|
|
expr = new BinaryOperatorExpression( |
|
|
|
expr, BinaryOperatorType.LogicalAnd, |
|
|
|
expr, BinaryOperatorType.ConditionalAnd, |
|
|
|
new InvocationExpression( |
|
|
|
new InvocationExpression( |
|
|
|
new IdentifierExpression("Equals"), |
|
|
|
new IdentifierExpression("Equals"), |
|
|
|
new List<Expression> { |
|
|
|
new List<Expression> { |
|
|
|
new CastExpression(currentType, new IdentifierExpression("obj"), CastType.Cast) |
|
|
|
new CastExpression(currentType.Clone(), new IdentifierExpression("obj")) |
|
|
|
})); |
|
|
|
})); |
|
|
|
method.Body.AddChild(new ReturnStatement(expr)); |
|
|
|
method.Body.Add(new ReturnStatement(expr)); |
|
|
|
|
|
|
|
|
|
|
|
methods.Add(method); |
|
|
|
methods.Add(method); |
|
|
|
|
|
|
|
|
|
|
@ -298,141 +334,132 @@ namespace CSharpBinding.Refactoring |
|
|
|
method = new MethodDeclaration { |
|
|
|
method = new MethodDeclaration { |
|
|
|
Name = "Equals", |
|
|
|
Name = "Equals", |
|
|
|
Modifiers = Modifiers.Public, |
|
|
|
Modifiers = Modifiers.Public, |
|
|
|
ReturnType = boolReference |
|
|
|
ReturnType = boolReference.Clone() |
|
|
|
}; |
|
|
|
}; |
|
|
|
method.Parameters.Add(new ParameterDeclaration(currentType, "other")); |
|
|
|
method.Parameters.Add(new ParameterDeclaration(currentType, "other")); |
|
|
|
method.Body = new BlockStatement(); |
|
|
|
method.Body = new BlockStatement(); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
method.Body.AddChild(new VariableDeclaration( |
|
|
|
method.Body.Add(new VariableDeclarationStatement( |
|
|
|
|
|
|
|
currentType.Clone(), |
|
|
|
"other", |
|
|
|
"other", |
|
|
|
new CastExpression(currentType, new IdentifierExpression("obj")), |
|
|
|
new CastExpression(currentType.Clone(), new IdentifierExpression("obj")))); |
|
|
|
currentType)); |
|
|
|
method.Body.Add(new IfElseStatement( |
|
|
|
method.Body.AddChild(new IfElseStatement( |
|
|
|
|
|
|
|
new BinaryOperatorExpression(new IdentifierExpression("other"), BinaryOperatorType.Equality, new PrimitiveExpression(null, "null")), |
|
|
|
new BinaryOperatorExpression(new IdentifierExpression("other"), BinaryOperatorType.Equality, new PrimitiveExpression(null, "null")), |
|
|
|
new ReturnStatement(new PrimitiveExpression(false, "false")))); |
|
|
|
new ReturnStatement(new PrimitiveExpression(false, "false")))); |
|
|
|
|
|
|
|
|
|
|
|
// expr = new BinaryOperatorExpression(new ThisReferenceExpression(),
|
|
|
|
|
|
|
|
// BinaryOperatorType.ReferenceEquality,
|
|
|
|
|
|
|
|
// new IdentifierExpression("obj"));
|
|
|
|
|
|
|
|
// method.Body.AddChild(new IfElseStatement(expr, new ReturnStatement(new PrimitiveExpression(true, "true"))));
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expr = null; |
|
|
|
expr = null; |
|
|
|
foreach (IField field in currentClass.Fields) { |
|
|
|
foreach (IField field in currentClass.GetFields()) { |
|
|
|
if (field.IsStatic) continue; |
|
|
|
if (field.IsStatic) continue; |
|
|
|
|
|
|
|
|
|
|
|
if (expr == null) { |
|
|
|
if (expr == null) { |
|
|
|
expr = TestEquality("other", field); |
|
|
|
expr = TestEquality("other", field); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.LogicalAnd, |
|
|
|
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.ConditionalAnd, |
|
|
|
TestEquality("other", field)); |
|
|
|
TestEquality("other", field)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
foreach (IProperty property in currentClass.Properties) { |
|
|
|
foreach (IProperty property in currentClass.GetProperties()) { |
|
|
|
if (property.IsStatic || !property.IsAutoImplemented()) continue; |
|
|
|
if (property.IsStatic || !property.IsAutoImplemented()) continue; |
|
|
|
if (expr == null) { |
|
|
|
if (expr == null) { |
|
|
|
expr = TestEquality("other", property); |
|
|
|
expr = TestEquality("other", property); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.LogicalAnd, |
|
|
|
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.ConditionalAnd, |
|
|
|
TestEquality("other", property)); |
|
|
|
TestEquality("other", property)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
method.Body.AddChild(new ReturnStatement(expr ?? new PrimitiveExpression(true, "true"))); |
|
|
|
method.Body.Add(new ReturnStatement(expr ?? new PrimitiveExpression(true, "true"))); |
|
|
|
|
|
|
|
|
|
|
|
methods.Add(method); |
|
|
|
methods.Add(method); |
|
|
|
|
|
|
|
|
|
|
|
return methods; |
|
|
|
return methods; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
MethodDeclaration CreateGetHashCodeOverride(IClass currentClass) |
|
|
|
MethodDeclaration CreateGetHashCodeOverride(ITypeDefinition currentClass) |
|
|
|
{ |
|
|
|
{ |
|
|
|
TypeReference intReference = new TypeReference("System.Int32", true); |
|
|
|
const string hashCodeVarName = "hashCode"; |
|
|
|
VariableDeclaration hashCodeVar = new VariableDeclaration("hashCode", new PrimitiveExpression(0, "0"), intReference); |
|
|
|
AstType intReference = ConvertType(KnownTypeCode.Int32); |
|
|
|
|
|
|
|
VariableDeclarationStatement hashCodeVar = new VariableDeclarationStatement(intReference, hashCodeVarName, new PrimitiveExpression(0, "0")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create new method declaration (to insert after main inserted method)
|
|
|
|
MethodDeclaration getHashCodeMethod = new MethodDeclaration { |
|
|
|
MethodDeclaration getHashCodeMethod = new MethodDeclaration { |
|
|
|
Name = "GetHashCode", |
|
|
|
Name = "GetHashCode", |
|
|
|
Modifier = Modifiers.Public | Modifiers.Override, |
|
|
|
Modifiers = Modifiers.Public | Modifiers.Override, |
|
|
|
TypeReference = intReference, |
|
|
|
ReturnType = intReference.Clone(), |
|
|
|
Body = new BlockStatement() |
|
|
|
Body = new BlockStatement() |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
getHashCodeMethod.Body.AddChild(new LocalVariableDeclaration(hashCodeVar)); |
|
|
|
getHashCodeMethod.Body.Add(hashCodeVar); |
|
|
|
|
|
|
|
|
|
|
|
if (currentClass.Fields.Any(f => !f.IsStatic) || currentClass.Properties.Any(p => !p.IsStatic && p.IsAutoImplemented())) { |
|
|
|
if (currentClass.Fields.Any(f => !f.IsStatic) || currentClass.Properties.Any(p => !p.IsStatic && p.IsAutoImplemented())) { |
|
|
|
bool usePrimeMultiplication = currentClass.ProjectContent.Language == LanguageProperties.CSharp; |
|
|
|
bool usePrimeMultiplication = true; // Always leave true for C#?
|
|
|
|
BlockStatement hashCalculationBlock; |
|
|
|
BlockStatement hashCalculationBlock = new BlockStatement(); |
|
|
|
|
|
|
|
getHashCodeMethod.Body.Add(new UncheckedStatement(hashCalculationBlock)); |
|
|
|
if (usePrimeMultiplication) { |
|
|
|
// hashCalculationBlock = getHashCodeMethod.Body;
|
|
|
|
hashCalculationBlock = new BlockStatement(); |
|
|
|
|
|
|
|
getHashCodeMethod.Body.AddChild(new UncheckedStatement(hashCalculationBlock)); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
hashCalculationBlock = getHashCodeMethod.Body; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int fieldIndex = 0; |
|
|
|
int fieldIndex = 0; |
|
|
|
|
|
|
|
|
|
|
|
foreach (IField field in currentClass.Fields) { |
|
|
|
foreach (IField field in currentClass.Fields) { |
|
|
|
if (field.IsStatic) continue; |
|
|
|
if (field.IsStatic) continue; |
|
|
|
|
|
|
|
|
|
|
|
AddToBlock(hashCodeVar, getHashCodeMethod, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, field); |
|
|
|
AddToBlock(hashCodeVarName, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, field); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
foreach (IProperty property in currentClass.Properties) { |
|
|
|
foreach (IProperty property in currentClass.Properties) { |
|
|
|
if (property.IsStatic || !property.IsAutoImplemented()) continue; |
|
|
|
if (property.IsStatic || !property.IsAutoImplemented()) continue; |
|
|
|
|
|
|
|
|
|
|
|
AddToBlock(hashCodeVar, getHashCodeMethod, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, property); |
|
|
|
AddToBlock(hashCodeVarName, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, property); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
getHashCodeMethod.Body.AddChild(new ReturnStatement(new IdentifierExpression(hashCodeVar.Name))); |
|
|
|
getHashCodeMethod.Body.Add(new ReturnStatement(new IdentifierExpression(hashCodeVarName))); |
|
|
|
return getHashCodeMethod; |
|
|
|
return getHashCodeMethod; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void AddToBlock(VariableDeclaration hashCodeVar, MethodDeclaration getHashCodeMethod, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IField field) |
|
|
|
void AddToBlock(string hashCodeVarName, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IField field) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(field.Name), "GetHashCode")); |
|
|
|
Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(field.Name), "GetHashCode")); |
|
|
|
if (usePrimeMultiplication) { |
|
|
|
if (usePrimeMultiplication) { |
|
|
|
int prime = largePrimes[fieldIndex++ % largePrimes.Length]; |
|
|
|
int prime = largePrimes[fieldIndex++ % largePrimes.Length]; |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.ExclusiveOr, expr); |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.ExclusiveOr, expr); |
|
|
|
} |
|
|
|
} |
|
|
|
if (IsValueType(field.ReturnType)) { |
|
|
|
if (IsReferenceType(field.ReturnType)) { |
|
|
|
hashCalculationBlock.AddChild(new ExpressionStatement(expr)); |
|
|
|
hashCalculationBlock.Add(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(field.Name), BinaryOperatorType.InEquality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
hashCalculationBlock.AddChild(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(field.Name), BinaryOperatorType.ReferenceInequality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); |
|
|
|
hashCalculationBlock.Add(new ExpressionStatement(expr)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void AddToBlock(VariableDeclaration hashCodeVar, MethodDeclaration getHashCodeMethod, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IProperty property) |
|
|
|
void AddToBlock(string hashCodeVarName, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IProperty property) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(property.Name), "GetHashCode")); |
|
|
|
Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(property.Name), "GetHashCode")); |
|
|
|
if (usePrimeMultiplication) { |
|
|
|
if (usePrimeMultiplication) { |
|
|
|
int prime = largePrimes[fieldIndex++ % largePrimes.Length]; |
|
|
|
int prime = largePrimes[fieldIndex++ % largePrimes.Length]; |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.ExclusiveOr, expr); |
|
|
|
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.ExclusiveOr, expr); |
|
|
|
} |
|
|
|
} |
|
|
|
if (IsValueType(property.ReturnType)) { |
|
|
|
if (IsReferenceType(property.ReturnType)) { |
|
|
|
hashCalculationBlock.AddChild(new ExpressionStatement(expr)); |
|
|
|
hashCalculationBlock.Add(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(property.Name), BinaryOperatorType.InEquality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
hashCalculationBlock.AddChild(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(property.Name), BinaryOperatorType.ReferenceInequality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); |
|
|
|
hashCalculationBlock.Add(new ExpressionStatement(expr)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
OperatorDeclaration CreateOperatorOverload(OverloadableOperatorType op, IClass currentClass, BlockStatement body) |
|
|
|
OperatorDeclaration CreateOperatorOverload(OperatorType op, ITypeDefinition currentClass, BlockStatement body) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return new OperatorDeclaration() { |
|
|
|
return new OperatorDeclaration() { |
|
|
|
OverloadableOperator = op, |
|
|
|
OperatorType = op, |
|
|
|
TypeReference = new TypeReference("System.Boolean", true), |
|
|
|
ReturnType = ConvertType(KnownTypeCode.Boolean), |
|
|
|
Parameters = { |
|
|
|
Parameters = { |
|
|
|
new ParameterDeclarationExpression(ConvertType(currentClass.DefaultReturnType), "lhs"), |
|
|
|
new ParameterDeclaration(ConvertType(currentClass), "lhs"), |
|
|
|
new ParameterDeclarationExpression(ConvertType(currentClass.DefaultReturnType), "rhs") |
|
|
|
new ParameterDeclaration(ConvertType(currentClass), "rhs") |
|
|
|
}, |
|
|
|
}, |
|
|
|
Modifier = Modifiers.Public | Modifiers.Static, |
|
|
|
Modifiers = Modifiers.Public | Modifiers.Static, |
|
|
|
Body = body |
|
|
|
Body = body |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
@ -441,15 +468,28 @@ namespace CSharpBinding.Refactoring |
|
|
|
{ |
|
|
|
{ |
|
|
|
base.CancelButtonClick(sender, e); |
|
|
|
base.CancelButtonClick(sender, e); |
|
|
|
|
|
|
|
|
|
|
|
editor.Document.Insert(anchor.Offset, baseCall); |
|
|
|
// editor.Document.Insert(anchor.Offset, baseCall);
|
|
|
|
editor.Select(anchor.Offset, baseCall.Length); |
|
|
|
// editor.Select(anchor.Offset, baseCall.Length);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (baseCallNode != null) { |
|
|
|
|
|
|
|
// Insert at least the base call
|
|
|
|
|
|
|
|
MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; |
|
|
|
|
|
|
|
if (insertedOverrideMethod == null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// We are not inside of a method declaration
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
using (Script script = refactoringContext.StartScript()) { |
|
|
|
|
|
|
|
script.AddTo(insertedOverrideMethod.Body, baseCallNode); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected override void OKButtonClick(object sender, System.Windows.RoutedEventArgs e) |
|
|
|
protected override void OKButtonClick(object sender, System.Windows.RoutedEventArgs e) |
|
|
|
{ |
|
|
|
{ |
|
|
|
base.OKButtonClick(sender, e); |
|
|
|
base.OKButtonClick(sender, e); |
|
|
|
|
|
|
|
|
|
|
|
editor.Caret.Offset = insertionEndAnchor.Offset; |
|
|
|
// editor.Caret.Offset = insertionEndAnchor.Offset;
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|