using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Codelyzer.Analysis.Model; using CTA.Rules.Config; using CTA.Rules.Models; using CTA.Rules.Models.RulesFiles; using Newtonsoft.Json; namespace CTA.Rules.RuleFiles { /// /// Loads the rules file(s) /// public class RulesFileLoader { private readonly string _rulesFilesDir; private readonly string _targetFramework; private readonly string _overrideFile; private readonly string _assembliesDir; private readonly IEnumerable _projectReferences; private readonly ProjectLanguage _projectLanguage; /// /// Initializes a new RulesFileLoader /// /// References in the project to filter the rules by /// Directory containing rules file(s) to use /// Target framework to port to /// Path to rules file containing override rules. The override rules will be added to the built in rules, overriding any matching existing rules /// Directory containing assemblies containing additional actions /// The language the project is written in, C# or VB public RulesFileLoader(IEnumerable projectReferences, string rulesFilesDir, List targetFramework, ProjectLanguage projectLanguage, string overrideFile = "", string assembliesDir = "") { _rulesFilesDir = rulesFilesDir; try { _targetFramework = targetFramework.First(); if (targetFramework.Count > 1) { LogHelper.LogDebug("Please specify one target version. Multiple target versions is not supported"); } } catch (Exception ex) { LogHelper.LogError(ex, "Please specify one target version. Multiple target versions is not supported"); } _overrideFile = overrideFile; _assembliesDir = assembliesDir; _projectReferences = projectReferences; _projectLanguage = projectLanguage; } /// /// Loads rules from the main rules file and override file /// /// A RootNodes object containing all the rules after being merged public RulesFileLoaderResponse Load() { var mainNamespaceFileTasks = new Task(() => { NamespaceRecommendations rulesFile = new NamespaceRecommendations(); if (!string.IsNullOrEmpty(_rulesFilesDir) && Directory.Exists(_rulesFilesDir)) { rulesFile = LoadNamespaceFile(_rulesFilesDir); } return rulesFile; }); mainNamespaceFileTasks.Start(); var mainFileTask = new Task(() => { Rootobject rules = new Rootobject(); if (!string.IsNullOrEmpty(_rulesFilesDir) && Directory.Exists(_rulesFilesDir)) { rules = LoadRulesFiles(_rulesFilesDir); if (rules.NameSpaces != null) { rules.NameSpaces = rules.NameSpaces.Where(n => _projectReferences.Contains(new Reference() { Assembly = n.Assembly, Namespace = n.@namespace }) || (n.Assembly == Constants.Project)).ToList(); } } return rules; }); mainFileTask.Start(); var overrideNamespaceFileTasks = new Task(() => { NamespaceRecommendations rulesFile = new NamespaceRecommendations(); if (!string.IsNullOrEmpty(_overrideFile) && Directory.Exists(_overrideFile)) { rulesFile = LoadNamespaceFile(_overrideFile); } return rulesFile; }); overrideNamespaceFileTasks.Start(); var overrideTask = new Task(() => { Rootobject rules = new Rootobject(); if (!string.IsNullOrEmpty(_overrideFile) && Directory.Exists(_overrideFile)) { rules = LoadRulesFiles(_overrideFile); if (rules.NameSpaces != null) { rules.NameSpaces = rules.NameSpaces.Where(n => _projectReferences.Contains(new Reference() { Assembly = n.Assembly, Namespace = n.@namespace }) || (n.Assembly == Constants.Project && n.@namespace == Constants.Project)).ToList(); } } return rules; }); overrideTask.Start(); Task.WaitAll(mainNamespaceFileTasks, overrideNamespaceFileTasks, mainFileTask, overrideTask); var response = new RulesFileLoaderResponse(); if (_projectLanguage == ProjectLanguage.VisualBasic) { var rulesFileParser = new VisualBasicRulesFileParser(mainNamespaceFileTasks.Result, overrideNamespaceFileTasks.Result, mainFileTask.Result, overrideTask.Result, _assembliesDir, _targetFramework); var rootNodes = rulesFileParser.Process(); response.VisualBasicRootNodes = rootNodes; } else { RulesFileParser rulesFileParser = new RulesFileParser(mainNamespaceFileTasks.Result, overrideNamespaceFileTasks.Result, mainFileTask.Result, overrideTask.Result, _assembliesDir, _targetFramework); var rootNodes = rulesFileParser.Process(); response.CsharpRootNodes = rootNodes; } return response; } public NamespaceRecommendations LoadNamespaceFile(string pathToLoad) { NamespaceRecommendations nr = new NamespaceRecommendations(); var ruleFiles = Directory .EnumerateFiles(pathToLoad, "*.json", SearchOption.AllDirectories) .Where(r => _projectReferences .Select(p => p.Namespace?.ToLower()) .Contains(Path.GetFileNameWithoutExtension(r))) .ToList(); foreach (var ruleFile in ruleFiles) { try { var content = File.ReadAllText(ruleFile); var currentNode = JsonConvert.DeserializeObject(content); if (currentNode != null && !string.IsNullOrEmpty(currentNode.Name)) { nr.NameSpaces.Add(currentNode); } } catch (Exception ex) { LogHelper.LogError("Error parsing file: {0}{1} Exception:{2}", ruleFile, Environment.NewLine, ex.Message); return nr; } } return nr; } private Rootobject LoadRulesFiles(string ruleFilesDir) { Rootobject r = new Rootobject(); var rulesFiles = Directory.EnumerateFiles(ruleFilesDir, "*.json", SearchOption.AllDirectories); foreach (var rulesFile in rulesFiles) { try { var content = File.ReadAllText(rulesFile); var currentNode = JsonConvert.DeserializeObject(content); r.NameSpaces.AddRange(currentNode.NameSpaces); } catch (Exception ex) { LogHelper.LogError("Error parsing file: {0}{1} Exception:{2}", rulesFile, Environment.NewLine, ex.Message); } } return r; } } }