db536cfb2c
核心修复: - 修复 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>
391 lines
13 KiB
C#
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;
|
|
}
|
|
}
|