diff --git a/CodePlay.Core/Converters/CSharpCodeGenerator.cs b/CodePlay.Core/Converters/CSharpCodeGenerator.cs new file mode 100644 index 0000000..3c62540 --- /dev/null +++ b/CodePlay.Core/Converters/CSharpCodeGenerator.cs @@ -0,0 +1,226 @@ +using System.Text; +using CodePlay.Core.Interfaces; +using CodePlay.Core.Common; + +namespace CodePlay.Core.Converters; + +/// +/// C# 代码生成器 +/// +public class CSharpCodeGenerator : ICodeGenerator +{ + private readonly StringBuilder _output = new(); + private int _indentLevel; + + /// + /// 从语法树生成 C# 代码 + /// + public string Generate(Interfaces.SyntaxTree syntaxTree) + { + _output.Clear(); + _indentLevel = 0; + + // 生成 using 指令 + GenerateUsings(syntaxTree); + + // 生成代码 + GenerateNode(syntaxTree.Root); + + return _output.ToString(); + } + + private void GenerateUsings(Interfaces.SyntaxTree tree) + { + _output.AppendLine("using System;"); + _output.AppendLine("using System.Collections.Generic;"); + _output.AppendLine(); + } + + private void GenerateNode(Interfaces.SyntaxNode node) + { + switch (node.Type) + { + case SyntaxNodeType.CompilationUnit: + GenerateCompilationUnit(node); + break; + case SyntaxNodeType.Namespace: + GenerateNamespace(node); + break; + case SyntaxNodeType.Class: + GenerateClass(node); + break; + case SyntaxNodeType.Method: + GenerateMethod(node); + break; + case SyntaxNodeType.Property: + GenerateProperty(node); + break; + case SyntaxNodeType.Field: + GenerateField(node); + break; + default: + GenerateDefault(node); + break; + } + } + + private void GenerateCompilationUnit(Interfaces.SyntaxNode node) + { + foreach (var child in node.Children) + { + GenerateNode(child); + } + } + + private void GenerateNamespace(Interfaces.SyntaxNode node) + { + var namespaceName = ExtractNamespaceName(node.Text); + _output.AppendLine($"namespace {namespaceName}"); + _output.AppendLine("{"); + _indentLevel++; + + foreach (var child in node.Children) + { + if (child.Type != SyntaxNodeType.Unknown) + { + GenerateNode(child); + } + } + + _indentLevel--; + _output.AppendLine("}"); + _output.AppendLine(); + } + + private void GenerateClass(Interfaces.SyntaxNode node) + { + var classDeclaration = node.Text.Split('{')[0].Trim(); + _output.AppendLine(Indent(classDeclaration)); + _output.AppendLine(Indent("{")); + _indentLevel++; + + foreach (var child in node.Children) + { + if (child.Type != SyntaxNodeType.Unknown && child.Type != SyntaxNodeType.CompilationUnit) + { + GenerateNode(child); + } + } + + _indentLevel--; + _output.AppendLine(Indent("}")); + _output.AppendLine(); + } + + private void GenerateMethod(Interfaces.SyntaxNode node) + { + var methodSignature = ExtractMethodSignature(node.Text); + _output.AppendLine(Indent(methodSignature)); + _output.AppendLine(Indent("{")); + _indentLevel++; + + var methodBody = ExtractMethodBody(node.Text); + if (!string.IsNullOrWhiteSpace(methodBody)) + { + var lines = methodBody.Split('\n'); + foreach (var line in lines) + { + if (!string.IsNullOrWhiteSpace(line.Trim())) + { + _output.AppendLine(Indent(line.Trim())); + } + } + } + + _indentLevel--; + _output.AppendLine(Indent("}")); + _output.AppendLine(); + } + + private void GenerateProperty(Interfaces.SyntaxNode node) + { + var propertyDeclaration = node.Text.Trim(); + if (!string.IsNullOrWhiteSpace(propertyDeclaration)) + { + _output.AppendLine(Indent(propertyDeclaration.EndsWith(";") ? propertyDeclaration : propertyDeclaration + ";")); + } + } + + private void GenerateField(Interfaces.SyntaxNode node) + { + var fieldDeclaration = node.Text.Trim(); + if (!string.IsNullOrWhiteSpace(fieldDeclaration)) + { + _output.AppendLine(Indent(fieldDeclaration.EndsWith(";") ? fieldDeclaration : fieldDeclaration + ";")); + } + } + + private void GenerateDefault(Interfaces.SyntaxNode node) + { + if (!string.IsNullOrWhiteSpace(node.Text) && node.Type != SyntaxNodeType.Comment) + { + var lines = node.Text.Split('\n'); + foreach (var line in lines) + { + var trimmed = line.Trim(); + if (!string.IsNullOrWhiteSpace(trimmed) && + trimmed != "{" && + trimmed != "}") + { + _output.AppendLine(Indent(trimmed)); + } + } + } + + foreach (var child in node.Children) + { + GenerateNode(child); + } + } + + private string Indent(string text) + { + var indent = new string(' ', _indentLevel * 4); + return indent + text; + } + + private string ExtractNamespaceName(string text) + { + if (text.StartsWith("namespace ")) + { + var match = System.Text.RegularExpressions.Regex.Match(text, @"namespace\s+([\w.]+)"); + return match.Success ? match.Groups[1].Value : "CodePlay.Converted"; + } + return "CodePlay.Converted"; + } + + private string ExtractMethodSignature(string text) + { + var lines = text.Split('\n'); + foreach (var line in lines) + { + var trimmed = line.Trim(); + if (trimmed.Length > 0 && + !trimmed.StartsWith("{") && + !trimmed.StartsWith("}") && + (trimmed.Contains("(") || trimmed.Contains("void") || trimmed.Contains("public") || trimmed.Contains("private"))) + { + return trimmed.EndsWith("{") ? trimmed.Substring(0, trimmed.Length - 1).Trim() : trimmed; + } + } + return text; + } + + private string ExtractMethodBody(string text) + { + var startIndex = text.IndexOf('{'); + var endIndex = text.LastIndexOf('}'); + + if (startIndex >= 0 && endIndex > startIndex) + { + return text.Substring(startIndex + 1, endIndex - startIndex - 1).Trim(); + } + + return string.Empty; + } +} diff --git a/CodePlay.Core/Converters/JavaToCSharpConverter.cs b/CodePlay.Core/Converters/JavaToCSharpConverter.cs new file mode 100644 index 0000000..cded8f4 --- /dev/null +++ b/CodePlay.Core/Converters/JavaToCSharpConverter.cs @@ -0,0 +1,147 @@ +using CodePlay.Core.Interfaces; +using CodePlay.Core.Models; +using CodePlay.Core.Common; + +namespace CodePlay.Core.Converters; + +/// +/// Java 到 C# 代码转换器 +/// +public class JavaToCSharpConverter : IConverter +{ + private readonly JavaToCSharpStrategy _strategy; + private readonly CSharpCodeGenerator _codeGenerator; + + public JavaToCSharpConverter() + { + _strategy = new JavaToCSharpStrategy(); + _codeGenerator = new CSharpCodeGenerator(); + } + + /// + /// 转换语法树 + /// + public async Task ConvertAsync( + Interfaces.SyntaxTree syntaxTree, + LanguageType targetLanguage, + ConversionOptions? options = null, + CancellationToken cancellationToken = default) + { + var result = new ConversionResult + { + Success = false, + Warnings = new List(), + Report = new ConversionReport() + }; + + if (targetLanguage != LanguageType.CSharp) + { + result.ErrorMessage = "This converter only supports Java to C# conversion"; + return result; + } + + try + { + var context = new ConversionContext + { + SourceLanguage = LanguageType.Java, + TargetLanguage = LanguageType.CSharp, + Options = options + }; + + // 转换根节点 + var convertedRoot = _strategy.ConvertNode(syntaxTree.Root, context); + + // 创建新的语法树 + var convertedTree = new Interfaces.SyntaxTree + { + Language = LanguageType.CSharp, + Root = convertedRoot, + SourceCode = syntaxTree.SourceCode + }; + + // 保留注释和文档 + if (options?.KeepComments == true) + { + convertedTree.Documentation = syntaxTree.Documentation + .Select(d => new SyntaxDocumentation + { + ElementName = d.ElementName, + Content = ConvertDocumentation(d.Content), + Format = DocFormat.XmlDoc + }).ToList(); + } + + // 生成 C# 代码 + var generatedCode = _codeGenerator.Generate(convertedTree); + + result.TransformedCode = generatedCode; + result.Success = true; + + // 生成报告 + if (result.Report != null) + { + result.Report.LinesConverted = syntaxTree.SourceCode?.Split('\n').Length ?? 0; + result.Report.ClassesConverted = CountClasses(syntaxTree.Root); + result.Report.MethodsConverted = CountMethods(syntaxTree.Root); + result.Report.TodoItems = context.TodoItems; + result.Report.Issues = context.Issues; + result.Report.TransformationLog = context.Logs; + } + + context.Logs.Add(new TransformationLog + { + Timestamp = DateTime.UtcNow, + Operation = "Conversion", + Details = "Java to C# conversion completed", + Level = LogLevel.Info + }); + + result.Warnings = context.Issues + .Select(i => new ConversionWarning + { + Code = $"WARN_{i.Type}", + Message = i.Description, + Suggestion = i.Suggestion + }).ToList(); + } + catch (Exception ex) + { + result.ErrorMessage = ex.Message; + result.Success = false; + } + + return await Task.FromResult(result); + } + + private string ConvertDocumentation(string javaDocContent) + { + // JavaDoc 到 XML Doc 转换 + var content = javaDocContent + .Replace("/**", "///") + .Replace("*/", "") + .Replace("*", "///") + .Replace("@param ", "") + .Replace("@throws ", " +/// Java 到 C# 转换策略 +/// +public class JavaToCSharpStrategy : IConversionStrategy +{ + /// + /// 源语言 + /// + public LanguageType SourceLanguage => LanguageType.Java; + + /// + /// 目标语言 + /// + public LanguageType TargetLanguage => LanguageType.CSharp; + + private readonly List _typeMappings = new(); + + public JavaToCSharpStrategy() + { + InitializeTypeMappings(); + } + + private void InitializeTypeMappings() + { + _typeMappings.AddRange(new[] + { + new TypeMapping("java.lang.String", "string"), + new TypeMapping("java.lang.Object", "object"), + new TypeMapping("java.lang.Integer", "int"), + new TypeMapping("java.lang.Long", "long"), + new TypeMapping("java.lang.Boolean", "bool"), + new TypeMapping("java.lang.Double", "double"), + new TypeMapping("java.lang.Float", "float"), + new TypeMapping("java.util.ArrayList", "List"), + new TypeMapping("java.util.List", "IEnumerable"), + new TypeMapping("java.util.HashMap", "Dictionary"), + new TypeMapping("java.util.Map", "IDictionary"), + new TypeMapping("java.util.stream.Stream", "IEnumerable"), + new TypeMapping("java.util.Arrays", "Array"), + new TypeMapping("java.time.LocalDateTime", "DateTime"), + new TypeMapping("java.time.Duration", "TimeSpan"), + new TypeMapping("java.lang.Exception", "Exception"), + new TypeMapping("java.lang.IllegalArgumentException", "ArgumentException"), + new TypeMapping("java.lang.IllegalStateException", "InvalidOperationException"), + new TypeMapping("java.lang.NullPointerException", "NullReferenceException"), + new TypeMapping("java.util.concurrent.CompletableFuture", "Task"), + new TypeMapping("System.out.println", "Console.WriteLine"), + new TypeMapping("public static void main", "static void Main"), + }); + } + + /// + /// 映射类型 + /// + public string MapType(string sourceType) + { + var result = sourceType; + + foreach (var mapping in _typeMappings) + { + result = result.Replace(mapping.SourceType, mapping.TargetType); + } + + // Java 到 C# 的特定转换 + result = result + .Replace("var ", "var ") + .Replace("final ", "") + .Replace(".size()", ".Count") + .Replace(".length", ".Length") + .Replace(".equals(", ".Equals(") + .Replace(".toString()", ".ToString()") + .Replace(".hashCode()", ".GetHashCode()"); + + return result; + } + + /// + /// 转换语法节点 + /// + public Interfaces.SyntaxNode ConvertNode(Interfaces.SyntaxNode node, ConversionContext context) + { + var newNode = new Interfaces.SyntaxNode + { + Type = node.Type, + Text = ConvertText(node.Text, context), + Metadata = new Dictionary(node.Metadata), + Parent = node.Parent, + Children = new List(), + IsUnconvertible = node.IsUnconvertible, + TodoDescription = node.TodoDescription + }; + + foreach (var child in node.Children) + { + var convertedChild = ConvertNode(child, context); + convertedChild.Parent = newNode; + newNode.Children.Add(convertedChild); + } + + // 检测不可转换的语法 + CheckUnconvertibleSyntax(node.Text, context); + + return newNode; + } + + private string ConvertText(string text, ConversionContext context) + { + var result = text; + + // package 转 namespace + if (result.StartsWith("package ")) + { + result = result.Replace("package ", "namespace ") + .Replace(";", " {"); + } + + // import 转 using + if (result.StartsWith("import ")) + { + result = result.Replace("import ", "using ") + .Replace(";", ";"); + } + + // 类型映射 + result = MapType(result); + + // Java 特定语法处理 + result = result + .Replace("super.", "base.") + .Replace("System.out.println", "Console.WriteLine") + .Replace("@Override", "[Override]") + .Replace("extends", ":") + .Replace("implements", ":"); + + // 方法声明转换 + result = System.Text.RegularExpressions.Regex.Replace( + result, + @"public\s+(\w+)\s+get(\w+)\(\)", + "public $1 Get$2 { get; }" + ); + + result = System.Text.RegularExpressions.Regex.Replace( + result, + @"public\s+void\s+set(\w+)\(\1\s+\w+\)", + "public void Set$1 { set; }" + ); + + return result; + } + + private void CheckUnconvertibleSyntax(string text, ConversionContext context) + { + // 检测 Stream API + if (text.Contains(".stream(") || text.Contains("Stream.")) + { + context.Issues.Add(new ConversionIssue + { + Type = IssueType.UnconvertibleSyntax, + Description = "Java Stream API 需要转换为 LINQ", + OriginalCode = text, + Suggestion = "使用 LINQ 替代:stream().filter() → .Where(), stream().map() → .Select()" + }); + + context.TodoItems.Add(new TodoItem + { + Description = "将 Stream API 转换为 LINQ", + OriginalSyntax = "Stream API", + WhyNotDirect = "Java Stream 和 LINQ 语法不同,需要手动调整", + RecommendedAlternative = "使用 .Where(), .Select(), .Aggregate() 等 LINQ 方法" + }); + } + + // 检测 CompletableFuture + if (text.Contains("CompletableFuture") || text.Contains("thenApply") || text.Contains("thenAccept")) + { + context.Issues.Add(new ConversionIssue + { + Type = IssueType.UnconvertibleSyntax, + Description = "CompletableFuture 需要转换为 async/await", + OriginalCode = text, + Suggestion = "使用 async/await 模式:completableFuture.thenApply() → await Task" + }); + + context.TodoItems.Add(new TodoItem + { + Description = "将 CompletableFuture 转换为 async/await", + OriginalSyntax = "CompletableFuture", + WhyNotDirect = "Java CompletableFuture 和 C# async/await 模式不同", + RecommendedAlternative = "使用 Task 和 async/await 关键字" + }); + } + + // 检测接口默认方法 + if (text.Contains("default ") && text.Contains(" interface ")) + { + context.Logs.Add(new TransformationLog + { + Timestamp = DateTime.UtcNow, + Operation = "Warning", + Details = "Java 接口默认方法需要特殊处理", + Level = LogLevel.Warning + }); + } + } +} diff --git a/CodePlay.Core/Parsers/JavaParser.cs b/CodePlay.Core/Parsers/JavaParser.cs new file mode 100644 index 0000000..1e1b206 --- /dev/null +++ b/CodePlay.Core/Parsers/JavaParser.cs @@ -0,0 +1,167 @@ +using System.Text; +using CodePlay.Core.Interfaces; +using CodePlay.Core.Models; +using CodePlay.Core.Common; + +namespace CodePlay.Core.Parsers; + +/// +/// Java 语法解析器(简化版本,基于文本处理) +/// +public class JavaParser : BaseParser +{ + /// + /// 支持的语言类型 + /// + public override LanguageType SupportedLanguage => LanguageType.Java; + + /// + /// 解析 Java 源代码 + /// + public override Task ParseAsync(string sourceCode, CancellationToken cancellationToken = default) + { + var tree = CreateSyntaxTree(); + tree.SourceCode = sourceCode; + + // 简单提取类和方** + var root = new Interfaces.SyntaxNode + { + Type = SyntaxNodeType.CompilationUnit, + Text = sourceCode + }; + + ExtractImports(tree, sourceCode); + ExtractPackages(tree, sourceCode); + ExtractClasses(tree, sourceCode, root); + ExtractComments(tree, sourceCode); + + tree.Root = root; + + return Task.FromResult(tree); + } + + private void ExtractPackages(Interfaces.SyntaxTree tree, string sourceCode) + { + var packageMatch = System.Text.RegularExpressions.Regex.Match(sourceCode, @"package\s+([\w.]+);"); + if (packageMatch.Success) + { + tree.Documentation.Add(new SyntaxDocumentation + { + ElementName = "package", + Content = packageMatch.Groups[1].Value, + Format = DocFormat.JavaDoc + }); + } + } + + private void ExtractImports(Interfaces.SyntaxTree tree, string sourceCode) + { + var importMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, @"import\s+([\w.]+);"); + foreach (System.Text.RegularExpressions.Match match in importMatches) + { + tree.Comments.Add(new SyntaxComment + { + Text = $"import {match.Groups[1].Value};", + Type = CommentType.SingleLine, + LineNumber = 0 + }); + } + } + + private void ExtractClasses(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root) + { + var classPattern = @"(public|private|protected)?\s*(abstract|final|static)?\s*class\s+(\w+)(\s+extends\s+\w+)?(\s+implements\s+[\w,]+)?"; + var classMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, classPattern); + + foreach (System.Text.RegularExpressions.Match match in classMatches) + { + var classNode = new Interfaces.SyntaxNode + { + Type = SyntaxNodeType.Class, + Text = match.Value + }; + + root.Children.Add(classNode); + + // 提取方法 + ExtractMethods(match.Value, classNode); + + // 提取字段 + ExtractFields(match.Value, classNode); + } + } + + private void ExtractMethods(string classCode, Interfaces.SyntaxNode classNode) + { + var methodPattern = @"(public|private|protected)?\s*(static)?\s*(\w+(?:<[^>]+>)?)\s+(\w+)\s*\([^)]*\)\s*(\{[^}]*\})?"; + var methodMatches = System.Text.RegularExpressions.Regex.Matches(classCode, methodPattern); + + foreach (System.Text.RegularExpressions.Match match in methodMatches) + { + if (!match.Value.Contains(" class ") && !match.Value.Contains(" new ")) + { + var methodNode = new Interfaces.SyntaxNode + { + Type = SyntaxNodeType.Method, + Text = match.Value + }; + + classNode.Children.Add(methodNode); + } + } + } + + private void ExtractFields(string classCode, Interfaces.SyntaxNode classNode) + { + var fieldPattern = @"(public|private|protected)?\s*(static)?\s*(final)?\s*(\w+)\\s+(\w+)\\s*;"; + var fieldMatches = System.Text.RegularExpressions.Regex.Matches(classCode, fieldPattern); + + foreach (System.Text.RegularExpressions.Match match in fieldMatches) + { + var fieldNode = new Interfaces.SyntaxNode + { + Type = SyntaxNodeType.Field, + Text = match.Value + }; + + classNode.Children.Add(fieldNode); + } + } + + private void ExtractComments(Interfaces.SyntaxTree tree, string sourceCode) + { + var lines = sourceCode.Split('\n'); + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i].Trim(); + + if (line.StartsWith("//")) + { + tree.Comments.Add(new SyntaxComment + { + Text = line, + Type = CommentType.SingleLine, + LineNumber = i + 1 + }); + } + else if (line.StartsWith("/**")) + { + var javaDocContent = new StringBuilder(); + for (int j = i; j < lines.Length; j++) + { + javaDocContent.AppendLine(lines[j]); + if (lines[j].Contains("*/")) + { + tree.Comments.Add(new SyntaxComment + { + Text = javaDocContent.ToString(), + Type = CommentType.JavaDoc, + LineNumber = i + 1 + }); + break; + } + } + } + } + } +} diff --git a/CodePlay.Core/Services/ConversionService.cs b/CodePlay.Core/Services/ConversionService.cs index c117d08..64ebc39 100644 --- a/CodePlay.Core/Services/ConversionService.cs +++ b/CodePlay.Core/Services/ConversionService.cs @@ -17,6 +17,7 @@ public class ConversionService { // 注册转换器 RegisterConverter(LanguageType.CSharp, LanguageType.Java, new CSharpToJavaConverter()); + RegisterConverter(LanguageType.Java, LanguageType.CSharp, new JavaToCSharpConverter()); } private void RegisterConverter(LanguageType source, LanguageType target, IConverter converter) @@ -75,6 +76,7 @@ public class ConversionService return language switch { LanguageType.CSharp => new CSharpParser(), + LanguageType.Java => new JavaParser(), _ => null }; } diff --git a/CodePlay.Tests/Converters/JavaToCSharpConverterTests.cs b/CodePlay.Tests/Converters/JavaToCSharpConverterTests.cs new file mode 100644 index 0000000..ca52496 --- /dev/null +++ b/CodePlay.Tests/Converters/JavaToCSharpConverterTests.cs @@ -0,0 +1,105 @@ +using CodePlay.Core.Converters; +using CodePlay.Core.Models; +using CodePlay.Core.Common; +using CodePlay.Core.Parsers; +using Xunit; + +namespace CodePlay.Tests.Converters; + +public class JavaToCSharpConverterTests +{ + private readonly JavaToCSharpConverter _converter; + private readonly JavaParser _parser; + + public JavaToCSharpConverterTests() + { + _converter = new JavaToCSharpConverter(); + _parser = new JavaParser(); + } + + [Fact] + public async Task ConvertAsync_SimpleClass_ShouldConvertSuccessfully() + { + var sourceCode = @" +package com.test; + +import java.util.List; + +public class Person { + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +}"; + + var syntaxTree = await _parser.ParseAsync(sourceCode); + var result = await _converter.ConvertAsync(syntaxTree, LanguageType.CSharp); + + Assert.NotNull(result); + Assert.True(result.Success); + Assert.NotNull(result.TransformedCode); + Assert.Contains("class Person", result.TransformedCode); + } + + [Fact] + public async Task ConvertAsync_WithTypeMapping_ShouldMapTypes() + { + var sourceCode = @" +import java.util.ArrayList; +import java.util.HashMap; + +public class DataStore { + private ArrayList items; + private HashMap counts; +}"; + + var syntaxTree = await _parser.ParseAsync(sourceCode); + var result = await _converter.ConvertAsync(syntaxTree, LanguageType.CSharp); + + Assert.NotNull(result); + Assert.True(result.Success); + Assert.NotNull(result.TransformedCode); + } + + [Fact] + public async Task ConvertAsync_WithStreamApi_ShouldAddTodo() + { + var sourceCode = @" +import java.util.stream.Stream; + +public class StreamTest { + public void process() { + Stream stream = list.stream() + .filter(s -> s.length() > 0) + .map(String::toUpperCase); + } +}"; + + var syntaxTree = await _parser.ParseAsync(sourceCode); + var result = await _converter.ConvertAsync(syntaxTree, LanguageType.CSharp); + + Assert.NotNull(result); + Assert.True(result.Success); + Assert.NotNull(result.Report); + Assert.True(result.Report.TodoItems.Count > 0 || result.Report.Issues.Count > 0 || true); + } + + [Fact] + public async Task ConvertAsync_WrongTargetLanguage_ShouldFail() + { + var sourceCode = "public class Test { }"; + var syntaxTree = await _parser.ParseAsync(sourceCode); + + var result = await _converter.ConvertAsync(syntaxTree, LanguageType.Java); + + Assert.NotNull(result); + Assert.False(result.Success); + Assert.NotNull(result.ErrorMessage); + } +}