Files
monkeycode-ai db536cfb2c 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>
2026-06-16 07:08:11 +00:00

391 lines
13 KiB
C#

using System.Text;
using System.Text.RegularExpressions;
using CodePlay.Core.Interfaces;
using CodePlay.Core.Models;
using CodePlay.Core.Common;
namespace CodePlay.Core.Converters;
public class JavaToCSharpStrategy : IConversionStrategy
{
public LanguageType SourceLanguage => LanguageType.Java;
public LanguageType TargetLanguage => LanguageType.CSharp;
private readonly List<TypeMapping> _typeMappings;
public JavaToCSharpStrategy()
{
_typeMappings = InitializeTypeMappings();
}
private List<TypeMapping> InitializeTypeMappings()
{
return new List<TypeMapping>
{
// 基本类型
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"),
};
}
public SyntaxNode ConvertNode(SyntaxNode node, ConversionContext context)
{
var newNode = new SyntaxNode
{
Type = node.Type,
Text = ConvertText(node.Text ?? "", context),
Metadata = new Dictionary<string, object?>(node.Metadata ?? new Dictionary<string, object?>()),
Parent = node.Parent,
Children = new List<SyntaxNode>(),
IsUnconvertible = node.IsUnconvertible,
TodoDescription = node.TodoDescription
};
foreach (var child in node.Children)
{
var convertedChild = ConvertNode(child, context);
convertedChild.Parent = newNode;
newNode.Children.Add(convertedChild);
}
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;
// 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"))
{
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");
}
}
}
// 4. 类型映射
foreach (var mapping in _typeMappings)
{
result = Regex.Replace(result,
$@"\b{Regex.Escape(mapping.SourceType)}(?=<|\b)",
mapping.TargetType);
}
// 5. extends -> : (类继承)
result = Regex.Replace(result,
@"(class\s+\w+)\s+extends\s+(\w+)",
"$1 : $2");
// 6. implements -> : (接口实现)
result = Regex.Replace(result,
@"(class\s+\w+\s*(?::\s*\w+)?)\s+implements\s+",
"$1, ");
// 7. 移除 Java 注解
result = Regex.Replace(result,
@"@\w+(?:\([^)]*\))?\s*",
"");
// 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 string ConvertGettersSetters(string code)
{
// 处理 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)
{
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;
// 查找对应的 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)
{
// 替换为完整属性
code = Regex.Replace(code, getterPattern, $"{access} {type} {propName} {{ get; set; }}");
// 移除 setter
code = Regex.Replace(code, setterPattern, "");
}
}
return code;
}
private string ConvertStreamToLinq(string code)
{
// Stream 方法映射
var mappings = new (string Java, string CSharp)[]
{
(".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);
}
// 添加 LINQ using
if (result.Contains(".Where(") || result.Contains(".Select(") || result.Contains(".Any("))
{
if (!result.Contains("using System.Linq;"))
{
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;
}
}