@@ -1,234 +0,0 @@
|
||||
package com.codechecker.api.analyzer;
|
||||
|
||||
import com.codechecker.analyzer.FieldDiffEngine;
|
||||
import com.codechecker.api.model.EndpointSnapshot;
|
||||
import com.codechecker.api.model.MethodParameterSnapshot;
|
||||
import com.codechecker.api.model.ParameterChange;
|
||||
import com.codechecker.api.parser.NestedDtoFieldParser;
|
||||
import com.codechecker.api.parser.NestedFieldInfo;
|
||||
import com.codechecker.git.GitChangeScanner;
|
||||
import com.codechecker.model.FieldChange;
|
||||
import com.codechecker.model.FieldInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 接口入参 diff(普通参数 + RequestBody 嵌套 Dto 字段)。
|
||||
*
|
||||
* path/query 规则:
|
||||
* - 形参名+类型相同,仅绑定名变 → 重命名
|
||||
* - 形参名+绑定名相同,仅类型变 → 类型变更
|
||||
* - 仅形参名变(绑定名不变)→ 不通知
|
||||
* - 类型与绑定名同时变,或三者都变 → 先删除后新增
|
||||
*/
|
||||
public class ParameterDiffEngine {
|
||||
private final NestedDtoFieldParser nestedDtoFieldParser;
|
||||
private final FieldDiffEngine fieldDiffEngine = new FieldDiffEngine();
|
||||
|
||||
public ParameterDiffEngine(Path repoRoot, List<String> searchDirs,
|
||||
GitChangeScanner gitScanner, String oldSha, String newSha, int maxDepth) {
|
||||
this.nestedDtoFieldParser = new NestedDtoFieldParser(repoRoot, searchDirs, gitScanner, oldSha, newSha, maxDepth);
|
||||
}
|
||||
|
||||
public List<ParameterChange> diff(EndpointSnapshot oldSnap, EndpointSnapshot newSnap) throws IOException {
|
||||
List<ParameterChange> changes = new ArrayList<>();
|
||||
changes.addAll(diffBodyParams(oldSnap, newSnap));
|
||||
changes.addAll(diffBindingParams(oldSnap, newSnap));
|
||||
return changes;
|
||||
}
|
||||
|
||||
private List<ParameterChange> diffBodyParams(EndpointSnapshot oldSnap, EndpointSnapshot newSnap)
|
||||
throws IOException {
|
||||
Map<String, MethodParameterSnapshot> oldParams = filterBySource(oldSnap, "body");
|
||||
Map<String, MethodParameterSnapshot> newParams = filterBySource(newSnap, "body");
|
||||
List<ParameterChange> changes = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, MethodParameterSnapshot> entry : newParams.entrySet()) {
|
||||
MethodParameterSnapshot oldParam = oldParams.get(entry.getKey());
|
||||
MethodParameterSnapshot newParam = entry.getValue();
|
||||
if (oldParam == null) {
|
||||
changes.addAll(addedBodyChanges(newParam));
|
||||
} else {
|
||||
changes.addAll(diffBodyDto(oldParam, newParam));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, MethodParameterSnapshot> entry : oldParams.entrySet()) {
|
||||
if (!newParams.containsKey(entry.getKey())) {
|
||||
changes.addAll(removedBodyChanges(entry.getValue()));
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
private List<ParameterChange> diffBindingParams(EndpointSnapshot oldSnap, EndpointSnapshot newSnap) {
|
||||
Map<String, MethodParameterSnapshot> oldParams = filterBindingParams(oldSnap);
|
||||
Map<String, MethodParameterSnapshot> newParams = filterBindingParams(newSnap);
|
||||
List<ParameterChange> changes = new ArrayList<>();
|
||||
|
||||
List<MethodParameterSnapshot> unmatchedOld = new ArrayList<>();
|
||||
List<MethodParameterSnapshot> unmatchedNew = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, MethodParameterSnapshot> entry : newParams.entrySet()) {
|
||||
MethodParameterSnapshot oldParam = oldParams.get(entry.getKey());
|
||||
MethodParameterSnapshot newParam = entry.getValue();
|
||||
if (oldParam == null) {
|
||||
unmatchedNew.add(newParam);
|
||||
} else if (!oldParam.getType().equals(newParam.getType())) {
|
||||
changes.add(ParameterChange.modified(
|
||||
newParam.displayName(),
|
||||
newParam.getType(),
|
||||
newParam.getDescription(),
|
||||
oldParam.getType() + " → " + newParam.getType(),
|
||||
newParam.getSource(),
|
||||
null, null, null));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, MethodParameterSnapshot> entry : oldParams.entrySet()) {
|
||||
if (!newParams.containsKey(entry.getKey())) {
|
||||
unmatchedOld.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
pairRenamedBindingParams(unmatchedOld, unmatchedNew, changes);
|
||||
|
||||
for (MethodParameterSnapshot removed : unmatchedOld) {
|
||||
changes.add(ParameterChange.removed(
|
||||
removed.displayName(), removed.getType(), removed.getDescription(),
|
||||
removed.getSource(), null, null, null));
|
||||
}
|
||||
for (MethodParameterSnapshot added : unmatchedNew) {
|
||||
changes.add(ParameterChange.added(
|
||||
added.displayName(), added.getType(), added.getDescription(),
|
||||
added.getSource(), null, null, null));
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
/** 形参名+类型相同,仅绑定名变化 → 重命名 */
|
||||
private void pairRenamedBindingParams(List<MethodParameterSnapshot> unmatchedOld,
|
||||
List<MethodParameterSnapshot> unmatchedNew,
|
||||
List<ParameterChange> changes) {
|
||||
Set<MethodParameterSnapshot> pairedOld = new HashSet<>();
|
||||
Set<MethodParameterSnapshot> pairedNew = new HashSet<>();
|
||||
|
||||
for (MethodParameterSnapshot oldParam : unmatchedOld) {
|
||||
if (pairedOld.contains(oldParam)) {
|
||||
continue;
|
||||
}
|
||||
for (MethodParameterSnapshot newParam : unmatchedNew) {
|
||||
if (pairedNew.contains(newParam)) {
|
||||
continue;
|
||||
}
|
||||
if (!oldParam.getSource().equals(newParam.getSource())) {
|
||||
continue;
|
||||
}
|
||||
if (!oldParam.getName().equals(newParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
if (!oldParam.getType().equals(newParam.getType())) {
|
||||
continue;
|
||||
}
|
||||
changes.add(ParameterChange.renamed(
|
||||
oldParam.getBindingName(),
|
||||
newParam.getBindingName(),
|
||||
newParam.getType(),
|
||||
newParam.getDescription(),
|
||||
newParam.getSource(),
|
||||
null, null, null));
|
||||
pairedOld.add(oldParam);
|
||||
pairedNew.add(newParam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmatchedOld.removeIf(pairedOld::contains);
|
||||
unmatchedNew.removeIf(pairedNew::contains);
|
||||
}
|
||||
|
||||
private Map<String, MethodParameterSnapshot> filterBindingParams(EndpointSnapshot snap) {
|
||||
Map<String, MethodParameterSnapshot> map = new LinkedHashMap<>();
|
||||
for (MethodParameterSnapshot p : snap.getParameters()) {
|
||||
if (isBindingParam(p)) {
|
||||
map.put(p.identityKey(), p);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, MethodParameterSnapshot> filterBySource(EndpointSnapshot snap, String source) {
|
||||
Map<String, MethodParameterSnapshot> map = new LinkedHashMap<>();
|
||||
for (MethodParameterSnapshot p : snap.getParameters()) {
|
||||
if (source.equals(p.getSource())) {
|
||||
map.put(p.identityKey(), p);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private boolean isBindingParam(MethodParameterSnapshot param) {
|
||||
return "path".equals(param.getSource()) || "query".equals(param.getSource());
|
||||
}
|
||||
|
||||
private List<ParameterChange> diffBodyDto(MethodParameterSnapshot oldParam,
|
||||
MethodParameterSnapshot newParam) throws IOException {
|
||||
List<NestedFieldInfo> oldFields = nestedDtoFieldParser.parseNestedFieldsAtOldCommit(oldParam.getDtoClassName());
|
||||
List<NestedFieldInfo> newFields = nestedDtoFieldParser.parseNestedFieldsAtNewCommit(newParam.getDtoClassName());
|
||||
List<FieldChange> fieldChanges = fieldDiffEngine.diff(toFieldInfo(oldFields), toFieldInfo(newFields));
|
||||
List<ParameterChange> result = new ArrayList<>();
|
||||
for (FieldChange fc : fieldChanges) {
|
||||
result.add(mapFieldChange(fc, newParam.getName(), newParam.getDtoClassName()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ParameterChange mapFieldChange(FieldChange fc, String bodyParamName, String dtoName) {
|
||||
String path = fc.getFieldName();
|
||||
switch (fc.getKind()) {
|
||||
case ADDED:
|
||||
return ParameterChange.added(path, fc.getNewType(), fc.getDescription(),
|
||||
"body", bodyParamName, dtoName, path);
|
||||
case REMOVED:
|
||||
return ParameterChange.removed(path, fc.getOldType(), fc.getDescription(),
|
||||
"body", bodyParamName, dtoName, path);
|
||||
case RENAMED:
|
||||
return ParameterChange.renamed(fc.getOldFieldName(), fc.getFieldName(),
|
||||
fc.getNewType(), fc.getDescription(), "body", bodyParamName, dtoName, path);
|
||||
case MODIFIED:
|
||||
default:
|
||||
return ParameterChange.modified(path, fc.getNewType(), fc.getDescription(),
|
||||
fc.getDetail(), "body", bodyParamName, dtoName, path);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ParameterChange> addedBodyChanges(MethodParameterSnapshot param) throws IOException {
|
||||
List<ParameterChange> list = new ArrayList<>();
|
||||
for (NestedFieldInfo field : nestedDtoFieldParser.parseNestedFieldsAtNewCommit(param.getDtoClassName())) {
|
||||
list.add(ParameterChange.added(field.getPath(), field.getType(), field.getDescription(),
|
||||
"body", param.getName(), param.getDtoClassName(), field.getPath()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<ParameterChange> removedBodyChanges(MethodParameterSnapshot param) throws IOException {
|
||||
List<ParameterChange> list = new ArrayList<>();
|
||||
for (NestedFieldInfo field : nestedDtoFieldParser.parseNestedFieldsAtOldCommit(param.getDtoClassName())) {
|
||||
list.add(ParameterChange.removed(field.getPath(), field.getType(), field.getDescription(),
|
||||
"body", param.getName(), param.getDtoClassName(), field.getPath()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<FieldInfo> toFieldInfo(List<NestedFieldInfo> nested) {
|
||||
List<FieldInfo> result = new ArrayList<>();
|
||||
for (NestedFieldInfo info : nested) {
|
||||
result.add(new FieldInfo(info.getPath(), info.getType(), info.getDescription()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user