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,113 @@
package com.codechecker.analyzer;
import com.codechecker.config.AppConfig;
import com.codechecker.model.ApiEndpoint;
import com.codechecker.model.ClassChangeReport;
import com.codechecker.model.ClassType;
import java.util.ArrayList;
import com.codechecker.parser.ConversionParser;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 根据变更报告匹配受影响的 HTTP 接口与 Dto→Entity 转换目标。
*/
public class ImpactAnalyzer {
private final ConversionParser conversionParser = new ConversionParser();
/**
* 填充 report 的影响列表新旧类名均参与匹配Entity/Model 不匹配接口。
*/
public void analyze(ClassChangeReport report, Map<String, ApiEndpoint> endpointIndex,
AppConfig config, Path repoRoot, String newSource, String oldSource,
DtoNestIndex nestIndex) throws IOException {
Set<String> matchNames = namesForMatching(report, nestIndex);
if (report.getClassType() != ClassType.ENTITY && report.getClassType() != ClassType.MODEL) {
matchEndpoints(report, endpointIndex, matchNames);
}
report.setObjectRoleLabels(NestedObjectRoleResolver.resolve(report, nestIndex, endpointIndex));
if (!config.isDtoEntityConversionEnabled()) {
return;
}
analyzeConversion(report, config, repoRoot, newSource, oldSource, matchNames);
}
/** 收集新旧类名及嵌套祖先 Dto/Vo用于接口/转换匹配 */
private Set<String> namesForMatching(ClassChangeReport report, DtoNestIndex nestIndex) {
Set<String> names = new LinkedHashSet<>();
names.add(report.getClassName());
if (report.getOldClassName() != null && !report.getOldClassName().isBlank()) {
names.add(report.getOldClassName());
}
if (nestIndex != null
&& (report.getClassType() == ClassType.DTO || report.getClassType() == ClassType.VO)) {
for (String name : new ArrayList<>(names)) {
names.addAll(nestIndex.expandImpactNames(name));
}
}
return names;
}
/** 在接口索引中匹配入参/返回类型 */
private void matchEndpoints(ClassChangeReport report, Map<String, ApiEndpoint> endpointIndex,
Set<String> matchNames) {
List<ApiEndpoint> inputImpacts = new ArrayList<>();
List<ApiEndpoint> frontendImpacts = new ArrayList<>();
for (ApiEndpoint endpoint : endpointIndex.values()) {
if (matchesAnyType(endpoint.getParamTypes(), matchNames)) {
inputImpacts.add(endpoint);
}
if (matchesAnyType(endpoint.getReturnTypes(), matchNames)) {
frontendImpacts.add(endpoint);
}
}
inputImpacts.forEach(report::addInputImpact);
frontendImpacts.forEach(report::addFrontendImpact);
}
/** 扫描 convert 方法与 BeanUtils.copyProperties 关联的 Entity */
private void analyzeConversion(ClassChangeReport report, AppConfig config, Path repoRoot,
String newSource, String oldSource, Set<String> matchNames) throws IOException {
for (String name : matchNames) {
if (newSource != null && !newSource.isBlank()) {
conversionParser.findConvertTargetsInClass(newSource, name)
.forEach(report::addConversionEntity);
}
if (oldSource != null && !oldSource.isBlank() && !oldSource.equals(newSource)) {
conversionParser.findConvertTargetsInClass(oldSource, name)
.forEach(report::addConversionEntity);
}
for (String scanDir : config.getConversionScanDirs()) {
conversionParser.findBeanUtilsTargets(repoRoot.resolve(scanDir), name)
.forEach(report::addConversionEntity);
}
}
}
/** 类型集合中是否包含任一目标类名 */
private boolean matchesAnyType(Collection<String> types, Set<String> classNames) {
if (types == null) {
return false;
}
for (String type : types) {
if (classNames.contains(type)) {
return true;
}
}
return false;
}
}