CodeMergeNG/CodeMerger.cs

1353 lines
46 KiB
C#
Raw Permalink Normal View History

2025-02-28 10:43:44 -03:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
/// <summary>
/// Clase para realizar análisis y merge de código C# utilizando Roslyn
/// </summary>
public class cCodeMerger
{
#region Constantes
// Constantes para namespace y clase virtual
private const string VIRTUAL_NAMESPACE = "__VirtualNamespace__";
private const string VIRTUAL_CLASS = "__VirtualClass__";
#endregion
#region Propiedades
// Configuración para namespace y clase por defecto
public string DefaultNamespace { get; set; } = VIRTUAL_NAMESPACE;
public string DefaultClass { get; set; } = VIRTUAL_CLASS;
// Log de operaciones
private StringBuilder _log = new StringBuilder();
// Diccionarios para búsqueda rápida
private Dictionary<string, CodeElement> _methodsBySignature = new Dictionary<string, CodeElement>();
private Dictionary<string, CodeElement> _propertiesByName = new Dictionary<string, CodeElement>();
private Dictionary<string, CodeElement> _fieldsByName = new Dictionary<string, CodeElement>();
private Dictionary<string, CodeElement> _classesByName = new Dictionary<string, CodeElement>();
private Dictionary<string, CodeElement> _namespacesByName = new Dictionary<string, CodeElement>();
private Dictionary<string, CodeElement> _usingsByName = new Dictionary<string, CodeElement>();
#endregion
#region Métodos Públicos
/// <summary>
/// Constructor de la clase CodeMerger
/// </summary>
/// <param name="defaultNamespace">Namespace por defecto a usar cuando no se especifique</param>
/// <param name="defaultClass">Clase por defecto a usar cuando no se especifique</param>
public cCodeMerger(string defaultNamespace = null, string defaultClass = null)
{
if (!string.IsNullOrEmpty(defaultNamespace))
DefaultNamespace = defaultNamespace;
if (!string.IsNullOrEmpty(defaultClass))
DefaultClass = defaultClass;
LogInfo("CodeMerger initialized");
}
/// <summary>
/// Verifica rápidamente si un código contiene un namespace específico
/// </summary>
public bool ContainsNamespace(string code, string namespaceName)
{
if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(namespaceName))
return false;
string pattern = $@"namespace\s+{Regex.Escape(namespaceName)}\s*{{";
return Regex.IsMatch(code, pattern);
}
/// <summary>
/// Verifica rápidamente si un código contiene una clase específica
/// </summary>
public bool ContainsClass(string code, string className)
{
if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(className))
return false;
string pattern = $@"class\s+{Regex.Escape(className)}\s*{{";
return Regex.IsMatch(code, pattern);
}
/// <summary>
/// Analiza el código C# y genera un árbol de CodeElement
/// </summary>
/// <param name="code">Código C# a analizar</param>
/// <returns>Elemento raíz del árbol generado</returns>
public CodeElement AnalyzeCode(string code)
{
if (string.IsNullOrEmpty(code))
{
LogError("Empty code provided for analysis");
return null;
}
try
{
LogInfo("Starting code analysis");
// Preparar código asegurando que tenga namespace y clase si es necesario
string preparedCode = PrepareCodeForParsing(code);
// Parsear código con Roslyn
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(preparedCode);
CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot();
// Construir árbol de CodeElement
CodeElement rootElement = BuildCodeElementTree(root);
// Construir índices para búsqueda rápida
BuildSearchIndices(rootElement);
LogInfo($"Code analysis completed. Root element: {rootElement.Type} {rootElement.Name}");
return rootElement;
}
catch (Exception ex)
{
LogError($"Error analyzing code: {ex.Message}");
return null;
}
}
/// <summary>
/// Realiza el merge de código snippet dentro de código master
/// </summary>
/// <param name="masterCode">Código principal que recibirá las modificaciones</param>
/// <param name="snippetCode">Código a integrar en el master</param>
/// <returns>Código resultante del merge</returns>
public string MergeCode(string masterCode, string snippetCode)
{
try
{
LogInfo("Starting code merge operation");
// Analizar ambos códigos
CodeElement masterRoot = AnalyzeCode(masterCode);
CodeElement snippetRoot = AnalyzeCode(snippetCode);
if (masterRoot == null || snippetRoot == null)
{
LogError("Could not analyze one or both code inputs");
return masterCode;
}
// Realizar el merge
CodeElement mergedRoot = MergeElements(masterRoot, snippetRoot);
// Reconstruir el código resultante
string result = ReconstructCode(mergedRoot);
LogInfo("Code merge completed successfully");
return result;
}
catch (Exception ex)
{
LogError($"Error during code merge: {ex.Message}");
return masterCode; // En caso de error, devolvemos el código master sin cambios
}
}
/// <summary>
/// Obtiene el log de operaciones realizadas
/// </summary>
public string GetLog()
{
return _log.ToString();
}
/// <summary>
/// Limpia el log de operaciones
/// </summary>
public void ClearLog()
{
_log.Clear();
LogInfo("Log cleared");
}
#endregion
#region Métodos Privados para Análisis de Código
/// <summary>
/// Prepara el código para análisis, añadiendo namespace y clase si es necesario
/// </summary>
private string PrepareCodeForParsing(string code)
{
bool hasNamespace = ContainsNamespace(code, ".*?");
bool hasClass = ContainsClass(code, ".*?");
if (hasNamespace && hasClass)
return code;
if (!hasNamespace && !hasClass)
{
// Ni namespace ni clase: envolver en ambos
LogInfo($"Adding virtual namespace '{DefaultNamespace}' and class '{DefaultClass}'");
return $"namespace {DefaultNamespace} {{ class {DefaultClass} {{ {code} }} }}";
}
else if (hasNamespace && !hasClass)
{
// Tiene namespace pero no clase
LogInfo($"Adding virtual class '{DefaultClass}'");
// Necesitamos insertar la clase dentro del namespace existente
Match namespaceMatch = Regex.Match(code, @"namespace\s+([^\s{]+)\s*{");
if (namespaceMatch.Success)
{
int openBracePos = code.IndexOf('{', namespaceMatch.Index);
if (openBracePos >= 0)
{
return code.Substring(0, openBracePos + 1) +
$" class {DefaultClass} {{ " +
code.Substring(openBracePos + 1) +
" } ";
}
}
return $"namespace {DefaultNamespace} {{ class {DefaultClass} {{ {code} }} }}";
}
else // hasClass && !hasNamespace
{
// Tiene clase pero no namespace
LogInfo($"Adding virtual namespace '{DefaultNamespace}'");
return $"namespace {DefaultNamespace} {{ {code} }}";
}
}
/// <summary>
/// Construye el árbol de CodeElement a partir del árbol sintáctico de Roslyn
/// </summary>
private CodeElement BuildCodeElementTree(CompilationUnitSyntax compilationUnit)
{
// Crear elemento raíz para representar el archivo completo
var rootElement = new CodeElement
{
Type = CodeElement.ElementType.Other,
Name = "Root",
OriginalText = compilationUnit.ToString(),
StartPosition = 0,
Length = compilationUnit.ToString().Length
};
// Procesar todas las directivas using
foreach (var usingDirective in compilationUnit.Usings)
{
var usingElement = new CodeElement
{
Type = CodeElement.ElementType.Using,
Name = usingDirective.Name.ToString(),
OriginalText = usingDirective.ToString(),
StartPosition = usingDirective.SpanStart,
Length = usingDirective.Span.Length
};
rootElement.AddChild(usingElement);
LogInfo($"Found using: {usingElement.Name}");
}
// Procesar todos los namespaces
foreach (var namespaceDeclaration in compilationUnit.Members.OfType<NamespaceDeclarationSyntax>())
{
ProcessNamespace(namespaceDeclaration, rootElement);
}
// Procesar clases que estén directamente en el root (sin namespace)
foreach (var typeDeclaration in compilationUnit.Members.OfType<TypeDeclarationSyntax>())
{
if (typeDeclaration is ClassDeclarationSyntax classDeclaration)
{
ProcessClass(classDeclaration, rootElement);
}
}
return rootElement;
}
/// <summary>
/// Procesa un namespace y sus miembros
/// </summary>
private void ProcessNamespace(NamespaceDeclarationSyntax namespaceDeclaration, CodeElement parent)
{
var namespaceElement = new CodeElement
{
Type = CodeElement.ElementType.Namespace,
Name = namespaceDeclaration.Name.ToString(),
OriginalText = namespaceDeclaration.ToString(),
StartPosition = namespaceDeclaration.SpanStart,
Length = namespaceDeclaration.Span.Length,
Indentation = ExtractIndentation(namespaceDeclaration),
LeadingComments = ExtractLeadingComments(namespaceDeclaration),
TrailingComments = ExtractTrailingComments(namespaceDeclaration)
};
parent.AddChild(namespaceElement);
LogInfo($"Found namespace: {namespaceElement.Name}");
// Procesar todas las clases dentro del namespace
foreach (var member in namespaceDeclaration.Members)
{
if (member is ClassDeclarationSyntax classDeclaration)
{
ProcessClass(classDeclaration, namespaceElement);
}
}
}
/// <summary>
/// Procesa una clase y sus miembros
/// </summary>
private void ProcessClass(ClassDeclarationSyntax classDeclaration, CodeElement parent)
{
var classElement = new CodeElement
{
Type = CodeElement.ElementType.Class,
Name = classDeclaration.Identifier.ToString(),
OriginalText = classDeclaration.ToString(),
StartPosition = classDeclaration.SpanStart,
Length = classDeclaration.Span.Length,
Indentation = ExtractIndentation(classDeclaration),
LeadingComments = ExtractLeadingComments(classDeclaration),
TrailingComments = ExtractTrailingComments(classDeclaration),
NamespaceName = parent.Type == CodeElement.ElementType.Namespace ? parent.Name : VIRTUAL_NAMESPACE
};
// Extraer información de herencia e interfaces
if (classDeclaration.BaseList != null)
{
foreach (var baseType in classDeclaration.BaseList.Types)
{
string baseTypeName = baseType.Type.ToString();
if (baseType == classDeclaration.BaseList.Types.First())
{
classElement.BaseClass = baseTypeName;
}
else
{
classElement.ImplementedInterfaces.Add(baseTypeName);
}
}
}
// Extraer modificadores
classElement.Modifiers = classDeclaration.Modifiers
.Select(m => m.ToString())
.ToArray();
// Extraer atributos
if (classDeclaration.AttributeLists.Any())
{
classElement.Attributes = classDeclaration.AttributeLists
.SelectMany(a => a.Attributes)
.Select(a => a.ToString())
.ToList();
}
parent.AddChild(classElement);
LogInfo($"Found class: {classElement.Name}");
// Procesar todos los miembros de la clase
foreach (var member in classDeclaration.Members)
{
if (member is FieldDeclarationSyntax fieldDeclaration)
{
ProcessField(fieldDeclaration, classElement);
}
else if (member is PropertyDeclarationSyntax propertyDeclaration)
{
ProcessProperty(propertyDeclaration, classElement);
}
else if (member is MethodDeclarationSyntax methodDeclaration)
{
ProcessMethod(methodDeclaration, classElement);
}
else if (member is ConstructorDeclarationSyntax constructorDeclaration)
{
ProcessConstructor(constructorDeclaration, classElement);
}
else if (member is ClassDeclarationSyntax nestedClassDeclaration)
{
// Clase anidada: procesamos recursivamente
ProcessClass(nestedClassDeclaration, classElement);
}
}
}
/// <summary>
/// Procesa un campo (field) de clase
/// </summary>
private void ProcessField(FieldDeclarationSyntax fieldDeclaration, CodeElement parent)
{
foreach (var variable in fieldDeclaration.Declaration.Variables)
{
var fieldElement = new CodeElement
{
Type = CodeElement.ElementType.Field,
Name = variable.Identifier.ToString(),
ReturnType = fieldDeclaration.Declaration.Type.ToString(),
OriginalText = fieldDeclaration.ToString(),
StartPosition = fieldDeclaration.SpanStart,
Length = fieldDeclaration.Span.Length,
Indentation = ExtractIndentation(fieldDeclaration),
LeadingComments = ExtractLeadingComments(fieldDeclaration),
TrailingComments = ExtractTrailingComments(fieldDeclaration),
NamespaceName = GetNamespaceFromParent(parent),
ClassName = parent.Name
};
// Extraer modificadores
fieldElement.Modifiers = fieldDeclaration.Modifiers
.Select(m => m.ToString())
.ToArray();
// Extraer inicializador si existe
if (variable.Initializer != null)
{
fieldElement.Initializer = variable.Initializer.Value.ToString();
}
// Extraer atributos
if (fieldDeclaration.AttributeLists.Any())
{
fieldElement.Attributes = fieldDeclaration.AttributeLists
.SelectMany(a => a.Attributes)
.Select(a => a.ToString())
.ToList();
}
// Generar firma normalizada
fieldElement.NormalizedSignature = $"{fieldElement.ReturnType} {fieldElement.Name}";
parent.AddChild(fieldElement);
LogInfo($"Found field: {fieldElement.Name} ({fieldElement.ReturnType})");
}
}
/// <summary>
/// Procesa una propiedad de clase
/// </summary>
private void ProcessProperty(PropertyDeclarationSyntax propertyDeclaration, CodeElement parent)
{
var propertyElement = new CodeElement
{
Type = CodeElement.ElementType.Property,
Name = propertyDeclaration.Identifier.ToString(),
ReturnType = propertyDeclaration.Type.ToString(),
OriginalText = propertyDeclaration.ToString(),
StartPosition = propertyDeclaration.SpanStart,
Length = propertyDeclaration.Span.Length,
Indentation = ExtractIndentation(propertyDeclaration),
LeadingComments = ExtractLeadingComments(propertyDeclaration),
TrailingComments = ExtractTrailingComments(propertyDeclaration),
NamespaceName = GetNamespaceFromParent(parent),
ClassName = parent.Name
};
// Extraer modificadores
propertyElement.Modifiers = propertyDeclaration.Modifiers
.Select(m => m.ToString())
.ToArray();
// Extraer accessors (get/set)
if (propertyDeclaration.AccessorList != null)
{
foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
{
if (accessor.Keyword.Text == "get")
{
propertyElement.HasGetter = true;
propertyElement.GetterAccessibility = accessor.Modifiers.Any()
? accessor.Modifiers.First().ToString()
: "";
}
else if (accessor.Keyword.Text == "set")
{
propertyElement.HasSetter = true;
propertyElement.SetterAccessibility = accessor.Modifiers.Any()
? accessor.Modifiers.First().ToString()
: "";
}
}
}
// Extraer inicializador de propiedad (para propiedades con => o { get; set; } = value)
if (propertyDeclaration.Initializer != null)
{
propertyElement.Initializer = propertyDeclaration.Initializer.Value.ToString();
}
// Extraer atributos
if (propertyDeclaration.AttributeLists.Any())
{
propertyElement.Attributes = propertyDeclaration.AttributeLists
.SelectMany(a => a.Attributes)
.Select(a => a.ToString())
.ToList();
}
// Generar firma normalizada
propertyElement.NormalizedSignature = $"{propertyElement.ReturnType} {propertyElement.Name}";
parent.AddChild(propertyElement);
LogInfo($"Found property: {propertyElement.Name} ({propertyElement.ReturnType})");
}
/// <summary>
/// Procesa un método de clase
/// </summary>
private void ProcessMethod(MethodDeclarationSyntax methodDeclaration, CodeElement parent)
{
var methodElement = new CodeElement
{
Type = CodeElement.ElementType.Method,
Name = methodDeclaration.Identifier.ToString(),
ReturnType = methodDeclaration.ReturnType.ToString(),
OriginalText = methodDeclaration.ToString(),
StartPosition = methodDeclaration.SpanStart,
Length = methodDeclaration.Span.Length,
Indentation = ExtractIndentation(methodDeclaration),
LeadingComments = ExtractLeadingComments(methodDeclaration),
TrailingComments = ExtractTrailingComments(methodDeclaration),
NamespaceName = GetNamespaceFromParent(parent),
ClassName = parent.Name
};
// Extraer modificadores
methodElement.Modifiers = methodDeclaration.Modifiers
.Select(m => m.ToString())
.ToArray();
// Extraer parámetros
methodElement.Parameters = ExtractParametersFromMethod(methodDeclaration);
// Extraer parámetros de tipo para métodos genéricos
if (methodDeclaration.TypeParameterList != null)
{
methodElement.TypeParameters = methodDeclaration.TypeParameterList.Parameters
.Select(p => p.Identifier.ToString())
.ToList();
}
// Extraer atributos
if (methodDeclaration.AttributeLists.Any())
{
methodElement.Attributes = methodDeclaration.AttributeLists
.SelectMany(a => a.Attributes)
.Select(a => a.ToString())
.ToList();
}
// Generar firma normalizada según requerimiento estricto
GenerateStrictMethodSignature(methodElement);
parent.AddChild(methodElement);
LogInfo($"Found method: {methodElement.Name} ({methodElement.NormalizedSignature})");
}
/// <summary>
/// Procesa un constructor de clase
/// </summary>
private void ProcessConstructor(ConstructorDeclarationSyntax constructorDeclaration, CodeElement parent)
{
var constructorElement = new CodeElement
{
Type = CodeElement.ElementType.Constructor,
Name = constructorDeclaration.Identifier.ToString(),
OriginalText = constructorDeclaration.ToString(),
StartPosition = constructorDeclaration.SpanStart,
Length = constructorDeclaration.Span.Length,
Indentation = ExtractIndentation(constructorDeclaration),
LeadingComments = ExtractLeadingComments(constructorDeclaration),
TrailingComments = ExtractTrailingComments(constructorDeclaration),
NamespaceName = GetNamespaceFromParent(parent),
ClassName = parent.Name
};
// Extraer modificadores
constructorElement.Modifiers = constructorDeclaration.Modifiers
.Select(m => m.ToString())
.ToArray();
// Extraer parámetros
constructorElement.Parameters = ExtractParametersFromConstructor(constructorDeclaration);
// Extraer atributos
if (constructorDeclaration.AttributeLists.Any())
{
constructorElement.Attributes = constructorDeclaration.AttributeLists
.SelectMany(a => a.Attributes)
.Select(a => a.ToString())
.ToList();
}
// Generar firma normalizada
GenerateStrictConstructorSignature(constructorElement);
parent.AddChild(constructorElement);
LogInfo($"Found constructor: {constructorElement.NormalizedSignature}");
}
/// <summary>
/// Extrae los parámetros de un método
/// </summary>
private List<ParameterInfo> ExtractParametersFromMethod(MethodDeclarationSyntax methodDeclaration)
{
var parameters = new List<ParameterInfo>();
if (methodDeclaration.ParameterList != null)
{
foreach (var param in methodDeclaration.ParameterList.Parameters)
{
var paramInfo = new ParameterInfo
{
Type = param.Type?.ToString() ?? "var",
Name = param.Identifier.ToString(),
HasDefaultValue = param.Default != null
};
// Capturar modificadores exactos (ref, out, in, params)
if (param.Modifiers.Any())
{
paramInfo.Modifier = string.Join(" ", param.Modifiers) + " ";
}
// Capturar valores por defecto si existen
if (param.Default != null)
{
paramInfo.DefaultValue = param.Default.Value.ToString();
}
parameters.Add(paramInfo);
}
}
return parameters;
}
/// <summary>
/// Extrae los parámetros de un constructor
/// </summary>
private List<ParameterInfo> ExtractParametersFromConstructor(ConstructorDeclarationSyntax constructorDeclaration)
{
var parameters = new List<ParameterInfo>();
if (constructorDeclaration.ParameterList != null)
{
foreach (var param in constructorDeclaration.ParameterList.Parameters)
{
var paramInfo = new ParameterInfo
{
Type = param.Type?.ToString() ?? "var",
Name = param.Identifier.ToString(),
HasDefaultValue = param.Default != null
};
// Capturar modificadores exactos (ref, out, in, params)
if (param.Modifiers.Any())
{
paramInfo.Modifier = string.Join(" ", param.Modifiers) + " ";
}
// Capturar valores por defecto si existen
if (param.Default != null)
{
paramInfo.DefaultValue = param.Default.Value.ToString();
}
parameters.Add(paramInfo);
}
}
return parameters;
}
/// <summary>
/// Genera una firma normalizada estricta para un método
/// </summary>
private void GenerateStrictMethodSignature(CodeElement methodElement)
{
// Construir lista de parámetros con tipo y nombre
var paramList = methodElement.Parameters.Select(p => $"{p.Modifier}{p.Type} {p.Name}").ToList();
string paramString = string.Join(", ", paramList);
// Incluir tipo genérico si existe
string typeParams = "";
if (methodElement.TypeParameters != null && methodElement.TypeParameters.Count > 0)
{
typeParams = $"<{string.Join(", ", methodElement.TypeParameters)}>";
}
// Incluir todo: tipo de retorno, nombre del método, parámetros de tipo y firma completa de parámetros
methodElement.NormalizedSignature = $"{methodElement.ReturnType} {methodElement.Name}{typeParams}({paramString})";
}
/// <summary>
/// Genera una firma normalizada estricta para un constructor
/// </summary>
private void GenerateStrictConstructorSignature(CodeElement constructorElement)
{
// Construir lista de parámetros con tipo y nombre
var paramList = constructorElement.Parameters.Select(p => $"{p.Modifier}{p.Type} {p.Name}").ToList();
string paramString = string.Join(", ", paramList);
// Para constructores: nombre de clase + parámetros completos
constructorElement.NormalizedSignature = $"{constructorElement.Name}({paramString})";
}
/// <summary>
/// Extrae la indentación de un nodo sintáctico
/// </summary>
private string ExtractIndentation(SyntaxNode node)
{
var leadingTrivia = node.GetLeadingTrivia();
var whitespaceTrivia = leadingTrivia.LastOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia));
return whitespaceTrivia.ToString();
}
/// <summary>
/// Extrae comentarios que preceden a un nodo
/// </summary>
private string ExtractLeadingComments(SyntaxNode node)
{
var leadingTrivia = node.GetLeadingTrivia();
var comments = leadingTrivia.Where(t =>
t.IsKind(SyntaxKind.SingleLineCommentTrivia) ||
t.IsKind(SyntaxKind.MultiLineCommentTrivia) ||
t.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia));
return string.Join(Environment.NewLine, comments);
}
/// <summary>
/// Extrae comentarios que siguen a un nodo
/// </summary>
private string ExtractTrailingComments(SyntaxNode node)
{
var trailingTrivia = node.GetTrailingTrivia();
var comments = trailingTrivia.Where(t =>
t.IsKind(SyntaxKind.SingleLineCommentTrivia) ||
t.IsKind(SyntaxKind.MultiLineCommentTrivia));
return string.Join(Environment.NewLine, comments);
}
/// <summary>
/// Obtiene el nombre del namespace desde el padre de un elemento
/// </summary>
private string GetNamespaceFromParent(CodeElement parent)
{
if (parent.Type == CodeElement.ElementType.Namespace)
return parent.Name;
if (parent.NamespaceName != null)
return parent.NamespaceName;
return VIRTUAL_NAMESPACE;
}
/// <summary>
/// Construye índices para búsqueda rápida de elementos
/// </summary>
private void BuildSearchIndices(CodeElement root)
{
// Limpiar índices existentes
_methodsBySignature.Clear();
_propertiesByName.Clear();
_fieldsByName.Clear();
_classesByName.Clear();
_namespacesByName.Clear();
_usingsByName.Clear();
// Recorrer árbol y poblar índices
BuildIndicesRecursive(root);
}
/// <summary>
/// Construye índices de búsqueda recursivamente
/// </summary>
private void BuildIndicesRecursive(CodeElement element)
{
// Agregar elemento al índice apropiado
switch (element.Type)
{
case CodeElement.ElementType.Method:
_methodsBySignature[element.NormalizedSignature] = element;
break;
case CodeElement.ElementType.Constructor:
_methodsBySignature[element.NormalizedSignature] = element;
break;
case CodeElement.ElementType.Property:
_propertiesByName[element.Name] = element;
break;
case CodeElement.ElementType.Field:
_fieldsByName[element.Name] = element;
break;
case CodeElement.ElementType.Class:
_classesByName[element.Name] = element;
break;
case CodeElement.ElementType.Namespace:
_namespacesByName[element.Name] = element;
break;
case CodeElement.ElementType.Using:
_usingsByName[element.Name] = element;
break;
}
// Procesar recursivamente hijos
foreach (var child in element.Children)
{
BuildIndicesRecursive(child);
}
}
#endregion
#region Métodos Privados para Merge de Código
/// <summary>
/// Realiza el merge de dos elementos de código y sus hijos
/// </summary>
private CodeElement MergeElements(CodeElement master, CodeElement snippet)
{
LogInfo($"Merging elements: {master.Type} and {snippet.Type}");
// El resultado base será una copia del elemento master
var result = master;
// Merge de usings
MergeUsings(result, snippet);
// Merge de namespaces
MergeNamespaces(result, snippet);
// Merge de clases
MergeClasses(result, snippet);
LogInfo("Element merge completed");
return result;
}
/// <summary>
/// Realiza el merge de directivas using
/// </summary>
private void MergeUsings(CodeElement master, CodeElement snippet)
{
var masterUsings = master.GetAllDescendantsOfType(CodeElement.ElementType.Using)
.Select(u => u.Name)
.ToHashSet();
foreach (var usingElement in snippet.GetAllDescendantsOfType(CodeElement.ElementType.Using))
{
if (!masterUsings.Contains(usingElement.Name))
{
// Nuevo using: agregar al master
var rootOrNamespace = master.Children.FirstOrDefault(c => c.Type == CodeElement.ElementType.Namespace) ?? master;
rootOrNamespace.AddChild(usingElement);
LogAddition(usingElement);
masterUsings.Add(usingElement.Name);
}
}
}
/// <summary>
/// Realiza el merge de namespaces
/// </summary>
private void MergeNamespaces(CodeElement master, CodeElement snippet)
{
var masterNamespaces = master.Children
.Where(c => c.Type == CodeElement.ElementType.Namespace)
.ToDictionary(n => n.Name);
foreach (var snippetNamespace in snippet.Children.Where(c => c.Type == CodeElement.ElementType.Namespace))
{
if (snippetNamespace.IsVirtualNamespace)
{
// Para namespace virtual, solo procesamos su contenido
foreach (var snippetClass in snippetNamespace.Children.Where(c => c.Type == CodeElement.ElementType.Class))
{
ProcessSnippetClass(master, snippetClass);
}
}
else if (masterNamespaces.TryGetValue(snippetNamespace.Name, out var masterNamespace))
{
// Namespace existente: merge de su contenido
foreach (var snippetClass in snippetNamespace.Children.Where(c => c.Type == CodeElement.ElementType.Class))
{
ProcessSnippetClass(masterNamespace, snippetClass);
}
}
else
{
// Nuevo namespace: agregar al master
master.AddChild(snippetNamespace);
LogAddition(snippetNamespace);
}
}
}
/// <summary>
/// Procesa una clase del snippet, ya sea agregándola o mergeando con una existente
/// </summary>
private void ProcessSnippetClass(CodeElement parent, CodeElement snippetClass)
{
// Buscar clase existente en el master
var masterClass = parent.Children
.FirstOrDefault(c => c.Type == CodeElement.ElementType.Class && c.Name == snippetClass.Name);
if (masterClass == null)
{
// Clase no existe: agregarla completa
if (!snippetClass.IsVirtualClass)
{
parent.AddChild(snippetClass);
LogAddition(snippetClass);
}
else
{
// Para clase virtual, solo procesamos su contenido directamente en el parent
MergeClassMembers(parent, snippetClass);
}
}
else
{
// Clase ya existe: hacer merge de miembros
MergeClassMembers(masterClass, snippetClass);
}
}
/// <summary>
/// Merge de miembros de una clase (fields, properties, methods, constructors)
/// </summary>
private void MergeClassMembers(CodeElement masterClass, CodeElement snippetClass)
{
// Merge de fields
MergeFields(masterClass, snippetClass);
// Merge de propiedades
MergeProperties(masterClass, snippetClass);
// Merge de métodos
MergeMethods(masterClass, snippetClass);
// Merge de constructores
MergeConstructors(masterClass, snippetClass);
// Clases anidadas
MergeNestedClasses(masterClass, snippetClass);
}
/// <summary>
/// Merge de campos (fields) de clase
/// </summary>
private void MergeFields(CodeElement masterClass, CodeElement snippetClass)
{
var masterFields = masterClass.Children
.Where(c => c.Type == CodeElement.ElementType.Field)
.ToDictionary(f => f.Name);
foreach (var snippetField in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Field))
{
if (!masterFields.ContainsKey(snippetField.Name))
{
// Field no existe: agregarlo
masterClass.AddChild(snippetField);
LogAddition(snippetField);
}
// No reemplazamos fields existentes
}
}
/// <summary>
/// Merge de propiedades de clase
/// </summary>
private void MergeProperties(CodeElement masterClass, CodeElement snippetClass)
{
var masterProperties = masterClass.Children
.Where(c => c.Type == CodeElement.ElementType.Property)
.ToDictionary(p => p.Name);
foreach (var snippetProperty in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Property))
{
if (!masterProperties.ContainsKey(snippetProperty.Name))
{
// Propiedad no existe: agregarla
masterClass.AddChild(snippetProperty);
LogAddition(snippetProperty);
}
// No reemplazamos propiedades existentes
}
}
/// <summary>
/// Merge de métodos de clase
/// </summary>
private void MergeMethods(CodeElement masterClass, CodeElement snippetClass)
{
var masterMethods = masterClass.Children
.Where(c => c.Type == CodeElement.ElementType.Method)
.ToDictionary(m => m.NormalizedSignature);
foreach (var snippetMethod in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Method))
{
if (masterMethods.TryGetValue(snippetMethod.NormalizedSignature, out var existingMethod))
{
// Método existe: reemplazarlo
masterClass.Children.Remove(existingMethod);
masterClass.AddChild(snippetMethod);
LogReplacement(existingMethod, snippetMethod);
}
else
{
// Método no existe: agregarlo
masterClass.AddChild(snippetMethod);
LogAddition(snippetMethod);
}
}
}
/// <summary>
/// Merge de constructores de clase
/// </summary>
private void MergeConstructors(CodeElement masterClass, CodeElement snippetClass)
{
var masterConstructors = masterClass.Children
.Where(c => c.Type == CodeElement.ElementType.Constructor)
.ToDictionary(m => m.NormalizedSignature);
foreach (var snippetConstructor in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Constructor))
{
if (masterConstructors.TryGetValue(snippetConstructor.NormalizedSignature, out var existingConstructor))
{
// Constructor existe: reemplazarlo
masterClass.Children.Remove(existingConstructor);
masterClass.AddChild(snippetConstructor);
LogReplacement(existingConstructor, snippetConstructor);
}
else
{
// Constructor no existe: agregarlo
masterClass.AddChild(snippetConstructor);
LogAddition(snippetConstructor);
}
}
}
/// <summary>
/// Merge de clases anidadas
/// </summary>
private void MergeNestedClasses(CodeElement masterClass, CodeElement snippetClass)
{
var masterNestedClasses = masterClass.Children
.Where(c => c.Type == CodeElement.ElementType.Class)
.ToDictionary(c => c.Name);
foreach (var snippetNestedClass in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Class))
{
if (masterNestedClasses.TryGetValue(snippetNestedClass.Name, out var existingClass))
{
// Clase anidada existe: merge recursivo
MergeClassMembers(existingClass, snippetNestedClass);
}
else
{
// Clase anidada no existe: agregarla
masterClass.AddChild(snippetNestedClass);
LogAddition(snippetNestedClass);
}
}
}
/// <summary>
/// Reconstruye el código a partir de un árbol de CodeElement
/// </summary>
private string ReconstructCode(CodeElement root)
{
var sb = new StringBuilder();
// Reconstruir usando desde el root
foreach (var usingElement in root.Children.Where(c => c.Type == CodeElement.ElementType.Using))
{
sb.AppendLine(usingElement.OriginalText);
}
// Reconstruir namespaces y su contenido
foreach (var namespaceElement in root.Children.Where(c => c.Type == CodeElement.ElementType.Namespace))
{
// Skip virtual namespace
if (namespaceElement.IsVirtualNamespace)
{
// Reconstruir directamente el contenido del namespace virtual
foreach (var classElement in namespaceElement.Children)
{
if (classElement.IsVirtualClass)
{
// Si es clase virtual, solo añadir su contenido
foreach (var member in classElement.Children)
{
sb.AppendLine(member.OriginalText);
}
}
else
{
sb.AppendLine(classElement.OriginalText);
}
}
}
else
{
// Namespace normal - reconstruir completo
sb.AppendLine(namespaceElement.OriginalText);
}
}
// Reconstruir clases que estén directamente en el root
foreach (var classElement in root.Children.Where(c => c.Type == CodeElement.ElementType.Class))
{
if (!classElement.IsVirtualClass)
{
sb.AppendLine(classElement.OriginalText);
}
}
return sb.ToString();
}
#endregion
#region Métodos para Logging
/// <summary>
/// Registra una operación de adición
/// </summary>
private void LogAddition(CodeElement element)
{
_log.AppendLine($"ADDED: {element.Type} {element.Name}");
}
/// <summary>
/// Registra una operación de reemplazo
/// </summary>
private void LogReplacement(CodeElement oldElement, CodeElement newElement)
{
_log.AppendLine($"REPLACED: {oldElement.Type} {oldElement.Name} with new implementation");
}
/// <summary>
/// Registra un mensaje informativo
/// </summary>
private void LogInfo(string message)
{
_log.AppendLine($"INFO: {message}");
}
/// <summary>
/// Registra un mensaje de error
/// </summary>
private void LogError(string message)
{
_log.AppendLine($"ERROR: {message}");
}
#endregion
}
/// <summary>
/// Elemento de código analizado
/// </summary>
public class CodeElement
{
public enum ElementType
{
Using,
Namespace,
Class,
Method,
Property,
Field,
Constructor,
Other
}
#region Propiedades Básicas
// Propiedades originales
public ElementType Type { get; set; }
public string Name { get; set; }
public string NamespaceName { get; set; }
public string ClassName { get; set; }
public string NormalizedSignature { get; set; }
public string OriginalText { get; set; }
public int StartPosition { get; set; }
public int Length { get; set; }
// Constantes para namespace y clase virtual
private const string VIRTUAL_NAMESPACE = "__VirtualNamespace__";
private const string VIRTUAL_CLASS = "__VirtualClass__";
public bool IsVirtualNamespace => NamespaceName == VIRTUAL_NAMESPACE;
public bool IsVirtualClass => ClassName == VIRTUAL_CLASS;
#endregion
#region Relaciones Jerárquicas
// Relaciones jerárquicas
public CodeElement Parent { get; set; }
public List<CodeElement> Children { get; set; } = new List<CodeElement>();
// Nivel de anidamiento (útil para visualización)
public int NestingLevel => Parent == null ? 0 : Parent.NestingLevel + 1;
// Path completo en la jerarquía
public string FullPath
{
get
{
if (Parent == null) return Name;
return $"{Parent.FullPath}.{Name}";
}
}
#endregion
#region Metadata para Reconstrucción
// Metadata para reconstrucción
public string[] Modifiers { get; set; }
public List<string> Attributes { get; set; } = new List<string>();
// Comentarios y trivia
public string LeadingComments { get; set; }
public string TrailingComments { get; set; }
public string Indentation { get; set; }
// Parámetros de tipo para genéricos
public List<string> TypeParameters { get; set; } = new List<string>();
// Para clases: información de herencia e interfaces
public string BaseClass { get; set; }
public List<string> ImplementedInterfaces { get; set; } = new List<string>();
// Para métodos y propiedades: tipo de retorno específico
public string ReturnType { get; set; }
// Para propiedades: accessors
public bool HasGetter { get; set; }
public bool HasSetter { get; set; }
public string GetterAccessibility { get; set; }
public string SetterAccessibility { get; set; }
// Para campos: inicializador
public string Initializer { get; set; }
// Para métodos y constructores: parámetros
public List<ParameterInfo> Parameters { get; set; } = new List<ParameterInfo>();
#endregion
#region Métodos
/// <summary>
/// Agrega un elemento hijo con manejo automático de relación Parent
/// </summary>
public void AddChild(CodeElement child)
{
Children.Add(child);
child.Parent = this;
}
/// <summary>
/// Busca un elemento hijo por nombre y tipo
/// </summary>
public CodeElement FindChild(string name, ElementType type)
{
return Children.FirstOrDefault(c => c.Name == name && c.Type == type);
}
/// <summary>
/// Encuentra todos los elementos hijos de un tipo específico
/// </summary>
public IEnumerable<CodeElement> FindAllChildren(ElementType type)
{
return Children.Where(c => c.Type == type);
}
/// <summary>
/// Busca en toda la jerarquía del elemento según un predicado
/// </summary>
public CodeElement FindInHierarchy(Func<CodeElement, bool> predicate)
{
if (predicate(this)) return this;
foreach (var child in Children)
{
var result = child.FindInHierarchy(predicate);
if (result != null) return result;
}
return null;
}
/// <summary>
/// Obtiene todos los elementos descendientes de un tipo específico
/// </summary>
public IEnumerable<CodeElement> GetAllDescendantsOfType(ElementType type)
{
var result = new List<CodeElement>();
CollectDescendantsOfType(this, type, result);
return result;
}
/// <summary>
/// Método auxiliar para recopilar descendientes recursivamente
/// </summary>
private void CollectDescendantsOfType(CodeElement element, ElementType type, List<CodeElement> result)
{
foreach (var child in element.Children)
{
if (child.Type == type)
result.Add(child);
CollectDescendantsOfType(child, type, result);
}
}
/// <summary>
/// Representación en cadena del elemento
/// </summary>
public override string ToString()
{
return $"{Type}: {NormalizedSignature ?? Name}";
}
#endregion
}
/// <summary>
/// Información de un parámetro de método o constructor
/// </summary>
public class ParameterInfo
{
public string Type { get; set; }
public string Name { get; set; }
public string Modifier { get; set; } = ""; // ref, out, in, params
public bool HasDefaultValue { get; set; }
public string DefaultValue { get; set; }
public override string ToString()
{
return $"{Modifier}{Type} {Name}{(HasDefaultValue ? $" = {DefaultValue}" : "")}";
}
}