314 lines
15 KiB
C#
314 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using SolutionCleanupTool.Interfaces;
|
|
using SolutionCleanupTool.Models;
|
|
|
|
namespace SolutionCleanupTool.Services
|
|
{
|
|
/// <summary>
|
|
/// Generates detailed cleanup reports with removed projects, error messages, and restoration recommendations
|
|
/// </summary>
|
|
public class ReportGenerator : IReportGenerator
|
|
{
|
|
/// <summary>
|
|
/// Generates a comprehensive cleanup report including all changes, validation results, and recommendations
|
|
/// </summary>
|
|
/// <param name="cleanupResult">The result of the cleanup operation</param>
|
|
/// <param name="validationResult">The result of post-cleanup validation</param>
|
|
/// <param name="solutionPath">Path to the original solution file</param>
|
|
/// <returns>Formatted report as a string</returns>
|
|
public string GenerateReport(CleanupResult cleanupResult, ValidationResult validationResult, string solutionPath)
|
|
{
|
|
var report = new StringBuilder();
|
|
|
|
// Header
|
|
report.AppendLine("=".PadRight(80, '='));
|
|
report.AppendLine("SOLUTION CLEANUP REPORT");
|
|
report.AppendLine("=".PadRight(80, '='));
|
|
report.AppendLine();
|
|
|
|
// Basic Information
|
|
report.AppendLine("BASIC INFORMATION");
|
|
report.AppendLine("-".PadRight(40, '-'));
|
|
report.AppendLine($"Solution File: {Path.GetFileName(solutionPath)}");
|
|
report.AppendLine($"Solution Path: {solutionPath}");
|
|
report.AppendLine($"Cleanup Date: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
|
report.AppendLine($"Cleanup Status: {(cleanupResult.Success ? "SUCCESS" : "FAILED")}");
|
|
report.AppendLine();
|
|
|
|
// Summary Statistics
|
|
report.AppendLine(GenerateSummary(cleanupResult));
|
|
|
|
// Backup Information
|
|
if (!string.IsNullOrEmpty(cleanupResult.BackupFilePath))
|
|
{
|
|
report.AppendLine("BACKUP INFORMATION");
|
|
report.AppendLine("-".PadRight(40, '-'));
|
|
report.AppendLine($"Backup Location: {cleanupResult.BackupFilePath}");
|
|
report.AppendLine($"Backup Created: {(File.Exists(cleanupResult.BackupFilePath) ? "YES" : "NO")}");
|
|
report.AppendLine();
|
|
}
|
|
|
|
// Detailed Removal Information
|
|
if (cleanupResult.RemovedProjects.Any())
|
|
{
|
|
report.AppendLine("REMOVED PROJECTS DETAILS");
|
|
report.AppendLine("-".PadRight(40, '-'));
|
|
|
|
for (int i = 0; i < cleanupResult.RemovedProjects.Count; i++)
|
|
{
|
|
var project = cleanupResult.RemovedProjects[i];
|
|
report.AppendLine($"{i + 1}. {project.Reference.Name}");
|
|
report.AppendLine($" Expected Path: {project.ExpectedPath}");
|
|
report.AppendLine($" Project GUID: {project.Reference.ProjectGuid}");
|
|
|
|
if (!string.IsNullOrEmpty(project.ErrorMessage))
|
|
{
|
|
report.AppendLine($" Error Message: {project.ErrorMessage}");
|
|
}
|
|
|
|
if (project.ReferencingProjects.Any())
|
|
{
|
|
report.AppendLine($" Referenced By: {string.Join(", ", project.ReferencingProjects)}");
|
|
}
|
|
|
|
report.AppendLine();
|
|
}
|
|
}
|
|
|
|
// Warnings
|
|
if (cleanupResult.Warnings.Any())
|
|
{
|
|
report.AppendLine("WARNINGS");
|
|
report.AppendLine("-".PadRight(40, '-'));
|
|
foreach (var warning in cleanupResult.Warnings)
|
|
{
|
|
report.AppendLine($"• {warning}");
|
|
}
|
|
report.AppendLine();
|
|
}
|
|
|
|
// Validation Results
|
|
report.AppendLine("POST-CLEANUP VALIDATION");
|
|
report.AppendLine("-".PadRight(40, '-'));
|
|
report.AppendLine($"Solution Valid: {(validationResult.IsValid ? "YES" : "NO")}");
|
|
report.AppendLine($"Can Load Solution: {(validationResult.CanLoadSolution ? "YES" : "NO")}");
|
|
report.AppendLine($"Has Circular References: {(validationResult.HasCircularReferences ? "YES" : "NO")}");
|
|
|
|
if (validationResult.Errors.Any())
|
|
{
|
|
report.AppendLine();
|
|
report.AppendLine("Validation Errors:");
|
|
foreach (var error in validationResult.Errors)
|
|
{
|
|
report.AppendLine($"• {error}");
|
|
}
|
|
}
|
|
|
|
if (validationResult.Warnings.Any())
|
|
{
|
|
report.AppendLine();
|
|
report.AppendLine("Validation Warnings:");
|
|
foreach (var warning in validationResult.Warnings)
|
|
{
|
|
report.AppendLine($"• {warning}");
|
|
}
|
|
}
|
|
report.AppendLine();
|
|
|
|
// Restoration Recommendations
|
|
if (cleanupResult.RemovedProjects.Any())
|
|
{
|
|
report.AppendLine(GenerateRestorationRecommendations(cleanupResult.RemovedProjects));
|
|
}
|
|
|
|
// Footer
|
|
report.AppendLine("=".PadRight(80, '='));
|
|
report.AppendLine("END OF REPORT");
|
|
report.AppendLine("=".PadRight(80, '='));
|
|
|
|
return report.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a concise summary report with key statistics
|
|
/// </summary>
|
|
/// <param name="cleanupResult">The result of the cleanup operation</param>
|
|
/// <returns>Summary report as a string</returns>
|
|
public string GenerateSummary(CleanupResult cleanupResult)
|
|
{
|
|
var summary = new StringBuilder();
|
|
|
|
summary.AppendLine("CLEANUP SUMMARY");
|
|
summary.AppendLine("-".PadRight(40, '-'));
|
|
summary.AppendLine($"Total Projects Removed: {cleanupResult.TotalProjectsRemoved}");
|
|
summary.AppendLine($"Projects with Details: {cleanupResult.RemovedProjects.Count}");
|
|
summary.AppendLine($"Warnings Generated: {cleanupResult.Warnings.Count}");
|
|
summary.AppendLine($"Operation Status: {(cleanupResult.Success ? "Completed Successfully" : "Failed")}");
|
|
|
|
if (cleanupResult.RemovedProjects.Any())
|
|
{
|
|
var projectsWithReferences = cleanupResult.RemovedProjects.Count(p => p.ReferencingProjects.Any());
|
|
summary.AppendLine($"Projects Referenced by Others: {projectsWithReferences}");
|
|
|
|
var totalReferences = cleanupResult.RemovedProjects.Sum(p => p.ReferencingProjects.Count);
|
|
summary.AppendLine($"Total Reference Relationships Removed: {totalReferences}");
|
|
}
|
|
|
|
summary.AppendLine();
|
|
|
|
return summary.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates detailed restoration recommendations for removed projects
|
|
/// </summary>
|
|
/// <param name="removedProjects">List of projects that were removed</param>
|
|
/// <returns>Restoration recommendations as a string</returns>
|
|
public string GenerateRestorationRecommendations(List<MissingProject> removedProjects)
|
|
{
|
|
var recommendations = new StringBuilder();
|
|
|
|
recommendations.AppendLine("RESTORATION RECOMMENDATIONS");
|
|
recommendations.AppendLine("-".PadRight(40, '-'));
|
|
|
|
if (!removedProjects.Any())
|
|
{
|
|
recommendations.AppendLine("No projects were removed, so no restoration is needed.");
|
|
recommendations.AppendLine();
|
|
return recommendations.ToString();
|
|
}
|
|
|
|
recommendations.AppendLine("If you need to restore any of the removed projects, follow these steps:");
|
|
recommendations.AppendLine();
|
|
|
|
recommendations.AppendLine("GENERAL RESTORATION STEPS:");
|
|
recommendations.AppendLine("1. Locate or recreate the missing project files");
|
|
recommendations.AppendLine("2. Restore the original solution file from the backup");
|
|
recommendations.AppendLine("3. Ensure project files are in the expected locations");
|
|
recommendations.AppendLine("4. Rebuild the solution to verify all references work");
|
|
recommendations.AppendLine();
|
|
|
|
recommendations.AppendLine("PROJECT-SPECIFIC RECOMMENDATIONS:");
|
|
recommendations.AppendLine();
|
|
|
|
for (int i = 0; i < removedProjects.Count; i++)
|
|
{
|
|
var project = removedProjects[i];
|
|
recommendations.AppendLine($"{i + 1}. {project.Reference.Name}");
|
|
recommendations.AppendLine($" Expected Location: {project.ExpectedPath}");
|
|
|
|
// Provide specific recommendations based on the project type
|
|
if (!string.IsNullOrEmpty(project.Reference.ProjectTypeGuid))
|
|
{
|
|
var projectType = GetProjectTypeDescription(project.Reference.ProjectTypeGuid);
|
|
recommendations.AppendLine($" Project Type: {projectType}");
|
|
}
|
|
|
|
// Suggest possible locations to look for the project
|
|
var possibleLocations = GeneratePossibleLocations(project.ExpectedPath);
|
|
if (possibleLocations.Any())
|
|
{
|
|
recommendations.AppendLine(" Possible Alternative Locations:");
|
|
foreach (var location in possibleLocations)
|
|
{
|
|
recommendations.AppendLine($" • {location}");
|
|
}
|
|
}
|
|
|
|
// If the project was referenced by others, mention the impact
|
|
if (project.ReferencingProjects.Any())
|
|
{
|
|
recommendations.AppendLine($" Impact: This project was referenced by {project.ReferencingProjects.Count} other project(s)");
|
|
recommendations.AppendLine($" Affected Projects: {string.Join(", ", project.ReferencingProjects)}");
|
|
}
|
|
|
|
recommendations.AppendLine();
|
|
}
|
|
|
|
recommendations.AppendLine("ALTERNATIVE SOLUTIONS:");
|
|
recommendations.AppendLine("• If projects are permanently removed, consider updating dependent projects");
|
|
recommendations.AppendLine("• Check source control history for when projects were last available");
|
|
recommendations.AppendLine("• Consider creating stub projects if dependencies are critical");
|
|
recommendations.AppendLine("• Review project documentation for migration or replacement guidance");
|
|
recommendations.AppendLine();
|
|
|
|
return recommendations.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a human-readable description of the project type based on GUID
|
|
/// </summary>
|
|
/// <param name="projectTypeGuid">The project type GUID</param>
|
|
/// <returns>Description of the project type</returns>
|
|
private string GetProjectTypeDescription(string projectTypeGuid)
|
|
{
|
|
// Common Visual Studio project type GUIDs
|
|
var projectTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{ "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", "C# Project" },
|
|
{ "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}", "VB.NET Project" },
|
|
{ "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}", "C++ Project" },
|
|
{ "{349C5851-65DF-11DA-9384-00065B846F21}", "Web Application Project" },
|
|
{ "{E24C65DC-7377-472B-9ABA-BC803B73C61A}", "Web Site Project" },
|
|
{ "{F85E285D-A4E0-4152-9332-AB1D724D3325}", "Modeling Project" },
|
|
{ "{A9ACE9BB-CECE-4E62-9AA4-C7E7C5BD2124}", "Database Project" },
|
|
{ "{4F174C21-8C12-11D0-8340-0000F80270F8}", "Database Project (Legacy)" },
|
|
{ "{3AC096D0-A1C2-E12C-1390-A8335801FDAB}", "Test Project" },
|
|
{ "{F2A71F9B-5D33-465A-A702-920D77279786}", "F# Project" }
|
|
};
|
|
|
|
return projectTypes.TryGetValue(projectTypeGuid, out var description)
|
|
? description
|
|
: $"Unknown Project Type ({projectTypeGuid})";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates possible alternative locations where a missing project might be found
|
|
/// </summary>
|
|
/// <param name="expectedPath">The expected path of the missing project</param>
|
|
/// <returns>List of possible alternative locations</returns>
|
|
private List<string> GeneratePossibleLocations(string expectedPath)
|
|
{
|
|
var locations = new List<string>();
|
|
|
|
if (string.IsNullOrEmpty(expectedPath))
|
|
return locations;
|
|
|
|
try
|
|
{
|
|
var fileName = Path.GetFileName(expectedPath);
|
|
var directory = Path.GetDirectoryName(expectedPath);
|
|
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(expectedPath);
|
|
|
|
if (!string.IsNullOrEmpty(directory) && !string.IsNullOrEmpty(fileName))
|
|
{
|
|
// Common alternative locations
|
|
locations.Add(Path.Combine(directory, "src", fileName));
|
|
locations.Add(Path.Combine(directory, "Source", fileName));
|
|
locations.Add(Path.Combine(directory, fileNameWithoutExtension, fileName));
|
|
locations.Add(Path.Combine(Path.GetDirectoryName(directory) ?? "", fileName));
|
|
|
|
// Check for common renamed patterns
|
|
if (fileNameWithoutExtension.Contains("."))
|
|
{
|
|
var parts = fileNameWithoutExtension.Split('.');
|
|
if (parts.Length > 1)
|
|
{
|
|
locations.Add(Path.Combine(directory, $"{parts[0]}.csproj"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// If path parsing fails, return empty list
|
|
}
|
|
|
|
return locations.Distinct().ToList();
|
|
}
|
|
}
|
|
} |