using System.Collections.Generic; using System.Linq; using CTA.WebForms.ClassConverters; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using CTA.WebForms.Extensions; using System; using CTA.WebForms.Metrics; using CTA.WebForms.Services; using CTA.Rules.Config; namespace CTA.WebForms.Factories { public class ClassConverterFactory { private readonly string _sourceProjectPath; private LifecycleManagerService _lifecycleManager; private TaskManagerService _taskManager; private CodeBehindReferenceLinkerService _codeBehindLinkerService; private WebFormMetricContext _metricsContext; public ClassConverterFactory(string sourceProjectPath, LifecycleManagerService lcManager, TaskManagerService taskManager, CodeBehindReferenceLinkerService codeBehindLinkerService, WebFormMetricContext metricsContext) { _sourceProjectPath = sourceProjectPath; _lifecycleManager = lcManager; _taskManager = taskManager; _codeBehindLinkerService = codeBehindLinkerService; _metricsContext = metricsContext; // TODO: Receive services required for ClassConverters // via constructor parameters } public ClassConverter Build(string sourceFileRelativePath, SemanticModel model, TypeDeclarationSyntax typeDeclarationNode) { try { // TODO: Add extra handling for non-ClassDeclarationSyntax // TypeDeclarationSyntax derived types (interfaces, enums, etc.) var symbol = model.GetDeclaredSymbol(typeDeclarationNode); if (symbol.GetAllInheritedBaseTypes().Any(typeSymbol => typeSymbol.Name.Equals(Constants.ExpectedGlobalBaseClass)) && sourceFileRelativePath.EndsWith(Constants.ExpectedGlobalFileName, StringComparison.InvariantCultureIgnoreCase)) { return new GlobalClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _lifecycleManager, _taskManager, _metricsContext); } // NOTE: The order is important from this point on, mainly because // Page-derived classes are also IHttpHandler derived if (symbol.GetAllInheritedBaseTypes().Any(typeSymbol => typeSymbol.Name.Equals(Constants.ExpectedPageBaseClass)) && sourceFileRelativePath.EndsWith(Constants.PageCodeBehindExtension, StringComparison.InvariantCultureIgnoreCase)) { return new PageCodeBehindClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _codeBehindLinkerService, _metricsContext); } if (symbol.GetAllInheritedBaseTypes().Any(typeSymbol => typeSymbol.Name.Equals(Constants.ExpectedControlBaseClass)) && sourceFileRelativePath.EndsWith(Constants.ControlCodeBehindExtension, StringComparison.InvariantCultureIgnoreCase)) { return new ControlCodeBehindClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _codeBehindLinkerService, _metricsContext); } if (symbol.GetAllInheritedBaseTypes().Any(typeSymbol => typeSymbol.Name.Equals(Constants.ExpectedMasterPageBaseClass)) && sourceFileRelativePath.EndsWith(Constants.MasterPageCodeBehindExtension, StringComparison.InvariantCultureIgnoreCase)) { return new MasterPageCodeBehindClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _codeBehindLinkerService, _metricsContext); } if (symbol.AllInterfaces.Any(interfaceSymbol => interfaceSymbol.Name.Equals(Constants.HttpHandlerInterface))) { return new HttpHandlerClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _lifecycleManager, _taskManager, _metricsContext); } if (symbol.AllInterfaces.Any(interfaceSymbol => interfaceSymbol.Name.Equals(Constants.HttpModuleInterface))) { return new HttpModuleClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _lifecycleManager, _taskManager, _metricsContext); } return new UnknownClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _metricsContext); } catch (Exception e) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to build class converter for {sourceFileRelativePath}"); return null; } } public ClassConverter Build(Dictionary<string, string> symbolClassConverterDic, string sourceFileRelativePath, SemanticModel model, TypeDeclarationSyntax typeDeclarationNode) { try { // TODO: Add extra handling for non-ClassDeclarationSyntax // TypeDeclarationSyntax derived types (interfaces, enums, etc.) var symbol = model.GetDeclaredSymbol(typeDeclarationNode); var classConvertName = string.Empty; symbolClassConverterDic.TryGetValue(symbol.ToDisplayString(), out classConvertName); if (classConvertName == "GlobalClassConverter") { return new GlobalClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _lifecycleManager, _taskManager, _metricsContext); } // NOTE: The order is important from this point on, mainly because // Page-derived classes are also IHttpHandler derived if (classConvertName == "PageCodeBehindClassConverter") { return new PageCodeBehindClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _codeBehindLinkerService, _metricsContext); } if (classConvertName == "ControlCodeBehindClassConverter") { return new ControlCodeBehindClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _codeBehindLinkerService, _metricsContext); } if (classConvertName == "MasterPageCodeBehindClassConverter") { return new MasterPageCodeBehindClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _codeBehindLinkerService, _metricsContext); } if (classConvertName == "HttpHandlerClassConverter") { return new HttpHandlerClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _lifecycleManager, _taskManager, _metricsContext); } if (classConvertName == "HttpModuleClassConverter") { return new HttpModuleClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _lifecycleManager, _taskManager, _metricsContext); } return new UnknownClassConverter(sourceFileRelativePath, _sourceProjectPath, model, typeDeclarationNode, symbol, _taskManager, _metricsContext); } catch (Exception e) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to build class converter for {sourceFileRelativePath}"); return null; } } public IEnumerable<ClassConverter> BuildMany(Dictionary<string, string> symbolClassConverterDic, string sourceFileRelativePath, SemanticModel model) { return model.SyntaxTree.GetNamespaceLevelTypes().Select(node => Build(symbolClassConverterDic, sourceFileRelativePath, model, node)) .Where(classConverter => classConverter != null) .ToList(); } } }