MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Refacto.../Core/BaseAIPlugin.cs

398 lines
15 KiB
C#
Executable File

using MarketAlly.AIPlugin;
using MarketAlly.AIPlugin.Refactoring.Plugins;
using MarketAlly.AIPlugin.Refactoring.Security;
using MarketAlly.AIPlugin.Refactoring.Caching;
using MarketAlly.AIPlugin.Refactoring.Performance;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
namespace MarketAlly.AIPlugin.Refactoring.Core
{
/// <summary>
/// Base class for all AI refactoring plugins providing common functionality
/// </summary>
public abstract class BaseAIPlugin : IAIPlugin
{
protected readonly IParameterExtractor ParameterExtractor;
protected readonly CentralizedErrorHandler ErrorHandler;
protected readonly ILogger? Logger;
protected readonly ISyntaxTreeCache SyntaxTreeCache;
protected readonly IAnalysisCache AnalysisCache;
protected readonly IMemoryPressureMonitor MemoryMonitor;
private readonly ActivitySource _activitySource;
protected BaseAIPlugin(
IParameterExtractor? parameterExtractor = null,
CentralizedErrorHandler? errorHandler = null,
ILogger? logger = null,
ISyntaxTreeCache? syntaxTreeCache = null,
IAnalysisCache? analysisCache = null,
IMemoryPressureMonitor? memoryMonitor = null)
{
ParameterExtractor = parameterExtractor ?? new ParameterExtractor();
ErrorHandler = errorHandler ?? GlobalErrorHandler.Instance;
Logger = logger;
SyntaxTreeCache = syntaxTreeCache ?? SyntaxTreeCacheFactory.Default;
AnalysisCache = analysisCache ?? AnalysisCacheFactory.Default;
MemoryMonitor = memoryMonitor ?? new MemoryPressureMonitor();
_activitySource = new ActivitySource($"MarketAlly.AIPlugin.{GetType().Name}");
}
/// <summary>
/// Plugin-specific parameters supported by this plugin
/// </summary>
public abstract IReadOnlyDictionary<string, Type> SupportedParameters { get; }
/// <summary>
/// Main execution method that includes common error handling and telemetry
/// </summary>
public virtual async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
{
using var activity = _activitySource.StartActivity($"{GetType().Name}.Execute");
var stopwatch = Stopwatch.StartNew();
try
{
activity?.SetTag("plugin.name", GetType().Name);
activity?.SetTag("parameters.count", parameters.Count);
// Validate parameters
var validationResult = ValidateParameters(parameters);
if (!validationResult.IsValid)
{
return CreateErrorResult(
GetType().Name,
"ExecuteAsync",
RefactoringErrorCode.InvalidInput,
validationResult.ErrorMessage);
}
// Execute plugin-specific logic
var result = await ExecuteInternalAsync(parameters);
activity?.SetTag("execution.success", result.Success);
activity?.SetTag("execution.duration_ms", stopwatch.ElapsedMilliseconds);
Logger?.LogInformation("Plugin {PluginName} executed successfully in {Duration}ms",
GetType().Name, stopwatch.ElapsedMilliseconds);
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
activity?.SetTag("execution.success", false);
activity?.SetTag("execution.error", ex.Message);
Logger?.LogError(ex, "Plugin {PluginName} execution failed after {Duration}ms",
GetType().Name, stopwatch.ElapsedMilliseconds);
return await ErrorHandler.HandleErrorAsync(GetType().Name, "ExecuteAsync", ex) ??
CreateErrorResult(GetType().Name, "ExecuteAsync", RefactoringErrorCode.Unknown, ex.Message, ex);
}
}
/// <summary>
/// Plugin-specific execution logic to be implemented by derived classes
/// </summary>
protected abstract Task<AIPluginResult> ExecuteInternalAsync(IReadOnlyDictionary<string, object> parameters);
/// <summary>
/// Validates input parameters using security checks
/// </summary>
protected virtual ParameterValidationResult ValidateParameters(IReadOnlyDictionary<string, object> parameters)
{
try
{
// Common parameter validation
foreach (var kvp in parameters)
{
if (kvp.Value is string stringValue)
{
// Security validation for string parameters
if (!stringValue.IsInputSafe())
{
return ParameterValidationResult.Invalid($"Parameter '{kvp.Key}' contains unsafe content");
}
// File path validation for common file-related parameters
if (IsFilePathParameter(kvp.Key) && !string.IsNullOrEmpty(stringValue))
{
try
{
SecurePathValidator.ValidatePath(stringValue);
}
catch (SecurityException ex)
{
return ParameterValidationResult.Invalid($"Parameter '{kvp.Key}' has invalid path: {ex.Message}");
}
}
}
}
// Plugin-specific validation
return ValidatePluginSpecificParameters(parameters);
}
catch (Exception ex)
{
return ParameterValidationResult.Invalid($"Parameter validation failed: {ex.Message}");
}
}
/// <summary>
/// Plugin-specific parameter validation
/// </summary>
protected virtual ParameterValidationResult ValidatePluginSpecificParameters(IReadOnlyDictionary<string, object> parameters)
{
return ParameterValidationResult.Valid();
}
/// <summary>
/// Determines if a parameter name represents a file path
/// </summary>
protected virtual bool IsFilePathParameter(string parameterName)
{
var filePathIndicators = new[] { "path", "filepath", "file", "directory", "dir" };
var lowerName = parameterName.ToLowerInvariant();
foreach (var indicator in filePathIndicators)
{
if (lowerName.Contains(indicator))
return true;
}
return false;
}
/// <summary>
/// Safely processes a file with memory-efficient handling
/// </summary>
protected async Task<ProcessingResult> ProcessFileAsync(string filePath, CancellationToken cancellationToken = default)
{
try
{
var validatedPath = SecurePathValidator.ValidatePath(filePath);
if (!File.Exists(validatedPath))
{
throw new FileNotFoundException($"File not found: {filePath}");
}
var processor = new MemoryEfficientFileProcessor(MemoryMonitor);
return await processor.ProcessLargeFileAsync(validatedPath, cancellationToken);
}
catch (Exception ex)
{
Logger?.LogError(ex, "Failed to process file: {FilePath}", filePath);
throw;
}
}
/// <summary>
/// Gets cached syntax tree for a file
/// </summary>
protected async Task<Microsoft.CodeAnalysis.SyntaxTree> GetSyntaxTreeAsync(string filePath, CancellationToken cancellationToken = default)
{
var validatedPath = SecurePathValidator.ValidatePath(filePath);
return await SyntaxTreeCache.GetOrCreateAsync(validatedPath, cancellationToken);
}
/// <summary>
/// Gets or performs cached analysis
/// </summary>
protected async Task<TResult> GetOrAnalyzeAsync<TResult>(
string filePath,
Func<Task<TResult>> analyzer,
CancellationToken cancellationToken = default) where TResult : class
{
var validatedPath = SecurePathValidator.ValidatePath(filePath);
return await AnalysisCache.GetOrAnalyzeAsync(validatedPath, analyzer, cancellationToken);
}
/// <summary>
/// Safely extracts parameter values with type checking
/// </summary>
protected T GetParameter<T>(IReadOnlyDictionary<string, object> parameters, string key, T defaultValue = default!)
{
return ParameterExtractor.GetParameter(parameters, key, defaultValue);
}
/// <summary>
/// Safely extracts parameter values with multiple key variations
/// </summary>
protected T GetParameter<T>(IReadOnlyDictionary<string, object> parameters, string[] keys, T defaultValue = default!)
{
return ParameterExtractor.GetParameter(parameters, keys, defaultValue);
}
/// <summary>
/// Creates a success result with standard formatting
/// </summary>
protected AIPluginResult CreateSuccessResult(object data, string? message = null)
{
var finalMessage = message ?? $"{GetType().Name} executed successfully";
return PluginResultHelpers.Success(data, finalMessage);
}
/// <summary>
/// Creates an error result with proper error handling
/// </summary>
protected AIPluginResult CreateErrorResult(string message, Exception? exception = null)
{
return CreateErrorResult(
GetType().Name,
"ExecuteInternalAsync",
RefactoringErrorCode.Unknown,
message,
exception);
}
/// <summary>
/// Creates an error result with detailed context
/// </summary>
protected AIPluginResult CreateErrorResult(string pluginName, string operation, RefactoringErrorCode errorCode, string message, Exception? exception = null)
{
var refactoringException = new RefactoringException(pluginName, operation, errorCode, message, exception);
var errorService = new ErrorHandlingService();
return errorService.CreateErrorResult(refactoringException);
}
/// <summary>
/// Creates an error result for validation failures
/// </summary>
protected AIPluginResult CreateValidationErrorResult(string parameterName, string validationMessage)
{
return CreateErrorResult(
GetType().Name,
"ExecuteInternalAsync",
RefactoringErrorCode.InvalidInput,
$"Validation failed for {parameterName}: {validationMessage}");
}
/// <summary>
/// Safely processes multiple files with adaptive concurrency
/// </summary>
protected async Task<TResult[]> ProcessMultipleFilesAsync<TResult>(
IEnumerable<string> filePaths,
Func<string, Task<TResult>> processor,
CancellationToken cancellationToken = default)
{
var validatedPaths = new List<string>();
foreach (var path in filePaths)
{
try
{
var validatedPath = SecurePathValidator.ValidatePath(path);
if (SecurePathValidator.IsFilePathSafeForAnalysis(validatedPath))
{
validatedPaths.Add(validatedPath);
}
}
catch (Exception ex)
{
Logger?.LogWarning(ex, "Skipping invalid file path: {FilePath}", path);
}
}
return await validatedPaths.ProcessWithAdaptiveConcurrencyAsync(processor, cancellationToken);
}
/// <summary>
/// Common cleanup for resources
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_activitySource?.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
/// <summary>
/// Result of parameter validation
/// </summary>
public class ParameterValidationResult
{
public bool IsValid { get; }
public string ErrorMessage { get; }
private ParameterValidationResult(bool isValid, string errorMessage = "")
{
IsValid = isValid;
ErrorMessage = errorMessage;
}
public static ParameterValidationResult Valid() => new(true);
public static ParameterValidationResult Invalid(string errorMessage) => new(false, errorMessage);
}
/// <summary>
/// Strongly-typed operation enums
/// </summary>
public enum RefactoringOperation
{
CodeAnalysis,
Documentation,
Formatting,
NamingConventions,
CodeCleanup,
Extraction,
Reorganization
}
/// <summary>
/// Strongly-typed formatting options
/// </summary>
public record FormattingOptions(
FormattingStyle Style = FormattingStyle.Microsoft,
int IndentationSize = 4,
bool OrganizeUsings = true,
bool RemoveUnnecessary = true,
bool FixIndentation = true,
bool CreateBackup = true);
public enum FormattingStyle
{
Microsoft,
Allman,
KandR,
Google
}
/// <summary>
/// Strongly-typed analysis options
/// </summary>
public record AnalysisOptions(
string AnalysisDepth = "detailed",
bool IncludeComplexity = true,
bool IncludeCodeSmells = true,
bool IncludeSuggestions = true,
int ComplexityThreshold = 10,
int MaxMethodLength = 50);
/// <summary>
/// Strongly-typed documentation options
/// </summary>
public record DocumentationOptions(
string Style = "intelligent",
bool IncludeExamples = false,
bool IncludeSeeAlso = false,
bool ApiDocFormat = false,
string DocumentationScope = "public");
}