using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Text.RegularExpressions; using System.Xml.Linq; namespace MarketAlly.ProjectDetector; public class ProjectAnalysis { // Project Information public string ProjectName { get; set; } = string.Empty; // Solution Information public bool HasSolutionFile { get; set; } // SDK Information public string Sdk { get; set; } = string.Empty; public bool HasMauiSdk { get; set; } public bool HasWebSdk { get; set; } public bool HasWorkerSdk { get; set; } public bool HasDesktopSdk { get; set; } // Output Information public string OutputType { get; set; } = string.Empty; public bool IsLibrary { get; set; } public bool IsExecutable { get; set; } public List TargetFrameworks { get; set; } = new(); // Package Management Files public List PackageReferences { get; set; } = new(); public List ProjectReferences { get; set; } = new(); // Node.js Analysis public Dictionary PackageJsonDependencies { get; set; } = new(); public Dictionary PackageJsonDevDependencies { get; set; } = new(); public Dictionary PackageJsonScripts { get; set; } = new(); // Python Analysis public List PythonRequirements { get; set; } = new(); public Dictionary PyProjectConfig { get; set; } = new(); // Java Analysis public Dictionary MavenConfig { get; set; } = new(); public Dictionary GradleConfig { get; set; } = new(); // Other Language Analysis public Dictionary GoModConfig { get; set; } = new(); public Dictionary CargoConfig { get; set; } = new(); // Project Structure public List Folders { get; set; } = new(); public List Files { get; set; } = new(); // Specific Technology Markers public bool HasAspNetCore { get; set; } public bool HasBlazor { get; set; } public bool HasMaui { get; set; } public bool HasWpf { get; set; } public bool HasWinForms { get; set; } public bool HasXamarin { get; set; } public bool HasEntityFramework { get; set; } public bool HasTestFramework { get; set; } public bool HasWorkerService { get; set; } public bool HasAzureFunctions { get; set; } public bool HasCommandLine { get; set; } public bool HasPluginFramework { get; set; } public bool HasInfrastructure { get; set; } // Analysis Results public int ConfidenceScore { get; set; } public string DetectionReason { get; set; } = string.Empty; public List DetectionEvidence { get; set; } = new(); public void AnalyzeProjectFile(string projectFilePath) { try { var extension = Path.GetExtension(projectFilePath).ToLowerInvariant(); switch (extension) { case ".csproj": case ".vbproj": case ".fsproj": AnalyzeDotNetProject(projectFilePath); break; case ".json" when Path.GetFileName(projectFilePath).Equals("package.json", StringComparison.OrdinalIgnoreCase): AnalyzePackageJson(projectFilePath); break; case ".xml" when Path.GetFileName(projectFilePath).Equals("pom.xml", StringComparison.OrdinalIgnoreCase): AnalyzePomXml(projectFilePath); break; case ".gradle" when Path.GetFileName(projectFilePath).Equals("build.gradle", StringComparison.OrdinalIgnoreCase): AnalyzeBuildGradle(projectFilePath); break; } } catch (Exception ex) { DetectionEvidence.Add($"Error reading project file: {ex.Message}"); } } private void AnalyzeDotNetProject(string csprojPath) { try { var content = File.ReadAllText(csprojPath); // Try XML parsing first for better accuracy try { var doc = XDocument.Parse(content); AnalyzeProjectXml(doc); } catch { // Fallback to string parsing AnalyzeProjectString(content); } } catch (Exception ex) { DetectionEvidence.Add($"Error reading .NET project file: {ex.Message}"); } } private void AnalyzeProjectXml(XDocument doc) { var project = doc.Root; // SDK Detection Sdk = project?.Attribute("Sdk")?.Value ?? string.Empty; HasMauiSdk = Sdk.Contains("Microsoft.NET.Sdk.Maui"); HasWebSdk = Sdk.Contains("Microsoft.NET.Sdk.Web"); HasWorkerSdk = Sdk.Contains("Microsoft.NET.Sdk.Worker"); HasDesktopSdk = Sdk.Contains("Microsoft.NET.Sdk.WindowsDesktop"); // Properties var properties = project?.Descendants("PropertyGroup"); foreach (var propGroup in properties ?? Enumerable.Empty()) { OutputType = propGroup.Element("OutputType")?.Value ?? OutputType; // Target frameworks var targetFramework = propGroup.Element("TargetFramework")?.Value; if (!string.IsNullOrEmpty(targetFramework)) TargetFrameworks.Add(targetFramework); var targetFrameworks = propGroup.Element("TargetFrameworks")?.Value; if (!string.IsNullOrEmpty(targetFrameworks)) TargetFrameworks.AddRange(targetFrameworks.Split(';')); // Technology markers HasMaui = HasMaui || propGroup.Element("UseMaui")?.Value == "true"; HasWpf = HasWpf || propGroup.Element("UseWPF")?.Value == "true"; HasWinForms = HasWinForms || propGroup.Element("UseWindowsForms")?.Value == "true"; // Worker service HasWorkerService = HasWorkerService || propGroup.Element("Worker")?.Value == "true"; // Executable/Library detection if (propGroup.Element("GeneratePackageOnBuild")?.Value == "true") { DetectionEvidence.Add("Has GeneratePackageOnBuild=true"); } var appMetadata = new[] { "ApplicationTitle", "ApplicationId", "ApplicationVersion", "ApplicationDisplayVersion" }; foreach (var metadata in appMetadata) { if (propGroup.Element(metadata) != null) { DetectionEvidence.Add($"Has {metadata} (app metadata)"); } } // Check for package metadata var packageMetadata = new[] { "PackageId", "Version", "Authors", "Description", "GeneratePackageOnBuild" }; foreach (var metadata in packageMetadata) { if (propGroup.Element(metadata) != null) { DetectionEvidence.Add($"Has {metadata} (NuGet package metadata)"); } } } // Package References var packageRefs = project?.Descendants("PackageReference"); foreach (var packageRef in packageRefs ?? Enumerable.Empty()) { var include = packageRef.Attribute("Include")?.Value; if (!string.IsNullOrEmpty(include)) { PackageReferences.Add(include); } } // Project References var projectRefs = project?.Descendants("ProjectReference"); foreach (var projectRef in projectRefs ?? Enumerable.Empty()) { var include = projectRef.Attribute("Include")?.Value; if (!string.IsNullOrEmpty(include)) { ProjectReferences.Add(include); } } AnalyzePackageReferences(); IsLibrary = OutputType.Equals("Library", StringComparison.OrdinalIgnoreCase) || (string.IsNullOrEmpty(OutputType) && !IsExecutable); IsExecutable = OutputType.Equals("Exe", StringComparison.OrdinalIgnoreCase) || OutputType.Equals("WinExe", StringComparison.OrdinalIgnoreCase); } private void AnalyzeProjectString(string content) { // SDK Detection var sdkMatch = Regex.Match(content, @"([^<]+)"); if (outputTypeMatch.Success) { OutputType = outputTypeMatch.Groups[1].Value; } // Package References var packageMatches = Regex.Matches(content, @"true"); HasWpf = HasWpf || content.Contains("true"); HasWinForms = HasWinForms || content.Contains("true"); AnalyzePackageReferences(); IsLibrary = OutputType.Equals("Library", StringComparison.OrdinalIgnoreCase); IsExecutable = OutputType.Equals("Exe", StringComparison.OrdinalIgnoreCase) || OutputType.Equals("WinExe", StringComparison.OrdinalIgnoreCase); } private void AnalyzePackageReferences() { var packages = string.Join(" ", PackageReferences); HasAspNetCore = PackageReferences.Any(p => p.Contains("Microsoft.AspNetCore")); HasBlazor = PackageReferences.Any(p => p.Contains("Microsoft.AspNetCore.Components")); HasEntityFramework = PackageReferences.Any(p => p.Contains("EntityFramework")); HasXamarin = PackageReferences.Any(p => p.Contains("Xamarin")); // Test frameworks var testFrameworks = new[] { "xunit", "nunit", "mstest", "Microsoft.NET.Test.Sdk" }; HasTestFramework = PackageReferences.Any(p => testFrameworks.Any(tf => p.Contains(tf, StringComparison.OrdinalIgnoreCase))); // Azure Functions var azureFunctionsPackages = new[] { "Microsoft.NET.Sdk.Functions", "Microsoft.Azure.Functions", "Microsoft.Azure.WebJobs" }; HasAzureFunctions = PackageReferences.Any(p => azureFunctionsPackages.Any(af => p.Contains(af))); // Command line packages var commandLinePackages = new[] { "CommandLineParser", "System.CommandLine", "McMaster.Extensions.CommandLineUtils" }; HasCommandLine = PackageReferences.Any(p => commandLinePackages.Any(cl => p.Contains(cl))); // Plugin frameworks var pluginPackages = new[] { "Microsoft.Extensions.DependencyInjection.Abstractions", "System.Composition", "MEF" }; HasPluginFramework = PackageReferences.Any(p => pluginPackages.Any(pf => p.Contains(pf))); // Infrastructure packages var infraPackages = new[] { "Pulumi", "Terraform", "AWS.CDK", "Azure.ResourceManager" }; HasInfrastructure = PackageReferences.Any(p => infraPackages.Any(inf => p.Contains(inf))); } private void AnalyzePackageJson(string packageJsonPath) { try { var content = File.ReadAllText(packageJsonPath); var packageJson = JsonSerializer.Deserialize>(content); if (packageJson != null) { // Dependencies if (packageJson.TryGetValue("dependencies", out var deps) && deps.ValueKind == JsonValueKind.Object) { foreach (var dep in deps.EnumerateObject()) { PackageJsonDependencies[dep.Name] = dep.Value.ToString(); } } // Dev Dependencies if (packageJson.TryGetValue("devDependencies", out var devDeps) && devDeps.ValueKind == JsonValueKind.Object) { foreach (var dep in devDeps.EnumerateObject()) { PackageJsonDevDependencies[dep.Name] = dep.Value.ToString(); } } // Scripts if (packageJson.TryGetValue("scripts", out var scripts) && scripts.ValueKind == JsonValueKind.Object) { foreach (var script in scripts.EnumerateObject()) { PackageJsonScripts[script.Name] = script.Value.ToString(); } } DetectionEvidence.Add("Found package.json (Node.js project)"); } } catch (Exception ex) { DetectionEvidence.Add($"Error analyzing package.json: {ex.Message}"); } } private void AnalyzePomXml(string pomPath) { try { var content = File.ReadAllText(pomPath); MavenConfig["content"] = content; DetectionEvidence.Add("Found pom.xml (Maven project)"); } catch (Exception ex) { DetectionEvidence.Add($"Error analyzing pom.xml: {ex.Message}"); } } private void AnalyzeBuildGradle(string gradlePath) { try { var content = File.ReadAllText(gradlePath); GradleConfig["content"] = content; DetectionEvidence.Add("Found build.gradle (Gradle project)"); } catch (Exception ex) { DetectionEvidence.Add($"Error analyzing build.gradle: {ex.Message}"); } } public void AnalyzeProjectStructure(string projectPath) { try { // Get folders Folders = Directory.GetDirectories(projectPath, "*", SearchOption.TopDirectoryOnly) .Select(d => Path.GetFileName(d)) .Where(d => !d.StartsWith(".")) .ToList(); // Get files (top level only for performance) Files = Directory.GetFiles(projectPath, "*", SearchOption.TopDirectoryOnly) .Select(f => Path.GetFileName(f)) .Where(f => !f.StartsWith(".")) .ToList(); // Add source file detection from subdirectories var sourceExtensions = new[] { "*.cs", "*.js", "*.ts", "*.py", "*.java", "*.go", "*.rs", "*.cpp", "*.c", "*.h" }; foreach (var ext in sourceExtensions) { var sourceFiles = Directory.GetFiles(projectPath, ext, SearchOption.AllDirectories) .Select(f => Path.GetRelativePath(projectPath, f)) .ToList(); if (sourceFiles.Any()) { Files.AddRange(sourceFiles); } } } catch (Exception ex) { DetectionEvidence.Add($"Error analyzing project structure: {ex.Message}"); } } public void AnalyzeSourceFiles(string projectPath) { try { // Python-specific files var requirementsPath = Path.Combine(projectPath, "requirements.txt"); if (File.Exists(requirementsPath)) { PythonRequirements = File.ReadAllLines(requirementsPath) .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#")) .ToList(); DetectionEvidence.Add("Found requirements.txt"); } // Go modules var goModPath = Path.Combine(projectPath, "go.mod"); if (File.Exists(goModPath)) { var content = File.ReadAllText(goModPath); GoModConfig["content"] = content; DetectionEvidence.Add("Found go.mod"); } // Rust cargo var cargoPath = Path.Combine(projectPath, "Cargo.toml"); if (File.Exists(cargoPath)) { var content = File.ReadAllText(cargoPath); CargoConfig["content"] = content; DetectionEvidence.Add("Found Cargo.toml"); } } catch (Exception ex) { DetectionEvidence.Add($"Error analyzing source files: {ex.Message}"); } } }