first commit

This commit is contained in:
2026-06-09 17:51:03 +08:00
commit ebf30399fb
47 changed files with 5532 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
package com.codechecker.api.parser;
import com.codechecker.git.GitChangeScanner;
import com.codechecker.model.FieldInfo;
import com.codechecker.parser.ClassFieldParser;
import com.codechecker.parser.TypeNameUtils;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* 递归展开 Dto/Vo 嵌套字段dot path与类变更字段解析解耦但复用 ClassFieldParser。
*/
public class NestedDtoFieldParser {
private static final Set<String> LEAF_TYPES = Set.of(
"String", "Integer", "int", "Long", "long", "Boolean", "boolean", "Double", "double",
"Float", "float", "Short", "short", "Byte", "byte", "Character", "char",
"BigDecimal", "BigInteger", "Date", "LocalDate", "LocalDateTime", "LocalTime",
"Instant", "Timestamp", "Object", "Void", "void"
);
private final ClassFieldParser classFieldParser = new ClassFieldParser();
private final JavaSourceLocator sourceLocator;
private final GitChangeScanner gitScanner;
private final String oldSha;
private final String newSha;
private final int maxDepth;
public NestedDtoFieldParser(Path repoRoot, List<String> searchDirs,
GitChangeScanner gitScanner, String oldSha, String newSha, int maxDepth) {
this.sourceLocator = new JavaSourceLocator(repoRoot, searchDirs);
this.gitScanner = gitScanner;
this.oldSha = oldSha;
this.newSha = newSha;
this.maxDepth = maxDepth;
}
public List<NestedFieldInfo> parseNestedFieldsAtOldCommit(String dtoClassName) throws IOException {
return parseNestedFields(dtoClassName, oldSha);
}
public List<NestedFieldInfo> parseNestedFieldsAtNewCommit(String dtoClassName) throws IOException {
return parseNestedFields(dtoClassName, newSha);
}
private List<NestedFieldInfo> parseNestedFields(String dtoClassName, String sha) throws IOException {
Set<String> visiting = new HashSet<>();
List<NestedFieldInfo> result = new ArrayList<>();
collectFields(dtoClassName, "", visiting, result, sha, 1);
return result;
}
private void collectFields(String className, String prefix, Set<String> visiting,
List<NestedFieldInfo> out, String sha, int depth) throws IOException {
if (className == null || className.isBlank() || visiting.contains(className) || depth > maxDepth) {
return;
}
visiting.add(className);
Optional<String> source = readSource(className, sha);
if (source.isEmpty()) {
visiting.remove(className);
return;
}
List<FieldInfo> fields = classFieldParser.parseFields(source.get(), className);
for (FieldInfo field : fields) {
String path = prefix.isBlank() ? field.getName() : prefix + "." + field.getName();
Set<String> nestedTypes = TypeNameUtils.peelDirectTypeNames(field.getType());
boolean expanded = false;
for (String nestedType : nestedTypes) {
if (isLeafType(nestedType) || nestedType.equals(className)) {
continue;
}
expanded = true;
// 嵌套字段路径用类型简单类名(如 UserSelfDto.nickName不用成员名userDtos.nickName
collectFields(nestedType, nestedType, visiting, out, sha, depth + 1);
}
if (!expanded) {
out.add(new NestedFieldInfo(path, field.getType(), field.getDescription()));
}
}
visiting.remove(className);
}
private Optional<String> readSource(String className, String sha) throws IOException {
if (sha != null && gitScanner != null) {
return sourceLocator.readSourceAtCommit(gitScanner, sha, className);
}
return sourceLocator.readSourceBySimpleName(className);
}
private boolean isLeafType(String simpleType) {
return LEAF_TYPES.contains(simpleType) || simpleType.endsWith("[]");
}
}