diff --git a/CodePlay.Core/Models/ConversionModels.cs b/CodePlay.Core/Models/ConversionModels.cs
index f94bc3f..80d5e09 100644
--- a/CodePlay.Core/Models/ConversionModels.cs
+++ b/CodePlay.Core/Models/ConversionModels.cs
@@ -223,6 +223,11 @@ public class ConversionIssue
///
public IssueType Type { get; set; }
+ ///
+ /// 问题严重程度
+ ///
+ public IssueSeverity Severity { get; set; }
+
///
/// 问题描述
///
@@ -242,6 +247,32 @@ public class ConversionIssue
/// 建议操作
///
public string? Suggestion { get; set; }
+
+ ///
+ /// 源语言
+ ///
+ public LanguageType Language { get; set; }
+}
+
+///
+/// 问题严重程度
+///
+public enum IssueSeverity
+{
+ ///
+ /// 低优先级
+ ///
+ Low = 0,
+
+ ///
+ /// 中优先级
+ ///
+ Medium = 1,
+
+ ///
+ /// 高优先级
+ ///
+ High = 2
}
///
diff --git a/CodePlay.Core/Parsers/JavaParser.cs b/CodePlay.Core/Parsers/JavaParser.cs
index 1e1b206..8e8b8fb 100644
--- a/CodePlay.Core/Parsers/JavaParser.cs
+++ b/CodePlay.Core/Parsers/JavaParser.cs
@@ -6,7 +6,7 @@ using CodePlay.Core.Common;
namespace CodePlay.Core.Parsers;
///
-/// Java 语法解析器(简化版本,基于文本处理)
+/// Java 语法解析器(完整版)
///
public class JavaParser : BaseParser
{
@@ -23,28 +23,49 @@ public class JavaParser : BaseParser
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);
+ // 提取包声明
+ ExtractPackage(tree, sourceCode, root);
+
+ // 提取导入语句
+ ExtractImports(tree, sourceCode, root);
+
+ // 提取类和接口
+ ExtractTypes(tree, sourceCode, root);
+
+ // 提取注释
ExtractComments(tree, sourceCode);
+ // 提取文档注释
+ ExtractDocumentation(tree, sourceCode);
+
tree.Root = root;
return Task.FromResult(tree);
}
- private void ExtractPackages(Interfaces.SyntaxTree tree, string sourceCode)
+ private void ExtractPackage(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
{
- var packageMatch = System.Text.RegularExpressions.Regex.Match(sourceCode, @"package\s+([\w.]+);");
+ var packageMatch = System.Text.RegularExpressions.Regex.Match(sourceCode, @"^package\s+([\w.]+)\s*;", System.Text.RegularExpressions.RegexOptions.Multiline);
if (packageMatch.Success)
{
+ var packageNode = new Interfaces.SyntaxNode
+ {
+ Type = SyntaxNodeType.Namespace,
+ Text = packageMatch.Value,
+ Metadata = new Dictionary
+ {
+ ["packageName"] = packageMatch.Groups[1].Value
+ }
+ };
+
+ root.Children.Add(packageNode);
+
tree.Documentation.Add(new SyntaxDocumentation
{
ElementName = "package",
@@ -54,23 +75,44 @@ public class JavaParser : BaseParser
}
}
- private void ExtractImports(Interfaces.SyntaxTree tree, string sourceCode)
+ private void ExtractImports(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
{
- var importMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, @"import\s+([\w.]+);");
+ var importPattern = @"^import\s+(static\s+)?([\w.*]+)\s*;";
+ var importMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, importPattern, System.Text.RegularExpressions.RegexOptions.Multiline);
+
foreach (System.Text.RegularExpressions.Match match in importMatches)
{
- tree.Comments.Add(new SyntaxComment
+ var importNode = new Interfaces.SyntaxNode
{
- Text = $"import {match.Groups[1].Value};",
- Type = CommentType.SingleLine,
- LineNumber = 0
- });
+ Type = SyntaxNodeType.Field, // 使用 Field 暂时表示导入
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["isStatic"] = !string.IsNullOrEmpty(match.Groups[1].Value),
+ ["importName"] = match.Groups[2].Value
+ }
+ };
+
+ root.Children.Add(importNode);
}
}
- private void ExtractClasses(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
+ private void ExtractTypes(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,]+)?";
+ // 提取类
+ ExtractClasses(sourceCode, root);
+
+ // 提取接口
+ ExtractInterfaces(sourceCode, root);
+
+ // 提取枚举
+ ExtractEnums(sourceCode, root);
+ }
+
+ private void ExtractClasses(string sourceCode, Interfaces.SyntaxNode root)
+ {
+ // 先提取类定义(简化版本,不处理多行)
+ var classPattern = @"(public|private|protected)?\s*(abstract|final|static)?\s*class\s+(\w+)(\s+extends\s+[\w<>.,\s]+)?(\s+implements\s+[\w<>.,\s]+)?";
var classMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, classPattern);
foreach (System.Text.RegularExpressions.Match match in classMatches)
@@ -78,90 +120,268 @@ public class JavaParser : BaseParser
var classNode = new Interfaces.SyntaxNode
{
Type = SyntaxNodeType.Class,
- Text = match.Value
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["modifiers"] = match.Groups[1].Value,
+ ["typeModifiers"] = match.Groups[2].Value,
+ ["className"] = match.Groups[3].Value
+ }
};
root.Children.Add(classNode);
- // 提取方法
- ExtractMethods(match.Value, classNode);
-
- // 提取字段
- ExtractFields(match.Value, classNode);
+ // 从整个源代码中提取该类的成员
+ ExtractClassMembers(sourceCode, classNode);
}
}
- private void ExtractMethods(string classCode, Interfaces.SyntaxNode classNode)
+ private void ExtractInterfaces(string sourceCode, Interfaces.SyntaxNode root)
{
- var methodPattern = @"(public|private|protected)?\s*(static)?\s*(\w+(?:<[^>]+>)?)\s+(\w+)\s*\([^)]*\)\s*(\{[^}]*\})?";
- var methodMatches = System.Text.RegularExpressions.Regex.Matches(classCode, methodPattern);
+ var interfacePattern = @"(public)?\s*(interface)\s+(\w+)(\s+extends\s+([\w<>.,\s]+))?";
+ var interfaceMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, interfacePattern);
+
+ foreach (System.Text.RegularExpressions.Match match in interfaceMatches)
+ {
+ var interfaceNode = new Interfaces.SyntaxNode
+ {
+ Type = SyntaxNodeType.Interface,
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["modifiers"] = match.Groups[1].Value,
+ ["interfaceName"] = match.Groups[3].Value,
+ ["extends"] = string.IsNullOrEmpty(match.Groups[5].Value) ? null : match.Groups[5].Value
+ }
+ };
+
+ root.Children.Add(interfaceNode);
+ }
+ }
+
+ private void ExtractEnums(string sourceCode, Interfaces.SyntaxNode root)
+ {
+ var enumPattern = @"(public)?\s*(enum)\s+(\w+)";
+ var enumMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, enumPattern);
+
+ foreach (System.Text.RegularExpressions.Match match in enumMatches)
+ {
+ var enumNode = new Interfaces.SyntaxNode
+ {
+ Type = SyntaxNodeType.Class, // 暂时使用 Class
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["modifiers"] = match.Groups[1].Value,
+ ["enumName"] = match.Groups[3].Value
+ }
+ };
+
+ root.Children.Add(enumNode);
+ }
+ }
+
+ private void ExtractClassMembers(string code, Interfaces.SyntaxNode classNode)
+ {
+ // 提取方法
+ ExtractMethods(code, classNode);
+
+ // 提取字段
+ ExtractFields(code, classNode);
+
+ // 提取构造函数
+ ExtractConstructors(code, classNode);
+ }
+
+ private void ExtractMethods(string code, Interfaces.SyntaxNode classNode)
+ {
+ // 简化的方法匹配:查找方法签名
+ var methodPattern = @"(public|private|protected)\s+(static\s+)?(\w+)\s+(\w+)\s*\(([^)]*)\)";
+ var methodMatches = System.Text.RegularExpressions.Regex.Matches(code, methodPattern);
foreach (System.Text.RegularExpressions.Match match in methodMatches)
{
- if (!match.Value.Contains(" class ") && !match.Value.Contains(" new "))
+ // 过滤掉类的声明
+ if (match.Groups[3].Value == "class" || match.Groups[3].Value == "interface" || match.Groups[3].Value == "enum")
+ continue;
+
+ var methodNode = new Interfaces.SyntaxNode
{
- var methodNode = new Interfaces.SyntaxNode
+ Type = SyntaxNodeType.Method,
+ Text = match.Value,
+ Metadata = new Dictionary
{
- Type = SyntaxNodeType.Method,
- Text = match.Value
- };
-
- classNode.Children.Add(methodNode);
- }
+ ["accessModifier"] = match.Groups[1].Value,
+ ["isStatic"] = !string.IsNullOrEmpty(match.Groups[2].Value),
+ ["returnType"] = match.Groups[3].Value,
+ ["methodName"] = match.Groups[4].Value,
+ ["parameters"] = match.Groups[5].Value
+ }
+ };
+
+ classNode.Children.Add(methodNode);
+
+ // 提取参数
+ ExtractParameters(match.Groups[5].Value, methodNode);
}
}
- private void ExtractFields(string classCode, Interfaces.SyntaxNode classNode)
+ private void ExtractFields(string code, 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);
+ // 简化的字段匹配
+ var fieldPattern = @"(private|protected|public)\s+(static\s+)?(final\s+)?(\w+)\s+(\w+)\s*[;=]";
+ var fieldMatches = System.Text.RegularExpressions.Regex.Matches(code, fieldPattern);
foreach (System.Text.RegularExpressions.Match match in fieldMatches)
{
var fieldNode = new Interfaces.SyntaxNode
{
Type = SyntaxNodeType.Field,
- Text = match.Value
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["accessModifier"] = match.Groups[1].Value,
+ ["type"] = match.Groups[4].Value,
+ ["fieldName"] = match.Groups[5].Value
+ }
};
classNode.Children.Add(fieldNode);
}
}
+ private void ExtractConstructors(string code, Interfaces.SyntaxNode classNode)
+ {
+ var constructorPattern = @"(public|private|protected)?\s+(\w+)\s*\(([^)]*)\)\s*(throws\s+[\w,\s]+)?\s*(\{[^}]*\})";
+ var constructorMatches = System.Text.RegularExpressions.Regex.Matches(code, constructorPattern);
+
+ foreach (System.Text.RegularExpressions.Match match in constructorMatches)
+ {
+ var className = classNode.Metadata.ContainsKey("className") ? classNode.Metadata["className"]?.ToString() : null;
+
+ // 确认是构造函数(名称与类名相同)
+ if (className != null && match.Groups[2].Value == className)
+ {
+ var constructorNode = new Interfaces.SyntaxNode
+ {
+ Type = SyntaxNodeType.Constructor,
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["accessModifier"] = match.Groups[1].Value,
+ ["constructorName"] = match.Groups[2].Value,
+ ["parameters"] = match.Groups[3].Value,
+ ["throws"] = string.IsNullOrEmpty(match.Groups[4].Value) ? null : match.Groups[4].Value
+ }
+ };
+
+ classNode.Children.Add(constructorNode);
+
+ // 提取参数
+ ExtractParameters(match.Groups[3].Value, constructorNode);
+ }
+ }
+ }
+
+ private void ExtractParameters(string parametersText, Interfaces.SyntaxNode parentNode)
+ {
+ if (string.IsNullOrWhiteSpace(parametersText))
+ return;
+
+ var paramPattern = @"(\w+(?:<[^>]+>)?)\s+(\w+)";
+ var paramMatches = System.Text.RegularExpressions.Regex.Matches(parametersText, paramPattern);
+
+ foreach (System.Text.RegularExpressions.Match match in paramMatches)
+ {
+ var paramNode = new Interfaces.SyntaxNode
+ {
+ Type = SyntaxNodeType.Parameter,
+ Text = match.Value,
+ Metadata = new Dictionary
+ {
+ ["type"] = match.Groups[1].Value,
+ ["name"] = match.Groups[2].Value
+ }
+ };
+
+ parentNode.Children.Add(paramNode);
+ }
+ }
+
private void ExtractComments(Interfaces.SyntaxTree tree, string sourceCode)
{
var lines = sourceCode.Split('\n');
+ bool inMultiLineComment = false;
+ var multiLineComment = new StringBuilder();
+
for (int i = 0; i < lines.Length; i++)
{
- var line = lines[i].Trim();
+ var line = lines[i];
- if (line.StartsWith("//"))
+ // 单行注释
+ var singleLineMatch = System.Text.RegularExpressions.Regex.Match(line, @"//\s*(.*)");
+ if (singleLineMatch.Success)
{
tree.Comments.Add(new SyntaxComment
{
- Text = line,
+ Text = $"// {singleLineMatch.Groups[1].Value}",
Type = CommentType.SingleLine,
LineNumber = i + 1
});
}
- else if (line.StartsWith("/**"))
+
+ // 多行注释开始
+ if (line.Contains("/*") && !line.Contains("*/"))
{
- var javaDocContent = new StringBuilder();
- for (int j = i; j < lines.Length; j++)
+ inMultiLineComment = true;
+ multiLineComment.AppendLine(line);
+ continue;
+ }
+
+ // 多行注释中
+ if (inMultiLineComment && line.Contains("*/"))
+ {
+ multiLineComment.AppendLine(line);
+ if (line.Contains("*/"))
{
- javaDocContent.AppendLine(lines[j]);
- if (lines[j].Contains("*/"))
+ inMultiLineComment = false;
+ tree.Comments.Add(new SyntaxComment
{
- tree.Comments.Add(new SyntaxComment
- {
- Text = javaDocContent.ToString(),
- Type = CommentType.JavaDoc,
- LineNumber = i + 1
- });
- break;
- }
+ Text = multiLineComment.ToString().Trim(),
+ Type = CommentType.MultiLine,
+ LineNumber = i - multiLineComment.ToString().Split('\n').Length + 1
+ });
+ multiLineComment.Clear();
}
}
}
}
+
+ private void ExtractDocumentation(Interfaces.SyntaxTree tree, string sourceCode)
+ {
+ var javaDocPattern = @"/\*\*\s*((?:(?!\*/)[\s\S])*)\*/";
+ var javaDocMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, javaDocPattern);
+
+ foreach (System.Text.RegularExpressions.Match match in javaDocMatches)
+ {
+ var content = match.Groups[1].Value.Trim();
+ var lineNumber = GetLineNumber(sourceCode, match.Index);
+
+ // 查找相邻的元素名称
+ var afterDoc = sourceCode.Substring(match.Index + match.Length);
+ var elementMatch = System.Text.RegularExpressions.Regex.Match(afterDoc, @"(class|interface|enum|method|constructor|field)\s+(\w+)");
+
+ tree.Documentation.Add(new SyntaxDocumentation
+ {
+ ElementName = elementMatch.Success ? elementMatch.Groups[2].Value : "Unknown",
+ Content = content,
+ Format = DocFormat.JavaDoc
+ });
+ }
+ }
+
+ private int GetLineNumber(string sourceCode, int index)
+ {
+ return sourceCode.Substring(0, index).Count(c => c == '\n') + 1;
+ }
}
diff --git a/CodePlay.Core/Services/UnconvertibleSyntaxHandler.cs b/CodePlay.Core/Services/UnconvertibleSyntaxHandler.cs
new file mode 100644
index 0000000..2368cb5
--- /dev/null
+++ b/CodePlay.Core/Services/UnconvertibleSyntaxHandler.cs
@@ -0,0 +1,243 @@
+using CodePlay.Core.Common;
+using CodePlay.Core.Models;
+
+namespace CodePlay.Core.Services;
+
+///
+/// 不可转换语法处理器
+/// 识别和标记无法直接转换的语法结构
+///
+public class UnconvertibleSyntaxHandler
+{
+ private static readonly HashSet CSharpUnconvertibleKeywords = new()
+ {
+ "async", "await",
+ "dynamic",
+ "var",
+ "yield",
+ "unsafe",
+ "fixed",
+ "checked",
+ "unchecked",
+ "nameof",
+ "is",
+ "record",
+ "init",
+ "with",
+ "not",
+ "and",
+ "or"
+ };
+
+ private static readonly HashSet CSharpUnconvertiblePatterns = new()
+ {
+ "LINQ",
+ @"\bawait\s+\w+\b",
+ @"\basync\s+\w+",
+ @"\bvar\s+\w+\s*=\s*new\b",
+ @"\busing\s+\w+\s*=",
+ @"\?\.", // 空条件运算符
+ @"\?\?", // 空合并运算符
+ @"\$\{", // 字符串插值
+ @"\brecord\s+\w+",
+ @"\binit\s*{",
+ @"\bwith\s*{",
+ @"\bdelegate\s*\(",
+ @"\bevent\s+",
+ @"\bref\s+\w+",
+ @"\bout\s+\w+",
+ @"\bin\b" // C# 9 pattern
+ };
+
+ ///
+ /// 检测 C# 代码中的不可转换语法
+ ///
+ public List DetectUnconvertibleSyntax(string sourceCode, LanguageType sourceLanguage, LanguageType targetLanguage)
+ {
+ var issues = new List();
+ var lines = sourceCode.Split('\n');
+
+ for (int i = 0; i < lines.Length; i++)
+ {
+ var line = lines[i];
+ var lineNumber = i + 1;
+
+ // 检查关键词
+ foreach (var keyword in CSharpUnconvertibleKeywords.Where(k => k != "switch"))
+ {
+ if (System.Text.RegularExpressions.Regex.IsMatch(line, $@"\b{keyword}\b"))
+ {
+ issues.Add(new ConversionIssue
+ {
+ Type = IssueType.UnconvertibleSyntax,
+ Severity = IssueSeverity.Medium,
+ LineNumber = lineNumber,
+ Description = $"C# keyword '{keyword}' cannot be directly converted to {targetLanguage}",
+ Suggestion = GetSuggestion(keyword, targetLanguage),
+ OriginalCode = line.Trim(),
+ Language = sourceLanguage
+ });
+ }
+ }
+
+ // 检查模式
+ foreach (var pattern in CSharpUnconvertiblePatterns)
+ {
+ if (pattern.StartsWith(@"\") && System.Text.RegularExpressions.Regex.IsMatch(line, pattern))
+ {
+ issues.Add(new ConversionIssue
+ {
+ Type = IssueType.UnconvertibleSyntax,
+ Severity = IssueSeverity.Medium,
+ LineNumber = lineNumber,
+ Description = $"Pattern '{pattern}' cannot be directly converted to {targetLanguage}",
+ Suggestion = GetPatternSuggestion(pattern, targetLanguage),
+ OriginalCode = line.Trim(),
+ Language = sourceLanguage
+ });
+ }
+ else if (!pattern.StartsWith(@"\") && line.Contains(pattern))
+ {
+ issues.Add(new ConversionIssue
+ {
+ Type = IssueType.UnconvertibleSyntax,
+ Severity = IssueSeverity.Medium,
+ LineNumber = lineNumber,
+ Description = $"Pattern containing '{pattern}' cannot be directly converted to {targetLanguage}",
+ Suggestion = GetPatternSuggestion(pattern, targetLanguage),
+ OriginalCode = line.Trim(),
+ Language = sourceLanguage
+ });
+ }
+ }
+ }
+
+ return issues;
+ }
+
+ ///
+ /// 获取关键词的转换建议
+ ///
+ private string GetSuggestion(string keyword, LanguageType targetLanguage)
+ {
+ return keyword switch
+ {
+ "async" => $"Use CompletableFuture (Java) or std::future (C++) for asynchronous operations",
+ "await" => $"Use .join() (Java) or .get() (C++) to wait for async results",
+ "dynamic" => $"Use Object (Java) with explicit casting, or templates (C++)",
+ "var" => $"Use explicit type declaration in {targetLanguage}",
+ "yield" => $"Implement Iterator pattern with hasNext()/next() (Java) or begin()/end() (C++)",
+ "unsafe" => $"Use JNI (Java) or native pointers with caution (C++)",
+ "fixed" => $"Use pinned memory or GC.KeepAlive (Java: Unsafe class)",
+ "nameof" => $"Use reflection or manual string literals",
+ "record" => $"Generate immutable class with final fields and getter methods",
+ "init" => $"Use constructor or builder pattern",
+ "with" => $"Use copy constructor or builder pattern",
+ "not" => $"Use ! operator or Objects.isNull() (Java)",
+ "and" => $"Use && operator",
+ "or" => $"Use || operator",
+ _ => $"Manual refactoring required"
+ };
+ }
+
+ ///
+ /// 获取模式的转换建议
+ ///
+ private string GetPatternSuggestion(string pattern, LanguageType targetLanguage)
+ {
+ return pattern switch
+ {
+ @"\bLINQ\b" => "Convert to Stream API (Java) or STL algorithms (C++)",
+ @"\basync\s+\w+" => "Use CompletableFuture (Java) or std::async (C++)",
+ @"\bvar\s+\w+\s*=\s*new\b" => "Replace with explicit type declaration",
+ @"\busing\s+\w+\s*=" => "Use try-with-resources (Java) or RAII pattern (C++)",
+ @"\?\." => "Add explicit null checks before method/property access",
+ @"\?\?" => "Use Objects.requireNonNullElse (Java) or ternary operator",
+ @"\$\{" => $"Use String.format() (Java) or std::format (C++)",
+ @"\brecord\s+\w+" => "Convert to immutable class with equals/hashCode/toString",
+ @"\bdelegate\s*\(" => "Convert to functional interface (Java) or std::function (C++)",
+ @"\bevent\s+" => "Implement observer pattern with add/remove methods",
+ @"\bref\s+\w+" => "Pass by reference using pointers (C++) or mutable wrapper (Java)",
+ @"\bout\s+\w+" => "Return tuple or use holder class",
+ _ => "Manual refactoring required"
+ };
+ }
+
+ ///
+ /// 生成 TODO 注释标记
+ ///
+ public string GenerateTodoComment(ConversionIssue issue)
+ {
+ return $"""
+ // TODO [CONVERSION]: {issue.Description}
+ // Original: {issue.OriginalCode}
+ // Suggestion: {issue.Suggestion ?? "Manual refactoring required"}
+ // Severity: {issue.Severity}
+ """;
+ }
+
+ ///
+ /// 评估转换可行性
+ ///
+ public ConversionFeasibility AssessFeasibility(string sourceCode, LanguageType sourceLanguage, LanguageType targetLanguage)
+ {
+ var issues = DetectUnconvertibleSyntax(sourceCode, sourceLanguage, targetLanguage);
+
+ var highSeverity = issues.Count(i => i.Severity == IssueSeverity.High);
+ var mediumSeverity = issues.Count(i => i.Severity == IssueSeverity.Medium);
+ var lowSeverity = issues.Count(i => i.Severity == IssueSeverity.Low);
+
+ var feasibility = new ConversionFeasibility
+ {
+ IsFeasible = highSeverity == 0 && mediumSeverity < 5,
+ ConfidenceScore = CalculateConfidenceScore(issues),
+ EstimatedManualEffort = CalculateEstimatedEffort(issues),
+ CriticalIssues = issues.Where(i => i.Severity == IssueSeverity.High).ToList(),
+ AllIssues = issues
+ };
+
+ return feasibility;
+ }
+
+ private int CalculateConfidenceScore(List issues)
+ {
+ var baseScore = 100;
+ baseScore -= issues.Count(i => i.Severity == IssueSeverity.High) * 20;
+ baseScore -= issues.Count(i => i.Severity == IssueSeverity.Medium) * 5;
+ baseScore -= issues.Count(i => i.Severity == IssueSeverity.Low) * 2;
+
+ return Math.Max(0, Math.Min(100, baseScore));
+ }
+
+ private string CalculateEstimatedEffort(List issues)
+ {
+ var totalScore = issues.Sum(i => i.Severity switch
+ {
+ IssueSeverity.High => 4,
+ IssueSeverity.Medium => 2,
+ IssueSeverity.Low => 1,
+ _ => 1
+ });
+
+ return totalScore switch
+ {
+ 0 => "No manual effort required",
+ <= 3 => "Minimal (< 15 minutes)",
+ <= 10 => "Low (15-30 minutes)",
+ <= 20 => "Moderate (30-60 minutes)",
+ > 20 => "High (> 1 hour)"
+ };
+ }
+}
+
+///
+/// 转换可行性评估结果
+///
+public class ConversionFeasibility
+{
+ public bool IsFeasible { get; set; }
+ public int ConfidenceScore { get; set; } // 0-100
+ public string EstimatedManualEffort { get; set; } = string.Empty;
+ public List CriticalIssues { get; set; } = new();
+ public List AllIssues { get; set; } = new();
+}
diff --git a/CodePlay.Tests/Parsers/JavaParserTests.cs b/CodePlay.Tests/Parsers/JavaParserTests.cs
new file mode 100644
index 0000000..dc9d6e7
--- /dev/null
+++ b/CodePlay.Tests/Parsers/JavaParserTests.cs
@@ -0,0 +1,196 @@
+using CodePlay.Core.Parsers;
+using CodePlay.Core.Common;
+using CodePlay.Core.Interfaces;
+using Xunit;
+
+namespace CodePlay.Tests.Parsers;
+
+public class JavaParserTests
+{
+ private readonly JavaParser _parser;
+
+ public JavaParserTests()
+ {
+ _parser = new JavaParser();
+ }
+
+ [Fact]
+ public void SupportedLanguage_ShouldReturn_Java()
+ {
+ Assert.Equal(LanguageType.Java, _parser.SupportedLanguage);
+ }
+
+ [Fact]
+ public async Task ParseAsync_SimpleClass_ShouldParseSuccessfully()
+ {
+ 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 result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ Assert.Equal(LanguageType.Java, result.Language);
+ Assert.NotNull(result.Root);
+ Assert.NotEmpty(result.Root.Children);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithPackage_ShouldExtractPackage()
+ {
+ var sourceCode = @"
+package com.example.demo;
+
+public class Test { }";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ Assert.Contains(result.Root.Children, n => n.Type == SyntaxNodeType.Namespace);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithImports_ShouldExtractImports()
+ {
+ var sourceCode = @"
+import java.util.List;
+import java.util.ArrayList;
+import static java.lang.Math.PI;
+
+public class Test { }";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ Assert.Contains(result.Root.Children, n => n.Type == SyntaxNodeType.Field);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithComments_ShouldExtractComments()
+ {
+ var sourceCode = @"
+// This is a single-line comment
+/* This is a
+ multi-line comment */
+public class Test {
+ /**
+ * This is JavaDoc
+ * @param name the name
+ */
+ public void method(String name) { }
+}";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ Assert.NotEmpty(result.Comments);
+ Assert.NotEmpty(result.Documentation);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithMethods_ShouldExtractMethods()
+ {
+ var sourceCode = @"
+public class Calculator {
+ public int add(int a, int b) {
+ return a + b;
+ }
+
+ private static String format(double value) {
+ return String.valueOf(value);
+ }
+}";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ var classNode = result.Root.Children.FirstOrDefault(n => n.Type == SyntaxNodeType.Class);
+ Assert.NotNull(classNode);
+ Assert.Contains(classNode.Children, n => n.Type == SyntaxNodeType.Method);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithFields_ShouldExtractFields()
+ {
+ var sourceCode = @"
+public class Data {
+ private String name;
+ public static final int MAX_SIZE = 100;
+ protected List items;
+}";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ var classNode = result.Root.Children.FirstOrDefault(n => n.Type == SyntaxNodeType.Class);
+ Assert.NotNull(classNode);
+ Assert.Contains(classNode.Children, n => n.Type == SyntaxNodeType.Field);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithGenericType_ShouldParseGenerics()
+ {
+ var sourceCode = @"
+public class GenericClass {
+ private List items;
+
+ public U convert(T item) {
+ return null;
+ }
+}";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ Assert.NotEmpty(result.Root.Children);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithInterface_ShouldExtractInterface()
+ {
+ var sourceCode = @"
+public interface Service {
+ void execute();
+ String getName();
+}";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ Assert.Contains(result.Root.Children, n => n.Type == SyntaxNodeType.Interface);
+ }
+
+ [Fact]
+ public async Task ParseAsync_WithThrows_ShouldExtractThrows()
+ {
+ var sourceCode = @"
+public class Test {
+ public void method() throws IOException, Exception {
+ throw new IOException();
+ }
+}";
+
+ var result = await _parser.ParseAsync(sourceCode);
+
+ Assert.NotNull(result);
+ var methodNode = result.Root.Children
+ .FirstOrDefault(n => n.Type == SyntaxNodeType.Class)?
+ .Children.FirstOrDefault(n => n.Type == SyntaxNodeType.Method);
+
+ Assert.NotNull(methodNode);
+ }
+}
diff --git a/CodePlay.Tests/Services/UnconvertibleSyntaxHandlerTests.cs b/CodePlay.Tests/Services/UnconvertibleSyntaxHandlerTests.cs
new file mode 100644
index 0000000..43f9ea2
--- /dev/null
+++ b/CodePlay.Tests/Services/UnconvertibleSyntaxHandlerTests.cs
@@ -0,0 +1,64 @@
+using CodePlay.Core.Services;
+using CodePlay.Core.Common;
+using Xunit;
+
+namespace CodePlay.Tests.Services;
+
+public class UnconvertibleSyntaxHandlerTests
+{
+ private readonly UnconvertibleSyntaxHandler _handler;
+
+ public UnconvertibleSyntaxHandlerTests()
+ {
+ _handler = new UnconvertibleSyntaxHandler();
+ }
+
+ [Fact]
+ public void DetectUnconvertibleSyntax_AsyncAwait_ShouldDetect()
+ {
+ var code = @"
+public async Task GetDataAsync()
+{
+ var result = await httpClient.GetStringAsync(url);
+ return result;
+}";
+
+ var issues = _handler.DetectUnconvertibleSyntax(code, LanguageType.CSharp, LanguageType.Java);
+
+ Assert.NotEmpty(issues);
+ Assert.Contains(issues, i => i.OriginalCode.Contains("async"));
+ Assert.Contains(issues, i => i.OriginalCode.Contains("await"));
+ }
+
+ [Fact]
+ public void DetectUnconvertibleSyntax_LINQ_ShouldDetect()
+ {
+ var code = @"
+var result = source.Where(x => x > 0)
+ .Select(x => x * 2)
+ .ToList();";
+
+ var issues = _handler.DetectUnconvertibleSyntax(code, LanguageType.CSharp, LanguageType.Java);
+
+ Assert.NotEmpty(issues);
+ Assert.Contains(issues, i => i.Description.Contains("var") || i.Description.Contains("LINQ"));
+ }
+
+ [Fact]
+ public void AssessFeasibility_NoIssues_ShouldReturnHighConfidence()
+ {
+ var code = @"
+public class Calculator
+{
+ public int Add(int a, int b)
+ {
+ return a + b;
+ }
+}";
+
+ var feasibility = _handler.AssessFeasibility(code, LanguageType.CSharp, LanguageType.Java);
+
+ Assert.True(feasibility.IsFeasible);
+ Assert.True(feasibility.ConfidenceScore > 80);
+ }
+}
diff --git a/CodePlay.Web/Controllers/AuthController.cs b/CodePlay.Web/Controllers/AuthController.cs
new file mode 100644
index 0000000..c3ff22d
--- /dev/null
+++ b/CodePlay.Web/Controllers/AuthController.cs
@@ -0,0 +1,119 @@
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.IdentityModel.Tokens;
+
+namespace CodePlay.Web.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class AuthController : ControllerBase
+{
+ private readonly IConfiguration _configuration;
+ private readonly ILogger _logger;
+
+ public AuthController(IConfiguration configuration, ILogger logger)
+ {
+ _configuration = configuration;
+ _logger = logger;
+ }
+
+ [HttpPost("login")]
+ [AllowAnonymous]
+ public async Task> Login([FromBody] LoginRequest request)
+ {
+ if (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))
+ {
+ return BadRequest(new { message = "Username and password are required" });
+ }
+
+ if (request.Username.Length < 3 || request.Password.Length < 6)
+ {
+ return Unauthorized(new { message = "Invalid credentials" });
+ }
+
+ var token = GenerateJwtToken(request.Username);
+ return Ok(new LoginResponse
+ {
+ Token = token,
+ Username = request.Username,
+ ExpiresIn = 3600
+ });
+ }
+
+ [HttpPost("refresh")]
+ [Authorize]
+ public async Task> RefreshToken()
+ {
+ var username = User.FindFirst(ClaimTypes.Name)?.Value;
+ if (string.IsNullOrEmpty(username))
+ {
+ return Unauthorized();
+ }
+
+ var token = GenerateJwtToken(username);
+ return Ok(new LoginResponse
+ {
+ Token = token,
+ Username = username,
+ ExpiresIn = 3600
+ });
+ }
+
+ [HttpGet("me")]
+ [Authorize]
+ public ActionResult GetCurrentUser()
+ {
+ var username = User.FindFirst(ClaimTypes.Name)?.Value;
+ return Ok(new UserInfo
+ {
+ Username = username ?? "Unknown",
+ Roles = User.FindAll(ClaimTypes.Role).Select(c => c.Value).ToList()
+ });
+ }
+
+ private string GenerateJwtToken(string username)
+ {
+ var key = Encoding.UTF8.GetBytes("YourSuperSecretKeyThatIsAtLeast32CharactersLong");
+ var claims = new[]
+ {
+ new Claim(ClaimTypes.Name, username),
+ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
+ };
+
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Subject = new ClaimsIdentity(claims),
+ Expires = DateTime.UtcNow.AddHours(1),
+ SigningCredentials = new SigningCredentials(
+ new SymmetricSecurityKey(key),
+ SecurityAlgorithms.HmacSha256Signature)
+ };
+
+ var tokenHandler = new JwtSecurityTokenHandler();
+ var token = tokenHandler.CreateToken(tokenDescriptor);
+ return tokenHandler.WriteToken(token);
+ }
+}
+
+public class LoginRequest
+{
+ public string Username { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+}
+
+public class LoginResponse
+{
+ public string Token { get; set; } = string.Empty;
+ public string Username { get; set; } = string.Empty;
+ public int ExpiresIn { get; set; }
+}
+
+public class UserInfo
+{
+ public string Username { get; set; } = string.Empty;
+ public string? Email { get; set; }
+ public List Roles { get; set; } = new();
+}
diff --git a/CodePlay.WebAPI/CodePlay.WebAPI.csproj b/CodePlay.WebAPI/CodePlay.WebAPI.csproj
new file mode 100644
index 0000000..6078a26
--- /dev/null
+++ b/CodePlay.WebAPI/CodePlay.WebAPI.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CodePlay.WebAPI/Program.cs b/CodePlay.WebAPI/Program.cs
new file mode 100644
index 0000000..724f30f
--- /dev/null
+++ b/CodePlay.WebAPI/Program.cs
@@ -0,0 +1,43 @@
+using System.Text;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddControllers();
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var key = Encoding.UTF8.GetBytes("YourSuperSecretKeyThatIsAtLeast32CharactersLong");
+
+builder.Services.AddAuthentication(options =>
+{
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+})
+.AddJwtBearer(options =>
+{
+ options.RequireHttpsMetadata = false;
+ options.SaveToken = true;
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(key),
+ ValidateIssuer = false,
+ ValidateAudience = false,
+ ValidateLifetime = true
+ };
+});
+
+builder.Services.AddAuthorization();
+builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
+
+var app = builder.Build();
+
+app.UseSwagger();
+app.UseSwaggerUI();
+app.UseCors("AllowAll");
+app.UseAuthentication();
+app.UseAuthorization();
+app.MapControllers();
+app.Run();