项目结构变更
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
package com.codechecker.analyzer;
|
||||
|
||||
import com.codechecker.model.FieldChange;
|
||||
import com.codechecker.model.FieldInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 对比新旧字段列表,产出新增/删除/类型修改/重命名(纯注释变更忽略)。
|
||||
*/
|
||||
public class FieldDiffEngine {
|
||||
|
||||
/**
|
||||
* 按字段名对比;删除+新增且说明匹配时合并为重命名。
|
||||
* 输出顺序:按新字段声明顺序,未配对的删除字段置于末尾。
|
||||
*/
|
||||
public List<FieldChange> diff(List<FieldInfo> oldFields, List<FieldInfo> newFields) {
|
||||
Map<String, FieldInfo> oldMap = toMap(oldFields);
|
||||
Map<String, FieldInfo> newMap = toMap(newFields);
|
||||
|
||||
List<FieldChange> modified = new ArrayList<>();
|
||||
List<FieldInfo> added = new ArrayList<>();
|
||||
List<FieldInfo> removed = new ArrayList<>();
|
||||
|
||||
for (FieldInfo newField : newFields) {
|
||||
FieldInfo oldField = oldMap.get(newField.getName());
|
||||
if (oldField == null) {
|
||||
added.add(newField);
|
||||
} else if (!oldField.getType().equals(newField.getType())) {
|
||||
modified.add(FieldChange.modified(oldField, newField, buildTypeDetail(oldField, newField)));
|
||||
}
|
||||
// 仅 @Schema / 注释文案变化:不纳入字段变更
|
||||
}
|
||||
|
||||
for (FieldInfo oldField : oldFields) {
|
||||
if (!newMap.containsKey(oldField.getName())) {
|
||||
removed.add(oldField);
|
||||
}
|
||||
}
|
||||
|
||||
List<FieldChange> renamed = pairRenames(removed, added);
|
||||
return mergeInOrder(newFields, renamed, modified, added, removed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将删除+新增配对为字段重命名。
|
||||
* 优先:说明相同且类型相同;其次:说明相同但类型不同(重命名+改类型)。
|
||||
*/
|
||||
private List<FieldChange> pairRenames(List<FieldInfo> removed, List<FieldInfo> added) {
|
||||
List<FieldChange> renames = new ArrayList<>();
|
||||
Set<FieldInfo> matchedRemoved = new LinkedHashSet<>();
|
||||
Set<FieldInfo> matchedAdded = new LinkedHashSet<>();
|
||||
|
||||
for (FieldInfo oldField : removed) {
|
||||
FieldInfo pair = findRenamePair(oldField, added, matchedAdded, true);
|
||||
if (pair == null) {
|
||||
pair = findRenamePair(oldField, added, matchedAdded, false);
|
||||
}
|
||||
if (pair != null) {
|
||||
renames.add(FieldChange.renamed(oldField, pair));
|
||||
matchedRemoved.add(oldField);
|
||||
matchedAdded.add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
removed.removeIf(matchedRemoved::contains);
|
||||
added.removeIf(matchedAdded::contains);
|
||||
return renames;
|
||||
}
|
||||
|
||||
private FieldInfo findRenamePair(FieldInfo removed, List<FieldInfo> added,
|
||||
Set<FieldInfo> excluded, boolean requireSameType) {
|
||||
for (FieldInfo candidate : added) {
|
||||
if (excluded.contains(candidate)) {
|
||||
continue;
|
||||
}
|
||||
if (!descriptionsMatch(removed, candidate)) {
|
||||
continue;
|
||||
}
|
||||
if (requireSameType && !removed.getType().equals(candidate.getType())) {
|
||||
continue;
|
||||
}
|
||||
if (!requireSameType && removed.getType().equals(candidate.getType())) {
|
||||
continue;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 说明相同(非空)或双方均为空时视为匹配 */
|
||||
private boolean descriptionsMatch(FieldInfo oldField, FieldInfo newField) {
|
||||
String oldDesc = normalizeDescription(oldField.getDescription());
|
||||
String newDesc = normalizeDescription(newField.getDescription());
|
||||
if (oldDesc.isEmpty() && newDesc.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (oldDesc.isEmpty() || newDesc.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return oldDesc.equals(newDesc);
|
||||
}
|
||||
|
||||
private String normalizeDescription(String description) {
|
||||
return description == null ? "" : description.trim();
|
||||
}
|
||||
|
||||
/** 按新字段声明顺序合并各变更类型 */
|
||||
private List<FieldChange> mergeInOrder(List<FieldInfo> newFields, List<FieldChange> renamed,
|
||||
List<FieldChange> modified, List<FieldInfo> added,
|
||||
List<FieldInfo> removed) {
|
||||
Map<String, FieldChange> renamedByNewName = new LinkedHashMap<>();
|
||||
for (FieldChange change : renamed) {
|
||||
renamedByNewName.put(change.getFieldName(), change);
|
||||
}
|
||||
|
||||
Map<String, FieldChange> modifiedByName = new LinkedHashMap<>();
|
||||
for (FieldChange change : modified) {
|
||||
modifiedByName.put(change.getFieldName(), change);
|
||||
}
|
||||
|
||||
Set<String> emitted = new LinkedHashSet<>();
|
||||
List<FieldChange> result = new ArrayList<>();
|
||||
|
||||
for (FieldInfo newField : newFields) {
|
||||
String name = newField.getName();
|
||||
if (renamedByNewName.containsKey(name)) {
|
||||
result.add(renamedByNewName.get(name));
|
||||
emitted.add(name);
|
||||
} else if (modifiedByName.containsKey(name)) {
|
||||
result.add(modifiedByName.get(name));
|
||||
emitted.add(name);
|
||||
} else if (added.stream().anyMatch(f -> f.getName().equals(name))) {
|
||||
result.add(FieldChange.added(newField));
|
||||
emitted.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
for (FieldInfo oldField : removed) {
|
||||
result.add(FieldChange.removed(oldField));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 字段列表转 LinkedHashMap,保持声明顺序 */
|
||||
private Map<String, FieldInfo> toMap(List<FieldInfo> fields) {
|
||||
Map<String, FieldInfo> map = new LinkedHashMap<>();
|
||||
for (FieldInfo field : fields) {
|
||||
map.put(field.getName(), field);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/** 构造类型变化描述,如 Integer → String */
|
||||
private String buildTypeDetail(FieldInfo oldField, FieldInfo newField) {
|
||||
if (oldField.getType().equals(newField.getType())) {
|
||||
return "";
|
||||
}
|
||||
return oldField.getType() + " → " + newField.getType();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user