100 lines
4.1 KiB
Java
100 lines
4.1 KiB
Java
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("[]");
|
||
}
|
||
}
|