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; /// /// Clase para realizar análisis y merge de código C# utilizando Roslyn /// 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 _methodsBySignature = new Dictionary(); private Dictionary _propertiesByName = new Dictionary(); private Dictionary _fieldsByName = new Dictionary(); private Dictionary _classesByName = new Dictionary(); private Dictionary _namespacesByName = new Dictionary(); private Dictionary _usingsByName = new Dictionary(); #endregion #region Métodos Públicos /// /// Constructor de la clase CodeMerger /// /// Namespace por defecto a usar cuando no se especifique /// Clase por defecto a usar cuando no se especifique public cCodeMerger(string defaultNamespace = null, string defaultClass = null) { if (!string.IsNullOrEmpty(defaultNamespace)) DefaultNamespace = defaultNamespace; if (!string.IsNullOrEmpty(defaultClass)) DefaultClass = defaultClass; LogInfo("CodeMerger initialized"); } /// /// Verifica rápidamente si un código contiene un namespace específico /// 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); } /// /// Verifica rápidamente si un código contiene una clase específica /// 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); } /// /// Analiza el código C# y genera un árbol de CodeElement /// /// Código C# a analizar /// Elemento raíz del árbol generado 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; } } /// /// Realiza el merge de código snippet dentro de código master /// /// Código principal que recibirá las modificaciones /// Código a integrar en el master /// Código resultante del merge 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 } } /// /// Obtiene el log de operaciones realizadas /// public string GetLog() { return _log.ToString(); } /// /// Limpia el log de operaciones /// public void ClearLog() { _log.Clear(); LogInfo("Log cleared"); } #endregion #region Métodos Privados para Análisis de Código /// /// Prepara el código para análisis, añadiendo namespace y clase si es necesario /// 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} }}"; } } /// /// Construye el árbol de CodeElement a partir del árbol sintáctico de Roslyn /// 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()) { ProcessNamespace(namespaceDeclaration, rootElement); } // Procesar clases que estén directamente en el root (sin namespace) foreach (var typeDeclaration in compilationUnit.Members.OfType()) { if (typeDeclaration is ClassDeclarationSyntax classDeclaration) { ProcessClass(classDeclaration, rootElement); } } return rootElement; } /// /// Procesa un namespace y sus miembros /// 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); } } } /// /// Procesa una clase y sus miembros /// 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); } } } /// /// Procesa un campo (field) de clase /// 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})"); } } /// /// Procesa una propiedad de clase /// 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})"); } /// /// Procesa un método de clase /// 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})"); } /// /// Procesa un constructor de clase /// 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}"); } /// /// Extrae los parámetros de un método /// private List ExtractParametersFromMethod(MethodDeclarationSyntax methodDeclaration) { var parameters = new List(); 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; } /// /// Extrae los parámetros de un constructor /// private List ExtractParametersFromConstructor(ConstructorDeclarationSyntax constructorDeclaration) { var parameters = new List(); 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; } /// /// Genera una firma normalizada estricta para un método /// 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})"; } /// /// Genera una firma normalizada estricta para un constructor /// 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})"; } /// /// Extrae la indentación de un nodo sintáctico /// private string ExtractIndentation(SyntaxNode node) { var leadingTrivia = node.GetLeadingTrivia(); var whitespaceTrivia = leadingTrivia.LastOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia)); return whitespaceTrivia.ToString(); } /// /// Extrae comentarios que preceden a un nodo /// 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); } /// /// Extrae comentarios que siguen a un nodo /// 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); } /// /// Obtiene el nombre del namespace desde el padre de un elemento /// private string GetNamespaceFromParent(CodeElement parent) { if (parent.Type == CodeElement.ElementType.Namespace) return parent.Name; if (parent.NamespaceName != null) return parent.NamespaceName; return VIRTUAL_NAMESPACE; } /// /// Construye índices para búsqueda rápida de elementos /// 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); } /// /// Construye índices de búsqueda recursivamente /// 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 /// /// Realiza el merge de dos elementos de código y sus hijos /// 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; } /// /// Realiza el merge de directivas using /// 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); } } } /// /// Realiza el merge de namespaces /// 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); } } } /// /// Procesa una clase del snippet, ya sea agregándola o mergeando con una existente /// 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); } } /// /// Merge de miembros de una clase (fields, properties, methods, constructors) /// 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); } /// /// Merge de campos (fields) de clase /// 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 } } /// /// Merge de propiedades de clase /// 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 } } /// /// Merge de métodos de clase /// 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); } } } /// /// Merge de constructores de clase /// 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); } } } /// /// Merge de clases anidadas /// 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); } } } /// /// Reconstruye el código a partir de un árbol de CodeElement /// 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 /// /// Registra una operación de adición /// private void LogAddition(CodeElement element) { _log.AppendLine($"ADDED: {element.Type} {element.Name}"); } /// /// Registra una operación de reemplazo /// private void LogReplacement(CodeElement oldElement, CodeElement newElement) { _log.AppendLine($"REPLACED: {oldElement.Type} {oldElement.Name} with new implementation"); } /// /// Registra un mensaje informativo /// private void LogInfo(string message) { _log.AppendLine($"INFO: {message}"); } /// /// Registra un mensaje de error /// private void LogError(string message) { _log.AppendLine($"ERROR: {message}"); } #endregion } /// /// Elemento de código analizado /// 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 Children { get; set; } = new List(); // 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 Attributes { get; set; } = new List(); // 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 TypeParameters { get; set; } = new List(); // Para clases: información de herencia e interfaces public string BaseClass { get; set; } public List ImplementedInterfaces { get; set; } = new List(); // 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 Parameters { get; set; } = new List(); #endregion #region Métodos /// /// Agrega un elemento hijo con manejo automático de relación Parent /// public void AddChild(CodeElement child) { Children.Add(child); child.Parent = this; } /// /// Busca un elemento hijo por nombre y tipo /// public CodeElement FindChild(string name, ElementType type) { return Children.FirstOrDefault(c => c.Name == name && c.Type == type); } /// /// Encuentra todos los elementos hijos de un tipo específico /// public IEnumerable FindAllChildren(ElementType type) { return Children.Where(c => c.Type == type); } /// /// Busca en toda la jerarquía del elemento según un predicado /// public CodeElement FindInHierarchy(Func predicate) { if (predicate(this)) return this; foreach (var child in Children) { var result = child.FindInHierarchy(predicate); if (result != null) return result; } return null; } /// /// Obtiene todos los elementos descendientes de un tipo específico /// public IEnumerable GetAllDescendantsOfType(ElementType type) { var result = new List(); CollectDescendantsOfType(this, type, result); return result; } /// /// Método auxiliar para recopilar descendientes recursivamente /// private void CollectDescendantsOfType(CodeElement element, ElementType type, List result) { foreach (var child in element.Children) { if (child.Type == type) result.Add(child); CollectDescendantsOfType(child, type, result); } } /// /// Representación en cadena del elemento /// public override string ToString() { return $"{Type}: {NormalizedSignature ?? Name}"; } #endregion } /// /// Información de un parámetro de método o constructor /// 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}" : "")}"; } }