114 lines
4.4 KiB
Java
114 lines
4.4 KiB
Java
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;
|
||
}
|
||
}
|