using System;
using System.Linq;
using CTA.Rules.Actions.ActionHelpers;
using CTA.Rules.Config;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
namespace CTA.Rules.Actions.VisualBasic
{
///
/// List of actions that can run on Invocation Expressions
///
public class InvocationExpressionActions
{
///
/// This Method replaces the entire expression including the parameters. For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs(9.5) would return Abs(9.5) not Math.Abs(9.5).
/// Please ensure to include the prefix part of the matching invocation expression since this method will replace it and the parameters as well.
///
/// The new invocation expression including the method to replace with and the parameters.
/// The new invocation expression parameters to replace the old parameters with.
///
public Func GetReplaceMethodWithObjectAndParametersAction(string newMethod, string newParameters)
{
//TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parse expression ?
InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
node = SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName(newMethod),
SyntaxFactory.ParseArgumentList(newParameters))
.WithLeadingTrivia(node.GetLeadingTrivia())
.NormalizeWhitespace();
return node;
}
return ReplaceMethod;
}
///
/// This Method replaces the entire expression up to the matching invocation expression. For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs would return Abs(5.5) not Math.Abs(5.5).
/// Please ensure to include the prefix part of the matching invocation expression since this method will replace it.
///
/// The new invocation expression including the method to replace with.
///
public Func GetReplaceMethodWithObjectAction(string newMethod)
{
//TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ?
InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
node = node.WithExpression(SyntaxFactory.ParseExpression(newMethod))
.WithLeadingTrivia(node.GetLeadingTrivia())
.NormalizeWhitespace();
return node;
}
return ReplaceMethod;
}
///
/// This Method replaces the entire expression up to the matching invocation expression. It also adds the type from the typed argument list to the list of arguments.
/// Example: DependencyResolver.Current.GetService
/// The new invocation expression including the method to replace with.
///
public Func GetReplaceMethodWithObjectAddTypeAction(string newMethod)
{
InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
var typeToAdd = node.Expression.DescendantNodes()?.OfType()?.FirstOrDefault()?.TypeArgumentList;
if (typeToAdd != null)
{
var argumentList = SyntaxFactory.ParseArgumentList($"(TypeOf {typeToAdd.Arguments.ToString()})");
node = node.WithArgumentList(argumentList).WithLeadingTrivia(node.GetLeadingTrivia());
}
// in the test, the NormalizeWhitespace() was added weird white spaces .GetService(TypeOf Object )"
node = node.WithExpression(SyntaxFactory.ParseExpression(newMethod)).WithLeadingTrivia(node.GetLeadingTrivia());
return node;
}
return ReplaceMethod;
}
///
/// This Method replaces only matching method in the invocation expression and its parameters.
/// For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs and an oldMethod parameter of Round with parameters of 8.5 would return Math.Abs(8.5).
///
/// The matching method in the invocation expression to be replaced.
/// The new method to replace the old method with in the invocation expression.
/// The new invocation expression parameters to replace the old parameters with.
///
public Func GetReplaceMethodAndParametersAction(string oldMethod, string newMethod, string newParameters)
{
//TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ?
InvocationExpressionSyntax ReplaceOnlyMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
node = node.WithExpression(SyntaxFactory.ParseExpression(node.Expression.ToString().Replace(oldMethod, newMethod)))
.WithArgumentList(SyntaxFactory.ParseArgumentList(newParameters))
.WithLeadingTrivia(node.GetLeadingTrivia())
.NormalizeWhitespace();
return node;
}
return ReplaceOnlyMethod;
}
///
/// This Method replaces only matching method in the invocation expression. For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs and an oldMethod parameter of Round would return Math.Abs(5.5).
///
/// The matching method in the invocation expression to be replaced.
/// The new method to replace the old method with in the invocation expression.
///
public Func GetReplaceMethodOnlyAction(string oldMethod, string newMethod)
{
//TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ?
InvocationExpressionSyntax ReplaceOnlyMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
node = node.WithExpression(SyntaxFactory.ParseExpression(node.Expression.ToString().Replace(oldMethod, newMethod)))
.WithLeadingTrivia(node.GetLeadingTrivia())
.NormalizeWhitespace();
return node;
}
return ReplaceOnlyMethod;
}
///
/// This Method replaces only the parameters in the invocation expression. For example, Math.Round(5.5) invocation expression matching on Round with a parameter of (8) would return Math.Abs(8).
///
/// The new invocation expression parameters to replace the old parameters with.
///
public Func GetReplaceParametersOnlyAction(string newParameters)
{
//TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ?
InvocationExpressionSyntax ReplaceOnlyMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
node = node.WithArgumentList(SyntaxFactory.ParseArgumentList(newParameters)).NormalizeWhitespace();
return node;
}
return ReplaceOnlyMethod;
}
public Func GetAppendMethodAction(string appendMethod)
{
InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
var operatorToken = node.DescendantTokens().FirstOrDefault(t => t.IsKind(SyntaxKind.DotToken));
node = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.InvocationExpression(node),
operatorToken,
SyntaxFactory.IdentifierName(SyntaxFactory.ParseName(appendMethod).ToString())
),
SyntaxFactory.ArgumentList()
).NormalizeWhitespace();
return node;
}
return ReplaceMethod;
}
public Func GetAddCommentAction(string comment)
{
InvocationExpressionSyntax AddComment(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node)
{
return (InvocationExpressionSyntax)CommentHelper.AddVBComment(node, comment);
}
return AddComment;
}
}
}