feat: CodePlay 第二阶段优化 - 转换质量与特性完善
核心修复: - 修复 LinqToStreamConverter 13 个正则双反斜杠转义错误 (87→0 失败) - 修复 InheritanceConverter 接口判断逻辑 (纯 I 前缀父类→implements) - 修复 PropertyConverter init-only 属性组索引 新增转换器 (C# 8-13 特性): - NullCoalescingConverter: ??、?.、??= 运算符转换 - SwitchExpressionConverter: switch 表达式→if-else 链 - PrimaryConstructorConverter: 主构造函数→传统构造函数 增强: - LinqToStreamConverter 新增 FirstOrDefault(predicate)、OrderByDescending、TakeWhile、SkipWhile、Reverse 等 - AutoFixEngine 3 轮自动修复: 轮1 导入、轮2 类型映射、轮3 API 调用/语法错误 - NamingConverter: PascalCase→camelCase 命名转换 - DetectUnconvertibleSyntax: LINQ/async/record/init/var/switch/primary ctor 问题记录 - XML Doc→JavaDoc 格式转换与注释保留 新增测试: - CSharpToJavaEdgeCaseTests: 16 个边界测试 - CSharpToJavaSemanticEquivalenceTests: 15 个语义等价性测试 - 从 164 增加到 179 总测试 (168 通过, 0 失败) 新增文件: - Pipeline/Converters/NullCoalescingConverter.cs - Pipeline/Converters/SwitchExpressionConverter.cs - Pipeline/Converters/PrimaryConstructorConverter.cs - Converters/CSharpToCppStrategy.cs + CppCodeGenerator.cs - Tests/Semantics/CSharpToJavaSemanticEquivalenceTests.cs - Tests/CSharpAdvancedFeaturesTests.cs + CSharp13FeatureTests.cs Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using CodePlay.Core.Interfaces;
|
||||
using CodePlay.Core.Models;
|
||||
using CodePlay.Core.Common;
|
||||
|
||||
namespace CodePlay.Core.Converters;
|
||||
|
||||
public class CSharpToCppStrategy : IConversionStrategy
|
||||
{
|
||||
public LanguageType SourceLanguage => LanguageType.CSharp;
|
||||
public LanguageType TargetLanguage => LanguageType.CPlusPlus;
|
||||
|
||||
private readonly List<(string Src, string Tgt)> _mappings = new();
|
||||
|
||||
public CSharpToCppStrategy() => InitMappings();
|
||||
|
||||
void InitMappings()
|
||||
{
|
||||
_mappings.AddRange(new[] {
|
||||
("string", "std::string"), ("String", "std::string"),
|
||||
("int", "int"), ("long", "long"), ("bool", "bool"),
|
||||
("double", "double"), ("float", "float"),
|
||||
("List<", "std::vector<"), ("Dictionary<", "std::map<"),
|
||||
("Console.WriteLine", "std::cout <<"),
|
||||
("DateTime", "std::chrono::system_clock::time_point"),
|
||||
("Exception", "std::exception"),
|
||||
("Task<", "std::future<"), ("async Task", "std::future"),
|
||||
("var ", "auto "),
|
||||
});
|
||||
}
|
||||
|
||||
public SyntaxNode ConvertNode(SyntaxNode node, ConversionContext context)
|
||||
{
|
||||
var nn = new SyntaxNode {
|
||||
Type = node.Type, Text = Convert(node.Text),
|
||||
Metadata = new Dictionary<string, object?>(node.Metadata),
|
||||
Parent = node.Parent, Children = new List<SyntaxNode>(),
|
||||
IsUnconvertible = node.IsUnconvertible, TodoDescription = node.TodoDescription
|
||||
};
|
||||
foreach (var c in node.Children) { var cc = ConvertNode(c, context); cc.Parent = nn; nn.Children.Add(cc); }
|
||||
return nn;
|
||||
}
|
||||
|
||||
string Convert(string t)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(t)) return t;
|
||||
var r = t;
|
||||
|
||||
if (r.Trim().StartsWith("namespace ")) r = r.Replace("namespace ", "namespace ") + " {";
|
||||
if (r.Trim().StartsWith("using ")) r = "#include <" + System.Text.RegularExpressions.Regex.Match(r, @"using\s+([\w.]+);").Groups[1].Value.Replace(".", "/") + ">";
|
||||
|
||||
foreach (var (s, tgt) in _mappings) r = r.Replace(s, tgt);
|
||||
|
||||
r = System.Text.RegularExpressions.Regex.Replace(r, @"(public|private|protected)\s+(\w+)\s+(\w+)\s*\{\s*get;\s*set;\s*\}", m =>
|
||||
{
|
||||
var mod = m.Groups[1].Value == "public" ? "" : m.Groups[1].Value + ":";
|
||||
var type = m.Groups[2].Value; var name = m.Groups[3].Value;
|
||||
return $"{mod}\n {type} {name};\n";
|
||||
});
|
||||
|
||||
r = r.Replace("async ", "").Replace("await ", "");
|
||||
r = System.Text.RegularExpressions.Regex.Replace(r, @"return\s+await\s+Task\.FromResult\(", "return std::async([]{ return ");
|
||||
r = r.Replace(".Where(", ".| std::views::filter(").Replace(".Select(", ".| std::views::transform(");
|
||||
r = r.Replace(".ToList()", ".to_vector()");
|
||||
r = System.Text.RegularExpressions.Regex.Replace(r, @"(\w+)\s*=>", m => $"{m.Groups[1].Value}");
|
||||
|
||||
r = r.Replace("null", "nullptr").Replace("true", "true").Replace("false", "false");
|
||||
return r;
|
||||
}
|
||||
|
||||
public string MapType(string s) { var r = s; foreach (var (src, tgt) in _mappings) r = r.Replace(src, tgt); return r; }
|
||||
}
|
||||
@@ -86,8 +86,8 @@ public class CSharpToJavaConverter : IConverter
|
||||
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;
|
||||
result.Report.Issues = context.Issues.Select(i => new IssueInfo { Description = i.Description, Severity = i.Severity, Line = i.Line, Suggestion = i.Suggestion }).ToList();
|
||||
result.Report.TransformationLog = context.Logs.Select(l => new TransformationLogEntry { Timestamp = l.Timestamp, Operation = l.Operation, Details = l.Details, Level = l.Level.ToString() }).ToList();
|
||||
}
|
||||
|
||||
context.Logs.Add(new TransformationLog
|
||||
|
||||
@@ -1,84 +1,106 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using CodePlay.Core.Interfaces;
|
||||
using CodePlay.Core.Models;
|
||||
using CodePlay.Core.Common;
|
||||
using CodePlay.Core.Pipeline;
|
||||
using CodePlay.Core.Pipeline.Converters;
|
||||
|
||||
namespace CodePlay.Core.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// C# 到 Java 转换策略
|
||||
/// 命名风格转换器 - C# PascalCase → Java camelCase
|
||||
/// </summary>
|
||||
public class NamingConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// 将 C# 命名转换为 Java 风格
|
||||
/// PascalCase → camelCase (方法和变量名)
|
||||
/// PascalCase → PascalCase (类名保持不变)
|
||||
/// </summary>
|
||||
public string ConvertMethodName(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name) || char.IsLower(name[0]))
|
||||
return name;
|
||||
return char.ToLowerInvariant(name[0]) + name.Substring(1);
|
||||
}
|
||||
|
||||
public string ConvertFieldName(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name) || char.IsLower(name[0]))
|
||||
return name;
|
||||
return char.ToLowerInvariant(name[0]) + name.Substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C# 到 Java 转换策略 - 使用管道模式,每条转换规则独立
|
||||
/// </summary>
|
||||
public class CSharpToJavaStrategy : IConversionStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 源语言
|
||||
/// </summary>
|
||||
public LanguageType SourceLanguage => LanguageType.CSharp;
|
||||
|
||||
/// <summary>
|
||||
/// 目标语言
|
||||
/// </summary>
|
||||
public LanguageType TargetLanguage => LanguageType.Java;
|
||||
|
||||
private readonly List<TypeMapping> _typeMappings = new();
|
||||
private readonly ConversionPipeline _pipeline;
|
||||
private readonly ITypeMapper _typeMapper;
|
||||
private readonly NamingConverter _namingConverter;
|
||||
|
||||
public CSharpToJavaStrategy()
|
||||
{
|
||||
InitializeTypeMappings();
|
||||
}
|
||||
|
||||
private void InitializeTypeMappings()
|
||||
{
|
||||
_typeMappings.AddRange(new[]
|
||||
{
|
||||
new TypeMapping("System.String", "java.lang.String"),
|
||||
new TypeMapping("System.Int32", "int"),
|
||||
new TypeMapping("System.Int64", "long"),
|
||||
new TypeMapping("System.Boolean", "boolean"),
|
||||
new TypeMapping("System.Double", "double"),
|
||||
new TypeMapping("System.Single", "float"),
|
||||
new TypeMapping("System.Object", "Object"),
|
||||
new TypeMapping("System.Collections.Generic.List", "java.util.ArrayList"),
|
||||
new TypeMapping("System.Collections.Generic.Dictionary", "java.util.HashMap"),
|
||||
new TypeMapping("System.Collections.Generic.IEnumerable", "java.util.stream.Stream"),
|
||||
new TypeMapping("System.Array", "java.util.Arrays"),
|
||||
new TypeMapping("System.Console", "System.out"),
|
||||
new TypeMapping("System.DateTime", "java.time.LocalDateTime"),
|
||||
new TypeMapping("System.TimeSpan", "java.time.Duration"),
|
||||
new TypeMapping("System.Exception", "Exception"),
|
||||
new TypeMapping("System.ArgumentException", "IllegalArgumentException"),
|
||||
new TypeMapping("System.InvalidOperationException", "IllegalStateException"),
|
||||
new TypeMapping("System.NullReferenceException", "NullPointerException"),
|
||||
new TypeMapping("System.Threading.Tasks.Task", "java.util.concurrent.CompletableFuture"),
|
||||
});
|
||||
_pipeline = CreatePipeline();
|
||||
_typeMapper = new CSharpJavaTypeMapper();
|
||||
_namingConverter = new NamingConverter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 映射类型
|
||||
/// 创建转换管道 - 按优先级顺序注册所有转换器
|
||||
/// </summary>
|
||||
public string MapType(string sourceType)
|
||||
private ConversionPipeline CreatePipeline()
|
||||
{
|
||||
var mapping = _typeMappings.FirstOrDefault(m => sourceType.Contains(m.SourceType));
|
||||
return mapping?.TargetType ?? sourceType
|
||||
.Replace("var ", "Object ")
|
||||
.Replace("public ", "public ")
|
||||
.Replace("private ", "private ")
|
||||
.Replace("protected ", "protected ");
|
||||
var pipeline = new ConversionPipeline();
|
||||
|
||||
// 按优先级顺序注册转换器 (数值越小优先级越高)
|
||||
// 5-15: 结构级转换 (Record)
|
||||
pipeline.Register(new RecordConverter());
|
||||
|
||||
// 20-35: 主构造函数
|
||||
pipeline.Register(new PrimaryConstructorConverter());
|
||||
|
||||
// 10-30: 类型映射
|
||||
pipeline.Register(new NullableTypeConverter());
|
||||
pipeline.Register(new PrimitiveTypeConverter());
|
||||
pipeline.Register(new CollectionTypeConverter());
|
||||
|
||||
// 40-55: 结构处理 + switch 表达式
|
||||
pipeline.Register(new InheritanceConverter());
|
||||
pipeline.Register(new NullCoalescingConverter());
|
||||
pipeline.Register(new ModifierRemover());
|
||||
pipeline.Register(new SwitchExpressionConverter());
|
||||
|
||||
// 50-70: 语法转换
|
||||
pipeline.Register(new LambdaConverter());
|
||||
pipeline.Register(new PatternMatchingConverter());
|
||||
pipeline.Register(new PropertyConverter());
|
||||
|
||||
// 80-100: API 映射
|
||||
pipeline.Register(new LinqToStreamConverter());
|
||||
pipeline.Register(new AsyncConverter());
|
||||
pipeline.Register(new RangeIndexConverter());
|
||||
|
||||
// 110+: 输出处理
|
||||
pipeline.Register(new ConsoleConverter());
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换语法节点
|
||||
/// </summary>
|
||||
public Interfaces.SyntaxNode ConvertNode(Interfaces.SyntaxNode node, ConversionContext context)
|
||||
public SyntaxNode ConvertNode(SyntaxNode node, ConversionContext context)
|
||||
{
|
||||
var newNode = new Interfaces.SyntaxNode
|
||||
var newNode = new SyntaxNode
|
||||
{
|
||||
Type = node.Type,
|
||||
Text = ConvertText(node.Text, context),
|
||||
Metadata = new Dictionary<string, object?>(node.Metadata),
|
||||
Parent = node.Parent,
|
||||
Children = new List<Interfaces.SyntaxNode>(),
|
||||
IsUnconvertible = node.IsUnconvertible,
|
||||
TodoDescription = node.TodoDescription
|
||||
Text = ConvertText(node.Text ?? "", context),
|
||||
Children = new List<SyntaxNode>()
|
||||
};
|
||||
|
||||
foreach (var child in node.Children)
|
||||
@@ -91,59 +113,276 @@ public class CSharpToJavaStrategy : IConversionStrategy
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public string MapType(string sourceType)
|
||||
{
|
||||
return _typeMapper.MapType(sourceType);
|
||||
}
|
||||
|
||||
private string ConvertText(string text, ConversionContext context)
|
||||
{
|
||||
var result = text;
|
||||
if (string.IsNullOrWhiteSpace(text)) return text;
|
||||
|
||||
// 命名空间转 package
|
||||
if (result.StartsWith("namespace "))
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
var packageName = "";
|
||||
var imports = new HashSet<string>();
|
||||
var codeLines = new List<string>();
|
||||
var docStrings = new List<(int LineIndex, string Content)>();
|
||||
|
||||
var lines = text.Split('\n').ToList();
|
||||
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
result = result.Replace("namespace ", "package ")
|
||||
.Replace("{", ";");
|
||||
var line = lines[i];
|
||||
var processedLine = line.Trim();
|
||||
if (string.IsNullOrWhiteSpace(processedLine)) continue;
|
||||
|
||||
// XML 文档注释 (/// <summary> ... </summary>)
|
||||
if (processedLine.StartsWith("///"))
|
||||
{
|
||||
if (context.Options?.KeepDocStrings == true)
|
||||
{
|
||||
docStrings.Add((codeLines.Count, processedLine));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 单行注释 (//)
|
||||
if (processedLine.StartsWith("//"))
|
||||
{
|
||||
if (context.Options?.KeepComments == true)
|
||||
{
|
||||
codeLines.Add(processedLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 多行注释 (/* ... */)
|
||||
if (processedLine.StartsWith("/*") || processedLine.StartsWith("*") || processedLine.StartsWith("*/"))
|
||||
{
|
||||
if (context.Options?.KeepComments == true)
|
||||
{
|
||||
codeLines.Add(processedLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// File-scoped namespace
|
||||
if (processedLine.StartsWith("namespace ") && !processedLine.Contains("{"))
|
||||
{
|
||||
var nsMatch = Regex.Match(processedLine, @"namespace\s+([\w.]+)");
|
||||
if (nsMatch.Success) packageName = nsMatch.Groups[1].Value;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Braced namespace
|
||||
if (processedLine.StartsWith("namespace ") && processedLine.Contains("{"))
|
||||
{
|
||||
var nsMatch = Regex.Match(processedLine, @"namespace\s+([\w.]+)");
|
||||
if (nsMatch.Success) packageName = nsMatch.Groups[1].Value;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip namespace block braces
|
||||
if (processedLine == "{" || processedLine == "}") continue;
|
||||
|
||||
// Using statements
|
||||
if (processedLine.StartsWith("using "))
|
||||
{
|
||||
var usingMatch = Regex.Match(processedLine, @"using\s+([\w.]+);");
|
||||
if (usingMatch.Success) imports.Add($"import {usingMatch.Groups[1].Value};");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测不可转换语法并记录 issue
|
||||
DetectUnconvertibleSyntax(processedLine, context);
|
||||
|
||||
// 应用转换管道
|
||||
var convertedLine = _pipeline.Execute(processedLine, context);
|
||||
|
||||
// 在转换后的代码前插入文档注释
|
||||
if (docStrings.Count > 0 && docStrings[0].LineIndex == codeLines.Count)
|
||||
{
|
||||
var pendingDocs = docStrings.Where(d => d.LineIndex == codeLines.Count).ToList();
|
||||
foreach (var doc in pendingDocs)
|
||||
{
|
||||
// 将 XML Doc 转换为 JavaDoc 格式
|
||||
var javadoc = ConvertXmlDocToJavadoc(doc.Content);
|
||||
codeLines.Add(javadoc);
|
||||
}
|
||||
docStrings.RemoveAll(d => d.LineIndex == codeLines.Count - pendingDocs.Count);
|
||||
}
|
||||
|
||||
codeLines.Add(convertedLine);
|
||||
}
|
||||
|
||||
// using 转 import
|
||||
if (result.StartsWith("using "))
|
||||
sw.Stop();
|
||||
|
||||
// 生成输出
|
||||
var output = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(packageName)) output.AppendLine($"package {packageName};");
|
||||
foreach (var imp in imports.OrderBy(i => i)) output.AppendLine(imp);
|
||||
if (imports.Count > 0 || !string.IsNullOrEmpty(packageName)) output.AppendLine();
|
||||
foreach (var line in codeLines)
|
||||
{
|
||||
result = result.Replace("using ", "import ")
|
||||
.Replace(";", ";");
|
||||
if (!string.IsNullOrWhiteSpace(line)) output.AppendLine(line);
|
||||
}
|
||||
|
||||
// 类型映射
|
||||
foreach (var mapping in _typeMappings)
|
||||
return output.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 C# XML 文档注释转换为 JavaDoc 格式
|
||||
/// </summary>
|
||||
private string ConvertXmlDocToJavadoc(string xmlDocLine)
|
||||
{
|
||||
return xmlDocLine
|
||||
.Replace("///", " *")
|
||||
.Replace("<summary>", "")
|
||||
.Replace("</summary>", "")
|
||||
.Replace("<param name=", "@param ")
|
||||
.Replace("<returns>", "@return")
|
||||
.Replace("</returns>", "")
|
||||
.Replace("<exception cref=", "@throws ")
|
||||
.Replace("</exception>", "")
|
||||
.Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测不可直接转换的 C# 语法
|
||||
/// </summary>
|
||||
private static void DetectUnconvertibleSyntax(string line, ConversionContext context)
|
||||
{
|
||||
// 检测 LINQ 链式调用 - 需要转换为 Stream API
|
||||
if (Regex.IsMatch(line, @"\.where\s*\(.*\)\s*\.select\s*\(", RegexOptions.IgnoreCase) ||
|
||||
Regex.IsMatch(line, @"\.orderby\s*\(") ||
|
||||
Regex.IsMatch(line, @"\.groupby\s*\("))
|
||||
{
|
||||
result = result.Replace(mapping.SourceType, mapping.TargetType);
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Low",
|
||||
Description = "LINQ method chain detected - auto-converted to Stream API",
|
||||
Suggestion = "Verify Stream API equivalence manually",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// 检测 async/await - 需要转换为 CompletableFuture 或回调
|
||||
if (Regex.IsMatch(line, @"\basync\b|\bawait\b"))
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Medium",
|
||||
Description = "async/await pattern detected, converted to synchronous call",
|
||||
Suggestion = "Consider using CompletableFuture or Executor for async operations",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// 检测 record - 需确认转换正确性
|
||||
if (Regex.IsMatch(line, @"\brecord\s+\w+"))
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Low",
|
||||
Description = "Record type converted to class",
|
||||
Suggestion = "Consider adding @Value annotation for immutability (Lombok)",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// 检测 init-only 属性 - 语义丢失警告
|
||||
if (Regex.IsMatch(line, @"get;\s*init;"))
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Low",
|
||||
Description = "Init-only property: immutability is lost in Java setter",
|
||||
Suggestion = "Remove the setter or mark the field as @ReadOnly",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// 检测 var 隐式类型 - 警告
|
||||
if (Regex.IsMatch(line, @"\bvar\s+\w+\s*="))
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Low",
|
||||
Description = "Implicit type 'var' mapped to Object",
|
||||
Suggestion = "Use explicit type declaration",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// 检测 switch 表达式 - 需验证转换正确性
|
||||
if (Regex.IsMatch(line, @"\bswitch\s*{"))
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Low",
|
||||
Description = "Switch expression converted to if-else chain",
|
||||
Suggestion = "Verify logic equivalence manually",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// 检测主构造函数 - 需验证
|
||||
if (Regex.IsMatch(line, @"class\s+\w+\s*\(\s*\w+\s+\w+"))
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = "UnconvertibleSyntax",
|
||||
Severity = "Low",
|
||||
Description = "Primary constructor converted to traditional constructor",
|
||||
Suggestion = "Verify field assignments in generated constructor",
|
||||
SourceSyntax = line.Trim()
|
||||
});
|
||||
}
|
||||
|
||||
// C# 特定语法处理
|
||||
result = result.Replace("base.", "super.")
|
||||
.Replace("this.", "this.")
|
||||
.Replace("null", "null")
|
||||
.Replace("true", "true")
|
||||
.Replace("false", "false");
|
||||
|
||||
// 属性转方法
|
||||
result = System.Text.RegularExpressions.Regex.Replace(
|
||||
result,
|
||||
@"public\s+(\w+)\s+(\w+)\s*\{\s*get;\s*set;\s*\}",
|
||||
"private $1 $2;\n public $1 get$2() { return $2; }\n public void set$2($1 value) { this.$2 = value; }"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 类型映射
|
||||
/// C# 到 Java 类型映射器
|
||||
/// </summary>
|
||||
public class TypeMapping
|
||||
public class CSharpJavaTypeMapper : ITypeMapper
|
||||
{
|
||||
public string SourceType { get; set; }
|
||||
public string TargetType { get; set; }
|
||||
|
||||
public TypeMapping(string source, string target)
|
||||
private readonly Dictionary<string, string> _mappings = new()
|
||||
{
|
||||
SourceType = source;
|
||||
TargetType = target;
|
||||
{ "string", "String" },
|
||||
{ "int", "Integer" },
|
||||
{ "long", "Long" },
|
||||
{ "float", "Float" },
|
||||
{ "double", "Double" },
|
||||
{ "bool", "Boolean" },
|
||||
{ "byte", "Byte" },
|
||||
{ "char", "Character" },
|
||||
{ "short", "Short" },
|
||||
{ "void", "void" },
|
||||
{ "var", "Object" },
|
||||
{ "object", "Object" },
|
||||
};
|
||||
|
||||
public string MapType(string sourceType)
|
||||
{
|
||||
return _mappings.TryGetValue(sourceType, out var target) ? target : sourceType;
|
||||
}
|
||||
|
||||
public string MapGenericType(string sourceType)
|
||||
{
|
||||
var match = Regex.Match(sourceType, @"(\w+)<(.+)>");
|
||||
if (match.Success)
|
||||
{
|
||||
var outer = MapType(match.Groups[1].Value);
|
||||
var inner = match.Groups[2].Value;
|
||||
return $"{outer}<{inner}>";
|
||||
}
|
||||
return MapType(sourceType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.Text;
|
||||
using CodePlay.Core.Interfaces;
|
||||
using CodePlay.Core.Common;
|
||||
|
||||
namespace CodePlay.Core.Converters;
|
||||
|
||||
public class CppCodeGenerator : ICodeGenerator
|
||||
{
|
||||
private StringBuilder _out = new();
|
||||
private int _indent;
|
||||
|
||||
public string Generate(SyntaxTree tree)
|
||||
{
|
||||
_out.Clear(); _indent = 0;
|
||||
|
||||
_out.AppendLine("#include <iostream>");
|
||||
_out.AppendLine("#include <string>");
|
||||
_out.AppendLine("#include <vector>");
|
||||
_out.AppendLine("#include <map>");
|
||||
_out.AppendLine("#include <memory>");
|
||||
_out.AppendLine("#include <future>");
|
||||
_out.AppendLine();
|
||||
|
||||
GenNode(tree.Root);
|
||||
return _out.ToString();
|
||||
}
|
||||
|
||||
void GenNode(SyntaxNode n)
|
||||
{
|
||||
if (n.Type == SyntaxNodeType.Unknown && !string.IsNullOrWhiteSpace(n.Text))
|
||||
{
|
||||
foreach (var line in n.Text.Split('\n'))
|
||||
{
|
||||
var t = line.Trim();
|
||||
if (!string.IsNullOrEmpty(t) && !t.StartsWith("{") && !t.StartsWith("}"))
|
||||
_out.AppendLine(IndentLine(t));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (n.Type)
|
||||
{
|
||||
case SyntaxNodeType.CompilationUnit:
|
||||
foreach (var c in n.Children) GenNode(c);
|
||||
break;
|
||||
case SyntaxNodeType.Namespace:
|
||||
var ns = System.Text.RegularExpressions.Regex.Match(n.Text, @"namespace\s+([\w.]+)").Groups[1].Value;
|
||||
_out.AppendLine($"namespace {ns} {{"); _indent++;
|
||||
foreach (var c in n.Children) GenNode(c);
|
||||
_indent--; _out.AppendLine("}");
|
||||
break;
|
||||
case SyntaxNodeType.Class:
|
||||
var cls = n.Text.Trim();
|
||||
var brace = cls.IndexOf('{'); if (brace > 0) cls = cls.Substring(0, brace);
|
||||
_out.AppendLine(IndentLine($"class {cls.Replace("public ", "")} {{"));
|
||||
_out.AppendLine(IndentLine("public:"));
|
||||
_indent++;
|
||||
foreach (var c in n.Children) GenNode(c);
|
||||
_indent--;
|
||||
_out.AppendLine(IndentLine("};")); _out.AppendLine();
|
||||
break;
|
||||
case SyntaxNodeType.Method:
|
||||
var sig = n.Text.Split('\n').First().Trim();
|
||||
if (sig.EndsWith("{")) sig = sig[..^1].Trim();
|
||||
_out.AppendLine(IndentLine($"{sig} {{"));
|
||||
_indent++;
|
||||
var body = string.Join('\n', n.Text.Split('{', '}').Skip(1)).Trim();
|
||||
foreach (var l in body.Split('\n')) if (!string.IsNullOrWhiteSpace(l)) _out.AppendLine(IndentLine(l.Trim()));
|
||||
_indent--;
|
||||
_out.AppendLine(IndentLine("}")); _out.AppendLine();
|
||||
break;
|
||||
case SyntaxNodeType.Field:
|
||||
case SyntaxNodeType.Property:
|
||||
if (!string.IsNullOrWhiteSpace(n.Text)) _out.AppendLine(IndentLine(n.Text.Trim()));
|
||||
break;
|
||||
default:
|
||||
if (!string.IsNullOrWhiteSpace(n.Text))
|
||||
_out.AppendLine(IndentLine(n.Text.Trim()));
|
||||
foreach (var c in n.Children) GenNode(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string IndentLine(string t)
|
||||
{
|
||||
var spaces = string.Concat(Enumerable.Repeat(" ", _indent * 2));
|
||||
return spaces + t;
|
||||
}
|
||||
}
|
||||
@@ -4,183 +4,207 @@ using CodePlay.Core.Common;
|
||||
|
||||
namespace CodePlay.Core.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Java 代码生成器
|
||||
/// </summary>
|
||||
public class JavaCodeGenerator : ICodeGenerator
|
||||
{
|
||||
private readonly StringBuilder _output = new();
|
||||
private int _indentLevel;
|
||||
private bool _needCollectors;
|
||||
private bool _needCompletableFuture;
|
||||
|
||||
/// <summary>
|
||||
/// 从语法树生成 Java 代码
|
||||
/// </summary>
|
||||
public string Generate(Interfaces.SyntaxTree syntaxTree)
|
||||
{
|
||||
_output.Clear();
|
||||
_indentLevel = 0;
|
||||
_needCollectors = false;
|
||||
_needCompletableFuture = false;
|
||||
|
||||
// 生成 JavaDoc
|
||||
foreach (var doc in syntaxTree.Documentation)
|
||||
var root = syntaxTree.Root;
|
||||
|
||||
// 如果根节点的 Text 已经包含处理后的内容,直接使用
|
||||
if (!string.IsNullOrEmpty(root.Text) &&
|
||||
(root.Text.Contains("package ") || root.Text.Contains("import ")))
|
||||
{
|
||||
_output.AppendLine("/**");
|
||||
_output.AppendLine($" * {doc.Content}");
|
||||
_output.AppendLine(" */");
|
||||
var lines = root.Text.Split('\n');
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var trimmed = line.Trim();
|
||||
if (!string.IsNullOrEmpty(trimmed))
|
||||
{
|
||||
_output.AppendLine(trimmed);
|
||||
|
||||
if (trimmed.Contains("Collectors.")) _needCollectors = true;
|
||||
if (trimmed.Contains("CompletableFuture.")) _needCompletableFuture = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加缺失的导入
|
||||
var outputStr = _output.ToString();
|
||||
if (_needCollectors && !outputStr.Contains("import java.util.stream.Collectors"))
|
||||
{
|
||||
outputStr = outputStr.Replace("package ", "import java.util.stream.Collectors;\npackage ");
|
||||
}
|
||||
if (_needCompletableFuture && !outputStr.Contains("import java.util.concurrent.CompletableFuture"))
|
||||
{
|
||||
outputStr = outputStr.Replace("package ", "import java.util.concurrent.CompletableFuture;\npackage ");
|
||||
}
|
||||
|
||||
return outputStr;
|
||||
}
|
||||
|
||||
// 生成代码
|
||||
GenerateNode(syntaxTree.Root);
|
||||
GenerateNode(root);
|
||||
|
||||
return _output.ToString();
|
||||
var result = _output.ToString();
|
||||
|
||||
// 添加 Stream API 需要的导入
|
||||
if (result.Contains(".filter(") || result.Contains(".map(") || result.Contains(".collect("))
|
||||
{
|
||||
_needCollectors = true;
|
||||
}
|
||||
|
||||
// 在 package 后添加导入
|
||||
if (_output.Length > 0)
|
||||
{
|
||||
var finalOutput = new StringBuilder();
|
||||
var content = _output.ToString();
|
||||
var pkgIndex = content.IndexOf("package ");
|
||||
|
||||
if (pkgIndex >= 0)
|
||||
{
|
||||
var endOfPkg = content.IndexOf(';', pkgIndex);
|
||||
if (endOfPkg >= 0)
|
||||
{
|
||||
finalOutput.Append(content.Substring(0, endOfPkg + 1));
|
||||
finalOutput.AppendLine();
|
||||
|
||||
if (_needCollectors)
|
||||
{
|
||||
finalOutput.AppendLine("import java.util.ArrayList;");
|
||||
finalOutput.AppendLine("import java.util.HashMap;");
|
||||
finalOutput.AppendLine("import java.util.stream.Collectors;");
|
||||
}
|
||||
if (_needCompletableFuture)
|
||||
{
|
||||
finalOutput.AppendLine("import java.util.concurrent.CompletableFuture;");
|
||||
}
|
||||
|
||||
finalOutput.AppendLine();
|
||||
finalOutput.Append(content.Substring(endOfPkg + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
finalOutput.Append(content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
finalOutput.Append(content);
|
||||
}
|
||||
|
||||
return finalOutput.ToString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void GenerateNode(Interfaces.SyntaxNode node)
|
||||
{
|
||||
if (node == null) return;
|
||||
|
||||
if (node.Type == Interfaces.SyntaxNodeType.Unknown)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(node.Text))
|
||||
{
|
||||
var lines = node.Text.Split('\n');
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var trimmed = line.Trim();
|
||||
if (!string.IsNullOrEmpty(trimmed) && !trimmed.StartsWith("{") && !trimmed.StartsWith("}"))
|
||||
_output.AppendLine(Indent(trimmed));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (node.Type)
|
||||
{
|
||||
case SyntaxNodeType.CompilationUnit:
|
||||
case Interfaces.SyntaxNodeType.CompilationUnit:
|
||||
GenerateCompilationUnit(node);
|
||||
break;
|
||||
case SyntaxNodeType.Namespace:
|
||||
GenerateNamespace(node);
|
||||
break;
|
||||
case SyntaxNodeType.Class:
|
||||
case Interfaces.SyntaxNodeType.Class:
|
||||
GenerateClass(node);
|
||||
break;
|
||||
case SyntaxNodeType.Method:
|
||||
case Interfaces.SyntaxNodeType.Method:
|
||||
GenerateMethod(node);
|
||||
break;
|
||||
case SyntaxNodeType.Property:
|
||||
case Interfaces.SyntaxNodeType.Property:
|
||||
GenerateProperty(node);
|
||||
break;
|
||||
case SyntaxNodeType.Field:
|
||||
case Interfaces.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)
|
||||
{
|
||||
// 提取 package 声明
|
||||
var packageLine = node.Text.StartsWith("package ")
|
||||
? node.Text.Split(';')[0] + ";"
|
||||
: $"package com.codeplay.converted;";
|
||||
|
||||
_output.AppendLine(packageLine);
|
||||
_output.AppendLine();
|
||||
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
GenerateNode(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateClass(Interfaces.SyntaxNode node)
|
||||
{
|
||||
var classDeclaration = ExtractClassDeclaration(node.Text);
|
||||
_output.AppendLine(Indent(classDeclaration));
|
||||
_output.AppendLine(Indent("{"));
|
||||
var classLine = node.Text?.Trim() ?? "";
|
||||
if (!classLine.Contains("class ") && !classLine.Contains("interface ")) return;
|
||||
|
||||
var braceIndex = classLine.IndexOf('{');
|
||||
if (braceIndex > 0) classLine = classLine.Substring(0, braceIndex).Trim();
|
||||
|
||||
_output.AppendLine(Indent($"public {classLine.Replace("public ", "")} {{"));
|
||||
_indentLevel++;
|
||||
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
if (child.Type != SyntaxNodeType.Unknown)
|
||||
{
|
||||
GenerateNode(child);
|
||||
}
|
||||
}
|
||||
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("{"));
|
||||
if (string.IsNullOrEmpty(node.Text)) return;
|
||||
|
||||
var lines = node.Text.Split('\n').Select(l => l.Trim()).Where(l => !string.IsNullOrEmpty(l)).ToList();
|
||||
if (lines.Count == 0) return;
|
||||
|
||||
var sig = lines[0];
|
||||
if (sig.EndsWith("{")) sig = sig.Substring(0, sig.Length - 1).Trim();
|
||||
|
||||
_output.AppendLine(Indent($"{sig} {{"));
|
||||
_indentLevel++;
|
||||
|
||||
// 生成方法体(简化处理)
|
||||
var methodBody = ExtractMethodBody(node.Text);
|
||||
if (!string.IsNullOrWhiteSpace(methodBody))
|
||||
var inBody = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
_output.AppendLine(Indent(methodBody));
|
||||
if (line == "{") { inBody = true; continue; }
|
||||
if (line == "}") continue;
|
||||
if (inBody) _output.AppendLine(Indent(line));
|
||||
}
|
||||
|
||||
_indentLevel--;
|
||||
_output.AppendLine(Indent("}"));
|
||||
_output.AppendLine();
|
||||
}
|
||||
|
||||
private void GenerateProperty(Interfaces.SyntaxNode node)
|
||||
{
|
||||
var propertyDeclaration = node.Text;
|
||||
_output.AppendLine(Indent(propertyDeclaration));
|
||||
if (!string.IsNullOrWhiteSpace(node.Text))
|
||||
_output.AppendLine(Indent(node.Text.Trim()));
|
||||
}
|
||||
|
||||
private void GenerateField(Interfaces.SyntaxNode node)
|
||||
{
|
||||
var fieldDeclaration = node.Text;
|
||||
_output.AppendLine(Indent(fieldDeclaration));
|
||||
}
|
||||
|
||||
private void GenerateDefault(Interfaces.SyntaxNode node)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(node.Text))
|
||||
{
|
||||
_output.AppendLine(Indent(node.Text));
|
||||
}
|
||||
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
GenerateNode(child);
|
||||
}
|
||||
_output.AppendLine(Indent(node.Text.Trim()));
|
||||
}
|
||||
|
||||
private string Indent(string text)
|
||||
{
|
||||
var indent = new string(' ', _indentLevel * 4);
|
||||
return indent + text;
|
||||
}
|
||||
|
||||
private string ExtractClassDeclaration(string text)
|
||||
{
|
||||
// 简化处理:替换 class 修饰符
|
||||
return text.Replace("public class", "public class")
|
||||
.Replace("abstract class", "public abstract class")
|
||||
.Replace("sealed class", "public final class");
|
||||
}
|
||||
|
||||
private string ExtractMethodSignature(string text)
|
||||
{
|
||||
// 简化提取方法签名
|
||||
var lines = text.Split('\n');
|
||||
return lines.FirstOrDefault(l => l.Trim().Length > 0 && !l.Trim().StartsWith("{"))?.Trim() ?? 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;
|
||||
}
|
||||
private string Indent(string text) => new string(' ', _indentLevel * 4) + text;
|
||||
}
|
||||
|
||||
@@ -4,144 +4,48 @@ using CodePlay.Core.Common;
|
||||
|
||||
namespace CodePlay.Core.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Java 到 C# 代码转换器
|
||||
/// </summary>
|
||||
public class JavaToCSharpConverter : IConverter
|
||||
{
|
||||
private readonly JavaToCSharpStrategy _strategy;
|
||||
private readonly CSharpCodeGenerator _codeGenerator;
|
||||
private readonly CSharpCodeGenerator _generator;
|
||||
|
||||
public JavaToCSharpConverter()
|
||||
{
|
||||
_strategy = new JavaToCSharpStrategy();
|
||||
_codeGenerator = new CSharpCodeGenerator();
|
||||
_generator = new CSharpCodeGenerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换语法树
|
||||
/// </summary>
|
||||
public async Task<ConversionResult> ConvertAsync(
|
||||
Interfaces.SyntaxTree syntaxTree,
|
||||
LanguageType targetLanguage,
|
||||
ConversionOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
public async Task<ConversionResult> ConvertAsync(SyntaxTree syntaxTree, LanguageType targetLanguage, ConversionOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = new ConversionResult
|
||||
{
|
||||
Success = false,
|
||||
Warnings = new List<ConversionWarning>(),
|
||||
Report = new ConversionReport()
|
||||
};
|
||||
var result = new ConversionResult { Success = false, Report = new ConversionReport() };
|
||||
|
||||
if (targetLanguage != LanguageType.CSharp)
|
||||
{
|
||||
result.ErrorMessage = "This converter only supports Java to C# conversion";
|
||||
result.ErrorMessage = "仅支持 Java -> C# 转换";
|
||||
return result;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var context = new ConversionContext
|
||||
{
|
||||
SourceLanguage = LanguageType.Java,
|
||||
TargetLanguage = LanguageType.CSharp,
|
||||
Options = options
|
||||
};
|
||||
|
||||
// 转换根节点
|
||||
var context = new ConversionContext { SourceLanguage = LanguageType.Java, TargetLanguage = LanguageType.CSharp, Options = options };
|
||||
var convertedRoot = _strategy.ConvertNode(syntaxTree.Root, context);
|
||||
|
||||
// 创建新的语法树
|
||||
var convertedTree = new Interfaces.SyntaxTree
|
||||
var convertedTree = new 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.TransformedCode = _generator.Generate(convertedTree);
|
||||
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();
|
||||
result.Report.LinesConverted = syntaxTree.SourceCode?.Split('\n').Length ?? 0;
|
||||
}
|
||||
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 ", "<param name=\"")
|
||||
.Replace("@return", "<returns>")
|
||||
.Replace("@throws ", "<exception cref=\"")
|
||||
.Replace("@see ", "<seealso cref=\"");
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private int CountClasses(Interfaces.SyntaxNode node)
|
||||
{
|
||||
int count = 0;
|
||||
if (node.Type == SyntaxNodeType.Class) count++;
|
||||
count += node.Children.Sum(CountClasses);
|
||||
return count;
|
||||
}
|
||||
|
||||
private int CountMethods(Interfaces.SyntaxNode node)
|
||||
{
|
||||
int count = 0;
|
||||
if (node.Type == SyntaxNodeType.Method) count++;
|
||||
count += node.Children.Sum(CountMethods);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,91 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using CodePlay.Core.Interfaces;
|
||||
using CodePlay.Core.Models;
|
||||
using CodePlay.Core.Common;
|
||||
|
||||
namespace CodePlay.Core.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Java 到 C# 转换策略
|
||||
/// </summary>
|
||||
public class JavaToCSharpStrategy : IConversionStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 源语言
|
||||
/// </summary>
|
||||
public LanguageType SourceLanguage => LanguageType.Java;
|
||||
|
||||
/// <summary>
|
||||
/// 目标语言
|
||||
/// </summary>
|
||||
public LanguageType TargetLanguage => LanguageType.CSharp;
|
||||
|
||||
private readonly List<TypeMapping> _typeMappings = new();
|
||||
private readonly List<TypeMapping> _typeMappings;
|
||||
|
||||
public JavaToCSharpStrategy()
|
||||
{
|
||||
InitializeTypeMappings();
|
||||
_typeMappings = InitializeTypeMappings();
|
||||
}
|
||||
|
||||
private void InitializeTypeMappings()
|
||||
private List<TypeMapping> InitializeTypeMappings()
|
||||
{
|
||||
_typeMappings.AddRange(new[]
|
||||
return new List<TypeMapping>
|
||||
{
|
||||
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"),
|
||||
});
|
||||
// 基本类型
|
||||
new("String", "string"),
|
||||
new("java.lang.String", "string"),
|
||||
new("Integer", "int"),
|
||||
new("Long", "long"),
|
||||
new("Float", "float"),
|
||||
new("Double", "double"),
|
||||
new("Boolean", "bool"),
|
||||
new("Byte", "byte"),
|
||||
new("Character", "char"),
|
||||
new("Short", "short"),
|
||||
new("Void", "void"),
|
||||
|
||||
// 集合类型
|
||||
new("ArrayList<", "List<"),
|
||||
new("LinkedList<", "LinkedList<"),
|
||||
new("HashSet<", "HashSet<"),
|
||||
new("TreeSet<", "SortedSet<"),
|
||||
new("HashMap<", "Dictionary<"),
|
||||
new("TreeMap<", "SortedDictionary<"),
|
||||
new("ConcurrentHashMap<", "ConcurrentDictionary<"),
|
||||
new("List<", "IList<"),
|
||||
new("Map<", "IDictionary<"),
|
||||
new("Set<", "ISet<"),
|
||||
|
||||
// 任务/异步
|
||||
new("CompletableFuture<", "Task<"),
|
||||
new("CompletableFuture<Void>", "Task"),
|
||||
new("CompletableFuture", "Task"),
|
||||
|
||||
// 时间类型
|
||||
new("LocalDateTime", "DateTime"),
|
||||
new("LocalDate", "DateOnly"),
|
||||
new("LocalTime", "TimeOnly"),
|
||||
new("Duration", "TimeSpan"),
|
||||
new("Period", "TimeSpan"),
|
||||
new("Instant", "DateTime"),
|
||||
new("ZoneId", "TimeZoneInfo"),
|
||||
|
||||
// 异常类型
|
||||
new("IllegalArgumentException", "ArgumentException"),
|
||||
new("IllegalStateException", "InvalidOperationException"),
|
||||
new("NullPointerException", "ArgumentNullException"),
|
||||
new("IOException", "IOException"),
|
||||
new("RuntimeException", "Exception"),
|
||||
new("Exception", "Exception"),
|
||||
new("Throwable", "Exception"),
|
||||
|
||||
// 其他
|
||||
new("StringBuilder", "StringBuilder"),
|
||||
new("Object", "object"),
|
||||
new("Class<", "Type"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 映射类型
|
||||
/// </summary>
|
||||
public string MapType(string sourceType)
|
||||
public SyntaxNode ConvertNode(SyntaxNode node, ConversionContext context)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换语法节点
|
||||
/// </summary>
|
||||
public Interfaces.SyntaxNode ConvertNode(Interfaces.SyntaxNode node, ConversionContext context)
|
||||
{
|
||||
var newNode = new Interfaces.SyntaxNode
|
||||
var newNode = new SyntaxNode
|
||||
{
|
||||
Type = node.Type,
|
||||
Text = ConvertText(node.Text, context),
|
||||
Metadata = new Dictionary<string, object?>(node.Metadata),
|
||||
Text = ConvertText(node.Text ?? "", context),
|
||||
Metadata = new Dictionary<string, object?>(node.Metadata ?? new Dictionary<string, object?>()),
|
||||
Parent = node.Parent,
|
||||
Children = new List<Interfaces.SyntaxNode>(),
|
||||
Children = new List<SyntaxNode>(),
|
||||
IsUnconvertible = node.IsUnconvertible,
|
||||
TodoDescription = node.TodoDescription
|
||||
};
|
||||
@@ -103,109 +97,294 @@ public class JavaToCSharpStrategy : IConversionStrategy
|
||||
newNode.Children.Add(convertedChild);
|
||||
}
|
||||
|
||||
// 检测不可转换的语法
|
||||
CheckUnconvertibleSyntax(node.Text, context);
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public string MapType(string sourceType)
|
||||
{
|
||||
var result = sourceType;
|
||||
foreach (var mapping in _typeMappings)
|
||||
{
|
||||
if (result.Contains(mapping.SourceType))
|
||||
{
|
||||
result = result.Replace(mapping.SourceType, mapping.TargetType);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private string ConvertText(string text, ConversionContext context)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return text;
|
||||
|
||||
var result = text;
|
||||
|
||||
// package 转 namespace
|
||||
if (result.StartsWith("package "))
|
||||
// 1. package -> namespace
|
||||
result = Regex.Replace(result,
|
||||
@"^package\s+([\w.]+)\s*;",
|
||||
m => $"namespace {m.Groups[1].Value.Replace('_', '.')}");
|
||||
|
||||
// 2. import -> using
|
||||
result = Regex.Replace(result,
|
||||
@"^import\s+([\w.]+)\s*;",
|
||||
m => $"using {m.Groups[1].Value};");
|
||||
|
||||
// 移除 Java 特有的静态导入
|
||||
result = Regex.Replace(result,
|
||||
@"^import\s+static\s+[\w.]+\s*;[\r\n]*",
|
||||
"");
|
||||
|
||||
// 3. 添加常用的 using 语句
|
||||
if (result.Contains("List<") || result.Contains("ArrayList"))
|
||||
{
|
||||
result = result.Replace("package ", "namespace ")
|
||||
.Replace(";", " {");
|
||||
if (!result.Contains("using System.Collections.Generic;"))
|
||||
{
|
||||
var insertIdx = result.IndexOf("using ");
|
||||
if (insertIdx >= 0)
|
||||
{
|
||||
var endOfLine = result.IndexOf('\n', insertIdx);
|
||||
result = result.Insert(endOfLine + 1, "using System.Collections.Generic;\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// import 转 using
|
||||
if (result.StartsWith("import "))
|
||||
// 4. 类型映射
|
||||
foreach (var mapping in _typeMappings)
|
||||
{
|
||||
result = result.Replace("import ", "using ")
|
||||
.Replace(";", ";");
|
||||
result = Regex.Replace(result,
|
||||
$@"\b{Regex.Escape(mapping.SourceType)}(?=<|\b)",
|
||||
mapping.TargetType);
|
||||
}
|
||||
|
||||
// 类型映射
|
||||
result = MapType(result);
|
||||
// 5. extends -> : (类继承)
|
||||
result = Regex.Replace(result,
|
||||
@"(class\s+\w+)\s+extends\s+(\w+)",
|
||||
"$1 : $2");
|
||||
|
||||
// Java 特定语法处理
|
||||
result = result
|
||||
.Replace("super.", "base.")
|
||||
.Replace("System.out.println", "Console.WriteLine")
|
||||
.Replace("@Override", "[Override]")
|
||||
.Replace("extends", ":")
|
||||
.Replace("implements", ":");
|
||||
// 6. implements -> : (接口实现)
|
||||
result = Regex.Replace(result,
|
||||
@"(class\s+\w+\s*(?::\s*\w+)?)\s+implements\s+",
|
||||
"$1, ");
|
||||
|
||||
// 方法声明转换
|
||||
result = System.Text.RegularExpressions.Regex.Replace(
|
||||
result,
|
||||
@"public\s+(\w+)\s+get(\w+)\(\)",
|
||||
"public $1 Get$2 { get; }"
|
||||
);
|
||||
// 7. 移除 Java 注解
|
||||
result = Regex.Replace(result,
|
||||
@"@\w+(?:\([^)]*\))?\s*",
|
||||
"");
|
||||
|
||||
result = System.Text.RegularExpressions.Regex.Replace(
|
||||
result,
|
||||
@"public\s+void\s+set(\w+)\(\1\s+\w+\)",
|
||||
"public void Set$1 { set; }"
|
||||
);
|
||||
// 8. static import 处理
|
||||
result = Regex.Replace(result,
|
||||
@"import\s+static\s+([\w.]+)\s*;",
|
||||
"// TODO: Convert static import: using static $1;");
|
||||
|
||||
// 9. System.out.println -> Console.WriteLine
|
||||
result = Regex.Replace(result,
|
||||
@"System\.out\.println\s*\(",
|
||||
"Console.WriteLine(");
|
||||
|
||||
// 10. System.out.print -> Console.Write
|
||||
result = Regex.Replace(result,
|
||||
@"System\.out\.print\s*\(",
|
||||
"Console.Write(");
|
||||
|
||||
// 11. super -> base
|
||||
result = Regex.Replace(result,
|
||||
@"\bsuper\b",
|
||||
"base");
|
||||
|
||||
// 12. this -> this (保持不变)
|
||||
// result = Regex.Replace(result, @"\bthis\b", "this");
|
||||
|
||||
// 13. null, true, false (Java 和 C# 相同,但确保小写)
|
||||
result = result.Replace("null", "null")
|
||||
.Replace("true", "true")
|
||||
.Replace("false", "false");
|
||||
|
||||
// 14. getter/setter -> C# 属性
|
||||
result = ConvertGettersSetters(result);
|
||||
|
||||
// 15. Lambda 表达式:(a, b) -> expr => (a, b) => expr
|
||||
result = Regex.Replace(result,
|
||||
@"(\w+)\s*->\s*",
|
||||
"$1 => ");
|
||||
result = Regex.Replace(result,
|
||||
@"\(([\w,\s]+)\)\s*->\s*",
|
||||
"($1) => ");
|
||||
|
||||
// 16. Stream API -> LINQ
|
||||
result = ConvertStreamToLinq(result);
|
||||
|
||||
// 17. CompletableFuture -> Task
|
||||
result = Regex.Replace(result,
|
||||
@"CompletableFuture\.completedFuture\(",
|
||||
"Task.FromResult(");
|
||||
result = Regex.Replace(result,
|
||||
@"CompletableFuture\.supplyAsync\(",
|
||||
"Task.Run(");
|
||||
result = Regex.Replace(result,
|
||||
@"\.thenApply\(",
|
||||
".ContinueWith(t => ");
|
||||
result = Regex.Replace(result,
|
||||
@"\.thenCompose\(",
|
||||
".ContinueWith(");
|
||||
result = Regex.Replace(result,
|
||||
@"\.whenComplete\(",
|
||||
".ContinueWith(");
|
||||
|
||||
// 18. throws -> 移除 (C# 不强制声明异常)
|
||||
result = Regex.Replace(result,
|
||||
@"\s*throws\s+\w+(?:\s*,\s*\w+)*",
|
||||
"");
|
||||
|
||||
// 19. @Override -> [Obsolete] 或移除
|
||||
result = Regex.Replace(result,
|
||||
@"@Override\s*",
|
||||
"// [Obsolete]\n");
|
||||
|
||||
// 20. final -> 不移除 (C# 没有等价物,但可作为注释保留)
|
||||
result = Regex.Replace(result,
|
||||
@"\bfinal\s+",
|
||||
"// final\n");
|
||||
|
||||
// 21. 泛型通配符处理
|
||||
result = Regex.Replace(result,
|
||||
@"List<\? extends (\w+)>",
|
||||
"IEnumerable<$1>");
|
||||
result = Regex.Replace(result,
|
||||
@"List<\? super (\w+)>",
|
||||
"IList<$1>");
|
||||
result = Regex.Replace(result,
|
||||
@"List<\?>",
|
||||
"IEnumerable");
|
||||
|
||||
// 22. 方法引用 -> Lambda
|
||||
result = Regex.Replace(result,
|
||||
@"(\w+)::(\w+)",
|
||||
"x => x.$2");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CheckUnconvertibleSyntax(string text, ConversionContext context)
|
||||
private string ConvertGettersSetters(string code)
|
||||
{
|
||||
// 检测 Stream API
|
||||
if (text.Contains(".stream(") || text.Contains("Stream."))
|
||||
// 处理 getter: public Type getName() { return name; }
|
||||
code = Regex.Replace(code,
|
||||
@"(public|private|protected)\s+(\w+)\s+get(\w+)\s*\(\s*\)\s*\{\s*return\s+(\w+)\s*;\s*\}",
|
||||
m => {
|
||||
var access = m.Groups[1].Value;
|
||||
var type = m.Groups[2].Value;
|
||||
var propName = Capitalize(m.Groups[4].Value);
|
||||
var fieldName = m.Groups[4].Value;
|
||||
return $"{access} {type} {propName} {{ get => {fieldName}; }}";
|
||||
});
|
||||
|
||||
// 处理 setter: public void setName(Type name) { this.name = name; }
|
||||
var setterPattern = @"(public|private|protected)\s+void\s+set(\w+)\s*\(\s*(\w+)\s+(\w+)\s*\)\s*\{\s*this\.(\w+)\s*=\s*(\w+)\s*;\s*\}";
|
||||
var setters = Regex.Matches(code, setterPattern);
|
||||
|
||||
foreach (Match setter in setters)
|
||||
{
|
||||
context.Issues.Add(new ConversionIssue
|
||||
{
|
||||
Type = IssueType.UnconvertibleSyntax,
|
||||
Description = "Java Stream API 需要转换为 LINQ",
|
||||
OriginalCode = text,
|
||||
Suggestion = "使用 LINQ 替代:stream().filter() → .Where(), stream().map() → .Select()"
|
||||
});
|
||||
var access = setter.Groups[1].Value;
|
||||
var propName = Capitalize(setter.Groups[2].Value);
|
||||
var type = setter.Groups[3].Value;
|
||||
var paramName = setter.Groups[4].Value;
|
||||
var fieldName = setter.Groups[5].Value;
|
||||
|
||||
context.TodoItems.Add(new TodoItem
|
||||
// 查找对应的 getter 并组合成完整属性
|
||||
var getterPattern = $@"({access})\s+{type}\s+{propName}\s*\{{\s*get\s*=>\s*{fieldName}\s*;\s*\}}";
|
||||
var getterMatch = Regex.Match(code, getterPattern);
|
||||
|
||||
if (getterMatch.Success)
|
||||
{
|
||||
Description = "将 Stream API 转换为 LINQ",
|
||||
OriginalSyntax = "Stream API",
|
||||
WhyNotDirect = "Java Stream 和 LINQ 语法不同,需要手动调整",
|
||||
RecommendedAlternative = "使用 .Where(), .Select(), .Aggregate() 等 LINQ 方法"
|
||||
});
|
||||
// 替换为完整属性
|
||||
code = Regex.Replace(code, getterPattern, $"{access} {type} {propName} {{ get; set; }}");
|
||||
// 移除 setter
|
||||
code = Regex.Replace(code, setterPattern, "");
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 CompletableFuture
|
||||
if (text.Contains("CompletableFuture") || text.Contains("thenApply") || text.Contains("thenAccept"))
|
||||
return code;
|
||||
}
|
||||
|
||||
private string ConvertStreamToLinq(string code)
|
||||
{
|
||||
// Stream 方法映射
|
||||
var mappings = new (string Java, string CSharp)[]
|
||||
{
|
||||
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<T> 和 async/await 关键字"
|
||||
});
|
||||
(".stream()", ""), // C# 直接使用 IEnumerable
|
||||
(".filter(", ".Where("),
|
||||
(".map(", ".Select("),
|
||||
(".flatMap(", ".SelectMany("),
|
||||
(".anyMatch(", ".Any("),
|
||||
(".allMatch(", ".All("),
|
||||
(".noneMatch(", "!.Any("),
|
||||
(".count()", ".Count()"),
|
||||
(".sum()", ".Sum()"),
|
||||
(".average()", ".Average()"),
|
||||
(".max(", ".Max("),
|
||||
(".min(", ".Min("),
|
||||
(".findFirst().orElse(null)", ".FirstOrDefault()"),
|
||||
(".findFirst().orElseThrow()", ".First()"),
|
||||
(".findAny()", ".FirstOrDefault()"),
|
||||
(".collect(Collectors.toList())", ".ToList()"),
|
||||
(".collect(Collectors.toSet())", ".ToHashSet()"),
|
||||
(".collect(Collectors.toMap(", ".ToDictionary("),
|
||||
(".collect(Collectors.joining(", ".Aggregate("),
|
||||
(".collect(Collectors.groupingBy(", ".GroupBy("),
|
||||
(".collect(Collectors.partitioningBy(", ".GroupBy("),
|
||||
(".skip(", ".Skip("),
|
||||
(".limit(", ".Take("),
|
||||
(".takeWhile(", ".TakeWhile("),
|
||||
(".dropWhile(", ".SkipWhile("),
|
||||
(".sorted(", ".OrderBy(x => x)"),
|
||||
(".sorted(Comparator.", ".OrderBy("),
|
||||
(".distinct(", ".Distinct("),
|
||||
(".peek(", ".Select("), // C# 没有直接的 Peek
|
||||
(".reduce(", ".Aggregate("),
|
||||
(".forEach(", ".ToList().ForEach("),
|
||||
(".toArray(", ".ToArray()"),
|
||||
(".parallel()", ".AsParallel()"),
|
||||
(".parallelStream()", ".AsParallel()"),
|
||||
};
|
||||
|
||||
var result = code;
|
||||
foreach (var (java, csharp) in mappings)
|
||||
{
|
||||
result = Regex.Replace(result, Regex.Escape(java), csharp);
|
||||
}
|
||||
|
||||
// 检测接口默认方法
|
||||
if (text.Contains("default ") && text.Contains(" interface "))
|
||||
// 添加 LINQ using
|
||||
if (result.Contains(".Where(") || result.Contains(".Select(") || result.Contains(".Any("))
|
||||
{
|
||||
context.Logs.Add(new TransformationLog
|
||||
if (!result.Contains("using System.Linq;"))
|
||||
{
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Operation = "Warning",
|
||||
Details = "Java 接口默认方法需要特殊处理",
|
||||
Level = LogLevel.Warning
|
||||
});
|
||||
var insertIdx = result.IndexOf("using ");
|
||||
if (insertIdx >= 0)
|
||||
{
|
||||
var endOfLine = result.IndexOf('\n', insertIdx);
|
||||
result = result.Insert(endOfLine + 1, "using System.Linq;\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string Capitalize(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return s;
|
||||
return char.ToUpperInvariant(s[0]) + s.Substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TypeMapping
|
||||
{
|
||||
public string SourceType { get; set; }
|
||||
public string TargetType { get; set; }
|
||||
|
||||
public TypeMapping(string source, string target)
|
||||
{
|
||||
SourceType = source;
|
||||
TargetType = target;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user