398 lines
15 KiB
C#
Executable File
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");
|
|
} |