Skip to content

Commit

Permalink
Merge pull request #665 from aws/vb-support
Browse files Browse the repository at this point in the history
add visual basic support
  • Loading branch information
cslong authored Jun 28, 2022
2 parents eff922c + 9efb63c commit a978386
Show file tree
Hide file tree
Showing 154 changed files with 7,829 additions and 592 deletions.
46 changes: 23 additions & 23 deletions src/CTA.FeatureDetection.Common/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@
},
"Codelyzer.Analysis": {
"type": "Transitive",
"resolved": "2.4.56-alpha-g89b570063c",
"contentHash": "3u40R9jXqCZ3pLyY3cEFB2WbZIjvF/I+xQcT83oFbJXFAE5n7g1txDjzrv4Vyb9YHQJtlkAIUMkkiFn8dUNt7Q==",
"resolved": "2.4.65-alpha-gd06dae876d",
"contentHash": "8ocAQHFpL7NIVSQ8i518EQWUFujXcHqS/AuQfxHbX64PA6wNAkrBWTsjhKTvI05dLWZ6iHUZm3wQ5uxXU2gPlA==",
"dependencies": {
"Codelyzer.Analysis.Build": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.CSharp": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.VisualBasic": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.Build": "2.4.65-alpha-gd06dae876d",
"Codelyzer.Analysis.CSharp": "2.4.65-alpha-gd06dae876d",
"Codelyzer.Analysis.VisualBasic": "2.4.65-alpha-gd06dae876d",
"CommandLineParser": "2.8.0",
"Microsoft.Build.Utilities.Core": "17.1.0",
"Microsoft.Extensions.Logging.Console": "6.0.0",
Expand All @@ -69,41 +69,41 @@
},
"Codelyzer.Analysis.Build": {
"type": "Transitive",
"resolved": "2.4.56-alpha-g89b570063c",
"contentHash": "hvxLNqVU9Yh/hdTNsmT+qtLgy7J+0Nm1diRjHGKH/FI6ymZkvgAkYB2UWge1JkRAWyRm4WNF4ukgwUlB8WboPA==",
"resolved": "2.4.65-alpha-gd06dae876d",
"contentHash": "1EqRE2hIKMtkzzKpTyzDZrqCDcSjr9iJlM8WYQ7evQ1gT9YOGaUB/iO+/DdW4CsKNbFqhq6RvCeyhu32x272hw==",
"dependencies": {
"Buildalyzer": "4.1.4",
"Buildalyzer.Logger": "4.1.4",
"Buildalyzer.Workspaces": "4.1.4",
"Codelyzer.Analysis.Common": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.Common": "2.4.65-alpha-gd06dae876d",
"Microsoft.Extensions.Logging": "6.0.0",
"NuGet.Packaging": "6.0.0"
}
},
"Codelyzer.Analysis.Common": {
"type": "Transitive",
"resolved": "2.4.56-alpha-g89b570063c",
"contentHash": "ZflwAG3tOHPfqHmfnkUTl5RKuCzN7HqhVF7aw6fwUD1uE93HTdB4VBvkm0n3Nj6Ucv6Mv8UK3bYoYGr0+EYYIQ==",
"resolved": "2.4.65-alpha-gd06dae876d",
"contentHash": "igze76koUw5Ao9XrpjjdyLPobF5CCi9uSb26zLcnrnQsiS1k8m58CQJZKCfsP2mAsGb7HhC5IHmkzxUqGIu29g==",
"dependencies": {
"Codelyzer.Analysis.Model": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.Model": "2.4.65-alpha-gd06dae876d",
"Microsoft.Build": "17.0.0",
"Microsoft.VisualStudio.Setup.Configuration.Interop": "3.1.2196",
"Newtonsoft.Json": "13.0.1"
}
},
"Codelyzer.Analysis.CSharp": {
"type": "Transitive",
"resolved": "2.4.56-alpha-g89b570063c",
"contentHash": "68Z3EFQpRG7OJk3ImRVXjKxRcqZl9nk7E8rrD5fdfy64qd/I9KD8/eJ0xyEPwhwTzv+OsnZQyZ5YidOd9WLIEg==",
"resolved": "2.4.65-alpha-gd06dae876d",
"contentHash": "HlxgpiPQUw53w1c25F3WpO2NMEm+9ZekHmhxNcspy+P2pIHmhRLvmAhkQGYerk+J6teS7oJTHG31w6NgrxgdqA==",
"dependencies": {
"Codelyzer.Analysis.Common": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.Model": "2.4.56-alpha-g89b570063c"
"Codelyzer.Analysis.Common": "2.4.65-alpha-gd06dae876d",
"Codelyzer.Analysis.Model": "2.4.65-alpha-gd06dae876d"
}
},
"Codelyzer.Analysis.Model": {
"type": "Transitive",
"resolved": "2.4.56-alpha-g89b570063c",
"contentHash": "CJCJsGouBGuFe0MCJD3zinuaKQQXfLxT81oqpz2Z+yaTEi420fiHNLvlRzHILnaT3Uu1pBnbkCix5hxHRDWF0A==",
"resolved": "2.4.65-alpha-gd06dae876d",
"contentHash": "E0pzEAkmo1znKadLMIsrINDAffA+7wHaRhSV+IGjayvYx77XAjb54j2U61ysATOz2YXHqU3wQfWU2KhIgGHdZw==",
"dependencies": {
"Microsoft.CodeAnalysis": "4.1.0",
"Microsoft.CodeAnalysis.CSharp": "4.1.0",
Expand All @@ -114,11 +114,11 @@
},
"Codelyzer.Analysis.VisualBasic": {
"type": "Transitive",
"resolved": "2.4.56-alpha-g89b570063c",
"contentHash": "oZ2tGbAMHPjegveNz2hXLEJYxJvOOMjb27W7KNMKpynwLDm+Ck9GloGiGZi/ADpSvrxelYl6JQtYIeACJBu+Iw==",
"resolved": "2.4.65-alpha-gd06dae876d",
"contentHash": "92Y5YpPXOXvlNe8g3ipZ1MQ54yZwl8Ah91OGJEZSTnnb6CwFkgqiIp/mIlFtor/AyUCO/+MEKdqJ7Q/fOgogVQ==",
"dependencies": {
"Codelyzer.Analysis.Common": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.Model": "2.4.56-alpha-g89b570063c"
"Codelyzer.Analysis.Common": "2.4.65-alpha-gd06dae876d",
"Codelyzer.Analysis.Model": "2.4.65-alpha-gd06dae876d"
}
},
"CommandLineParser": {
Expand Down Expand Up @@ -1711,8 +1711,8 @@
"cta.rules.config": {
"type": "Project",
"dependencies": {
"Codelyzer.Analysis": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis.Model": "2.4.56-alpha-g89b570063c",
"Codelyzer.Analysis": "2.4.65-alpha-gd06dae876d",
"Codelyzer.Analysis.Model": "2.4.65-alpha-gd06dae876d",
"Microsoft.Extensions.Logging": "6.0.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"Microsoft.Extensions.Logging.Console": "6.0.0",
Expand Down
27 changes: 17 additions & 10 deletions src/CTA.Rules.Actions/ActionHelpers/FolderUpdate.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using CTA.Rules.Common.Helpers;
using CTA.Rules.Config;
using CTA.Rules.Models;

Expand All @@ -9,12 +10,17 @@ public class FolderUpdate
private readonly string _projectDir;
private readonly string _projectFile;
private readonly ProjectType _projectType;
private readonly string _codeFileExtension;

public FolderUpdate(string projectFile, ProjectType projectType)
{
_projectFile = projectFile;
_projectDir = Directory.GetParent(_projectFile).FullName;
_projectType = projectType;
_codeFileExtension =
VisualBasicUtils.IsVisualBasicProject(projectFile)
? FileExtension.VisualBasic
: FileExtension.CSharp;
}

//TODO Is there a better way to do this?
Expand All @@ -37,25 +43,25 @@ public string Run()
CreateMvcDirs(_projectDir);
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Program);
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Startup);
runResult = "Mvc project detected. Created static files, Program.cs, and Startup.cs";
runResult = $"Mvc project detected. Created static files, Program{_codeFileExtension} and Startup{_codeFileExtension}";
}
if (_projectType == ProjectType.WebApi)
{
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Program);
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Startup);
runResult = "Web API project detected. Created Program.cs and Startup.cs";
runResult = $"Web API project detected. Created Program{_codeFileExtension} and Startup{_codeFileExtension}";
}
if (_projectType == ProjectType.WCFConfigBasedService)
{
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Program);
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Startup);
runResult = "WCF Config Based Service Project detected. Created Program.cs and Startup.cs";
runResult = $"WCF Config Based Service Project detected. Created Program{_codeFileExtension} and Startup{_codeFileExtension}";
}
if (_projectType == ProjectType.WCFCodeBasedService)
{
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Program);
CreateStartupFiles(_projectDir, _projectType, FileTypeCreation.Startup);
runResult = "WCF Code Based Service Project detected. Created Program.cs and Startup.cs";
runResult = $"WCF Code Based Service Project detected. Created Program{_codeFileExtension} and Startup{_codeFileExtension}";
}
return runResult;
}
Expand All @@ -69,14 +75,14 @@ private void CreateStartupFiles(string projectDir, ProjectType projectType, File
{
string projectNamespace = GetProjectNamespace();

var file = Path.Combine(projectDir, string.Concat(fileType.ToString(), ".cs"));
var file = Path.Combine(projectDir, string.Concat(fileType.ToString(), _codeFileExtension));
if (File.Exists(file))
{
File.Move(file, string.Concat(file, ".bak"));
File.Move(file, string.Concat(file, FileExtension.Backup));
}
File.WriteAllText(file, GetStartupFileContent(projectNamespace, projectType, fileType));
File.WriteAllText(file, GetStartupFileContent(projectNamespace, projectType, fileType, _codeFileExtension));

LogChange(string.Format("Created {0}.cs file using {1} template", fileType.ToString(), projectType.ToString()));
LogChange(string.Format("Created {0}{2} file using {1} template", fileType.ToString(), projectType.ToString(), _codeFileExtension));
}

/// <summary>
Expand All @@ -85,10 +91,11 @@ private void CreateStartupFiles(string projectDir, ProjectType projectType, File
/// <param name="projectNamespace">The project namespace</param>
/// <param name="projectType">The project type</param>
/// <param name="fileType">Type of the file to be retrieved</param>
/// <param name="fileExtension">Extension of file to be retrieved</param>
/// <returns>The content of the startup file</returns>
private string GetStartupFileContent(string projectNamespace, ProjectType projectType, FileTypeCreation fileType)
private string GetStartupFileContent(string projectNamespace, ProjectType projectType, FileTypeCreation fileType, string fileExtension)
{
return TemplateHelper.GetTemplateFileContent(projectNamespace, projectType, fileType.ToString() + ".cs");
return TemplateHelper.GetTemplateFileContent(projectNamespace, projectType, fileType.ToString() + fileExtension);
}

/// <summary>
Expand Down
201 changes: 201 additions & 0 deletions src/CTA.Rules.Actions/ActionLoaderBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using CTA.Rules.Models;
using Codelyzer.Analysis;
using CTA.Rules.Config;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Newtonsoft.Json;

namespace CTA.Rules.Actions;

public class ActionLoaderBase
{
protected List<MethodInfo> projectLevelActions,
projectFileActions,
projectTypeActions,
memberAccessActions;

protected object projectLevelObject,
projectFileObject,
projectTypeObject,
memberAccessObject;

public Func<string, ProjectType, string> GetProjectLevelActions(string name, dynamic value) =>
GetAction<Func<string, ProjectType, string>>
(projectLevelActions, projectLevelObject, name, value);
public Func<string, ProjectType, List<string>, Dictionary<string, string>, List<string>, List<string>, string> GetProjectFileActions(string name, dynamic value) =>
GetAction<Func<string, ProjectType, List<string>, Dictionary<string, string>, List<string>, List<string>, string>>
(projectFileActions, projectFileObject, name, value);
public Func<ProjectType, ProjectConfiguration, ProjectResult, AnalyzerResult, string> GetProjectTypeActions(string name, dynamic value) =>
GetAction<Func<ProjectType, ProjectConfiguration, ProjectResult, AnalyzerResult, string>>
(projectTypeActions, projectTypeObject, name, value);
public Func<SyntaxGenerator, SyntaxNode, SyntaxNode> GetMemberAccessExpressionActions(string name, dynamic value) =>
GetAction<Func<SyntaxGenerator, SyntaxNode, SyntaxNode>>
(memberAccessActions, memberAccessObject, name, value);

#region helper functions

/// <summary>
/// Gets the action by invoking the methods that will create it
/// </summary>
/// <typeparam name="T">The type of the object</typeparam>
/// <param name="actions">List of actions on the type T</param>
/// <param name="invokeObject">The object that will be used to retrieve the action</param>
/// <param name="name">Name of the action</param>
/// <param name="value">Parameter(s) of the action</param>
/// <returns></returns>
public static T GetAction<T>(List<MethodInfo> actions, object invokeObject, string name, dynamic value)
{
T val = default;
try
{
string actionName = GetActionName(name);
var method = actions.FirstOrDefault(m => m.Name == actionName);
if (method == null)
{
LogHelper.LogDebug(string.Format("No such action {0}", actionName));
}
else
{
var parameters = GetParameters(value, method);

if (parameters != null)
{
val = (T)method.Invoke(invokeObject, parameters);
}
}
}
catch (Exception ex)
{
LogHelper.LogError(ex, "Error while loading action {0}", name);
}
return val;
}

public static List<Assembly> GetAssemblies(List<string> assemblyPaths)
{
var actionsAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.FullName.Contains("CTA.Rules.Actions"));

var assemblies = new List<Assembly>
{
actionsAssembly
};

foreach (var path in assemblyPaths)
{
try
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
assemblies.Add(assembly);
}
catch (Exception ex)
{
LogHelper.LogError(ex,
string.Format("Error loading assembly from {0}{1}{2}", path, Environment.NewLine, ex.Message));
}
}

return assemblies;
}

private static string GetActionName(string name)
{
return string.Concat("Get", name, "Action");
}

protected static List<MethodInfo> GetFuncMethods(Type t) => t.GetMethods().Where(m => m.ReturnType.ToString().Contains("System.Func")).ToList();

protected static bool TryCreateInstance(string actionName, List<Type> types, out object obj)
{
obj = null;
var type = types.FirstOrDefault(t => t.Name == actionName);
if (type == null)
{
return false;
}
obj = Activator.CreateInstance(type);
return true;
}

private static List<string> GetJsonParameters(string value, MethodInfo method)
{
List<string> result = new List<string>();

Dictionary<string, string> jsonParameters = JsonConvert.DeserializeObject<Dictionary<string, string>>(value);

var methodParams = method.GetParameters();
foreach (var p in methodParams)
{
if (jsonParameters.ContainsKey(p.Name))
{
result.Add(jsonParameters[p.Name]);
}
else if(p.IsOptional)
{
result.Add(p.HasDefaultValue && p.DefaultValue != null ? p.DefaultValue.ToString() : null);
}
else
{
LogHelper.LogDebug(string.Format("Parameter {0} is not available for action {1}", p.Name, method.Name));
return null;
}
}
return result;
}
/// <summary>
/// Gets the parameters for the action. The parameters should match the action signature in the provided rules file
/// </summary>
/// <param name="value">The paramter(s) as a string or JSON object</param>
/// <param name="method">The method for these parameters</param>
/// <returns></returns>
private static string[] GetParameters(dynamic value, MethodInfo method)
{
List<string> result = new List<string>();

try
{
if (value is string)
{
var strValue = value.ToString();
if (strValue.StartsWith("{"))
{
try
{
result = GetJsonParameters(value.ToString(), method);
}
catch (Exception)
{
result = new List<string>() { value };
}
}
else
{
result = new List<string>() { value };
var optionalParameters = method.GetParameters().Where(p => p.IsOptional);
// This should only run if optional parameter was not inlcuded originally.
// TODO: We do not support ONLY optional parameters > 1 at this time, this logic would need to be re-written properly, that scenario would fail at val = (T)method.Invoke(invokeObject, parameters);
if (optionalParameters.Any() && method.GetParameters().Count() > 1)
{
result.AddRange(optionalParameters.Select(p => p.HasDefaultValue && p.DefaultValue != null ? p.DefaultValue.ToString() : null));
}
}
}
else
{
result = GetJsonParameters(value.ToString(), method);
}
}
catch (Exception ex)
{
LogHelper.LogError(ex, "Error while loading parameters for action {0}", method.Name);
}
return result.ToArray();
}

#endregion
}
Loading

0 comments on commit a978386

Please sign in to comment.