Files
codeCheck/src/main/java/com/codechecker/analyzer/ImpactAnalyzer.java
2026-06-09 17:57:52 +08:00

114 lines
4.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}