using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Codelyzer.Analysis; using Codelyzer.Analysis.Build; using Codelyzer.Analysis.Model; using CTA.Rules.Analyzer; using CTA.Rules.Common.Helpers; using CTA.Rules.Config; using CTA.Rules.Models; using CTA.Rules.Models.Tokens; using CTA.Rules.RuleFiles; namespace CTA.Rules.Update { /// /// Runs rule updates on a Project /// public class ProjectRewriter { public ProjectConfiguration ProjectConfiguration; protected readonly List _sourceFileResults; protected readonly List _sourceFileBuildResults; protected readonly List _projectReferences; protected readonly ProjectResult _projectResult; protected readonly List _metaReferences; protected readonly AnalyzerResult _analyzerResult; protected readonly ProjectLanguage _projectLanguage; private IRulesAnalysis _rulesAnalyzer; /// /// Initializes a new instance of ProjectRewriter using an existing analysis /// /// The analysis results of the project /// ProjectConfiguration for this project public ProjectRewriter(AnalyzerResult analyzerResult, ProjectConfiguration projectConfiguration) { _projectResult = new ProjectResult() { ProjectFile = projectConfiguration.ProjectPath, TargetVersions = projectConfiguration.TargetVersions, SourceVersions = projectConfiguration.SourceVersions, UpgradePackages = projectConfiguration.PackageReferences.Select(p => new PackageAction() { Name = p.Key, OriginalVersion = p.Value.Item1, Version = p.Value.Item2 }).ToList(), MissingMetaReferences = analyzerResult?.ProjectBuildResult?.MissingReferences }; _analyzerResult = analyzerResult; _sourceFileBuildResults = analyzerResult?.ProjectBuildResult?.SourceFileBuildResults; _sourceFileResults = analyzerResult?.ProjectResult?.SourceFileResults; _projectReferences = analyzerResult?.ProjectBuildResult?.ExternalReferences?.ProjectReferences.Select(p => p.AssemblyLocation).ToList(); _metaReferences = analyzerResult?.ProjectBuildResult?.Project?.MetadataReferences?.Select(m => m.Display).ToList() ?? projectConfiguration.MetaReferences; ProjectConfiguration = projectConfiguration; _projectLanguage = VisualBasicUtils.IsVisualBasicProject(ProjectConfiguration.ProjectPath) ? ProjectLanguage.VisualBasic : ProjectLanguage.Csharp; } public ProjectRewriter(IDEProjectResult projectResult, ProjectConfiguration projectConfiguration) { _sourceFileResults = projectResult.RootNodes; _sourceFileBuildResults = projectResult.SourceFileBuildResults; ProjectConfiguration = projectConfiguration; _projectResult = new ProjectResult() { ProjectFile = projectConfiguration.ProjectPath, TargetVersions = projectConfiguration.TargetVersions, SourceVersions = projectConfiguration.SourceVersions, UpgradePackages = projectConfiguration.PackageReferences.Select(p => new PackageAction() { Name = p.Key, OriginalVersion = p.Value.Item1, Version = p.Value.Item2 }).ToList() }; _projectLanguage = VisualBasicUtils.IsVisualBasicProject(ProjectConfiguration.ProjectPath) ? ProjectLanguage.VisualBasic : ProjectLanguage.Csharp; } /// /// Initializes the project rewriter by getting a list of actions that will be run /// /// A list of project actions to be run public ProjectResult Initialize() { ProjectActions projectActions = new ProjectActions(); try { var allReferences = _sourceFileResults ?.SelectMany(s => s.References) .Union( _sourceFileResults .SelectMany(s => s.Children.OfType()) .Select(u => new Reference {Namespace = u.Identifier, Assembly = u.Identifier}) .Distinct()) .Union( _sourceFileResults .SelectMany(s => s.Children.OfType()) .Select(u => new Reference {Namespace = u.Identifier, Assembly = u.Identifier}) .Distinct()) .Union( ProjectConfiguration .AdditionalReferences .Select(r => new Reference {Assembly = r, Namespace = r})); var rulesFileLoader = new RulesFileLoader( allReferences, ProjectConfiguration.RulesDir, ProjectConfiguration.TargetVersions, _projectLanguage, overrideFile: string.Empty, ProjectConfiguration.AssemblyDir); var projectRules = rulesFileLoader.Load(); HashSet projectTokens; if (_projectLanguage == ProjectLanguage.VisualBasic) { _rulesAnalyzer = new VisualBasicRulesAnalysis(_sourceFileResults, projectRules.VisualBasicRootNodes, ProjectConfiguration.ProjectType); projectTokens = projectRules.VisualBasicRootNodes.ProjectTokens; } else { _rulesAnalyzer = new RulesAnalysis(_sourceFileResults, projectRules.CsharpRootNodes, ProjectConfiguration.ProjectType); projectTokens = projectRules.CsharpRootNodes.ProjectTokens; } projectActions = _rulesAnalyzer.Analyze(); _projectReferences.ForEach(p => { projectActions.ProjectReferenceActions.Add(Config.Utils.GetRelativePath(ProjectConfiguration.ProjectPath, p)); }); _projectResult.ActionPackages = projectActions.PackageActions.Distinct().ToList(); _projectResult.MetaReferences = _metaReferences; foreach (var p in ProjectConfiguration.PackageReferences) { projectActions.PackageActions.Add(new PackageAction() { Name = p.Key, OriginalVersion = p.Value.Item1, Version = p.Value.Item2 }); } MergePackages(projectActions.PackageActions); projectActions.ProjectLevelActions = projectTokens.SelectMany(p => p.ProjectTypeActions).Distinct().ToList(); projectActions.ProjectLevelActions.AddRange(projectTokens.SelectMany(p => p.ProjectLevelActions).Distinct()); projectActions.ProjectLevelActions.AddRange(projectTokens.SelectMany(p => p.ProjectFileActions).Distinct()); projectActions.CsharpProjectRules = projectRules.CsharpRootNodes; projectActions.VbProjectRules = projectRules.VisualBasicRootNodes; _projectResult.ProjectActions = projectActions; _projectResult.FeatureType = ProjectConfiguration.ProjectType; } catch (Exception ex) { LogHelper.LogError(ex, "Error while initializing project {0}", ProjectConfiguration.ProjectPath); } return _projectResult; } /// /// Initializes the ProjectRewriter then runs it /// public virtual ProjectResult Run() { var projectResult = Initialize(); return Run(projectResult.ProjectActions); } /// /// Runs the project rewriter using a previously initialized analysis /// /// public virtual ProjectResult Run(ProjectActions projectActions) { _projectResult.ProjectActions = projectActions; CodeReplacer baseReplacer = new CodeReplacer(_sourceFileBuildResults, ProjectConfiguration, _metaReferences, _analyzerResult, _projectLanguage, projectResult: _projectResult); _projectResult.ExecutedActions = baseReplacer.Run(projectActions, ProjectConfiguration.ProjectType); return _projectResult; } public virtual List RunIncremental(List updatedFiles, RootNodes projectRules) { var allReferences = _sourceFileResults?.SelectMany(s => s.References).Distinct(); RulesFileLoader rulesFileLoader = new RulesFileLoader(allReferences, Constants.RulesDefaultPath, ProjectConfiguration.TargetVersions, _projectLanguage, string.Empty, ProjectConfiguration.AssemblyDir); var rules = rulesFileLoader.Load(); HashSet projectTokens; if (_projectLanguage == ProjectLanguage.VisualBasic) { _rulesAnalyzer = new VisualBasicRulesAnalysis(_sourceFileResults, rules.VisualBasicRootNodes, ProjectConfiguration.ProjectType); projectTokens = rules.VisualBasicRootNodes.ProjectTokens; } else { _rulesAnalyzer = new RulesAnalysis(_sourceFileResults, rules.CsharpRootNodes, ProjectConfiguration.ProjectType); projectTokens = rules.CsharpRootNodes.ProjectTokens; } var projectActions = _rulesAnalyzer.Analyze(); CodeReplacer baseReplacer = new CodeReplacer(_sourceFileBuildResults, ProjectConfiguration, _metaReferences, _analyzerResult, _projectLanguage, updatedFiles, projectResult: _projectResult); _projectResult.ExecutedActions = baseReplacer.Run(projectActions, ProjectConfiguration.ProjectType); List ideFileActions = projectActions .FileActions .SelectMany(f => f.NodeTokens.Select(n => new IDEFileActions() { TextSpan = n.TextSpan, Description = n.Description, FilePath = f.FilePath, TextChanges = n.TextChanges })) .ToList(); return ideFileActions; } /// /// Merges the packages from packageActions with the packages in the ProjectConfiguration /// /// A list of packages and their versions to add to the project private void MergePackages(BlockingCollection packageActions) { if (ProjectConfiguration.PackageReferences != null) { foreach (var package in ProjectConfiguration.PackageReferences.Keys) { var versionTuple = ProjectConfiguration.PackageReferences[package]; var version = versionTuple != null ? versionTuple.Item2 ?? "*" : "*"; packageActions.Add(new FilePackageAction() { Name = package, Version = version }); } } } } }