feat: 项目整体命名修改cache-schema-checker
All checks were successful
缓存序列化结构检查 / cache-schema-check (push) Has been skipped
All checks were successful
缓存序列化结构检查 / cache-schema-check (push) Has been skipped
This commit is contained in:
210
src/main/java/com/codechecker/cache/config/ConfigLoader.java
vendored
Normal file
210
src/main/java/com/codechecker/cache/config/ConfigLoader.java
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
package com.codechecker.cache.config;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 双层配置加载器:先读取 jar 内 {@code default-config.yaml},再与业务仓库配置深度合并(业务配置优先)。
|
||||
*/
|
||||
public final class ConfigLoader {
|
||||
|
||||
private static final String DEFAULT_CONFIG_RESOURCE = "default-config.yaml";
|
||||
|
||||
private ConfigLoader() {
|
||||
}
|
||||
|
||||
public static CheckerConfig load(Path businessConfigPath) {
|
||||
Map<String, Object> merged = loadDefault();
|
||||
if (businessConfigPath != null && Files.exists(businessConfigPath)) {
|
||||
Map<String, Object> business = loadYaml(businessConfigPath);
|
||||
merged = deepMerge(merged, business);
|
||||
}
|
||||
return bind(merged);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> loadDefault() {
|
||||
try (InputStream in = ConfigLoader.class.getClassLoader().getResourceAsStream(DEFAULT_CONFIG_RESOURCE)) {
|
||||
if (in == null) {
|
||||
throw new IllegalStateException("jar 内缺少 default-config.yaml");
|
||||
}
|
||||
Object obj = new Yaml().load(in);
|
||||
return obj == null ? new LinkedHashMap<>() : (Map<String, Object>) obj;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("读取默认配置失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> loadYaml(Path path) {
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
Object obj = new Yaml().load(in);
|
||||
return obj == null ? new LinkedHashMap<>() : (Map<String, Object>) obj;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("读取业务配置失败: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> deepMerge(Map<String, Object> base, Map<String, Object> override) {
|
||||
Map<String, Object> result = new LinkedHashMap<>(base);
|
||||
for (Map.Entry<String, Object> entry : override.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object overrideValue = entry.getValue();
|
||||
Object baseValue = result.get(key);
|
||||
if (baseValue instanceof Map && overrideValue instanceof Map) {
|
||||
result.put(key, deepMerge((Map<String, Object>) baseValue, (Map<String, Object>) overrideValue));
|
||||
} else {
|
||||
// 标量、列表:业务配置直接覆盖
|
||||
result.put(key, overrideValue);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static CheckerConfig bind(Map<String, Object> map) {
|
||||
CheckerConfig config = new CheckerConfig();
|
||||
|
||||
config.setEnabled(bool(map, "enabled", true));
|
||||
config.setMode(str(map, "mode", "notify"));
|
||||
config.setScanTestSources(bool(map, "scan_test_sources", false));
|
||||
config.setSourceRoots(strList(map.get("source_roots")));
|
||||
config.setIncludeModules(strList(map.get("include_modules")));
|
||||
config.setExcludeModules(strList(map.get("exclude_modules")));
|
||||
|
||||
Map<String, Object> notify = asMap(map.get("notify"));
|
||||
CheckerConfig.Notify n = config.getNotify();
|
||||
n.setEnabled(bool(notify, "enabled", true));
|
||||
n.setWebhookUrl(resolveWebhookUrl(notify));
|
||||
n.setNotifyOnClean(bool(notify, "notify_on_clean", false));
|
||||
n.setTitlePrefix(str(notify, "title_prefix", "[缓存结构变更]"));
|
||||
|
||||
Map<String, Object> ignore = asMap(map.get("ignore"));
|
||||
CheckerConfig.Ignore ig = config.getIgnore();
|
||||
ig.setKeyPatterns(strList(ignore.get("key_patterns")));
|
||||
ig.setFilePatterns(strList(ignore.get("file_patterns")));
|
||||
ig.setWriterMethods(strList(ignore.get("writer_methods")));
|
||||
|
||||
Map<String, Object> detection = asMap(map.get("detection"));
|
||||
CheckerConfig.Detection d = config.getDetection();
|
||||
d.setPatterns(strList(detection.get("patterns")));
|
||||
d.setMinConfidence(dbl(detection, "min_confidence", 0.6));
|
||||
d.setMaxFieldDepth((int) lng(detection, "max_field_depth", 8));
|
||||
|
||||
Map<String, Object> severityOverrides = asMap(map.get("severity_overrides"));
|
||||
Map<String, String> so = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> e : severityOverrides.entrySet()) {
|
||||
so.put(e.getKey(), String.valueOf(e.getValue()));
|
||||
}
|
||||
config.setSeverityOverrides(so);
|
||||
|
||||
List<CheckerConfig.ManualMapping> mappings = new ArrayList<>();
|
||||
for (Object item : asList(map.get("manual_mappings"))) {
|
||||
Map<String, Object> m = asMap(item);
|
||||
CheckerConfig.ManualMapping mm = new CheckerConfig.ManualMapping();
|
||||
mm.setId(str(m, "id", null));
|
||||
mm.setWriterMethod(str(m, "writer_method", null));
|
||||
mm.setKeyPattern(str(m, "key_pattern", null));
|
||||
mm.setValueType(str(m, "value_type", null));
|
||||
mm.setDescription(str(m, "description", null));
|
||||
mappings.add(mm);
|
||||
}
|
||||
config.setManualMappings(mappings);
|
||||
|
||||
List<CheckerConfig.Suppression> suppressions = new ArrayList<>();
|
||||
for (Object item : asList(map.get("suppressions"))) {
|
||||
Map<String, Object> m = asMap(item);
|
||||
CheckerConfig.Suppression sp = new CheckerConfig.Suppression();
|
||||
sp.setId(str(m, "id", null));
|
||||
sp.setWriterMethod(str(m, "writer_method", null));
|
||||
sp.setKeyPattern(str(m, "key_pattern", null));
|
||||
sp.setChangeTypes(strList(m.get("change_types")));
|
||||
sp.setReason(str(m, "reason", null));
|
||||
suppressions.add(sp);
|
||||
}
|
||||
config.setSuppressions(suppressions);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 优先读取 webhook_url;兼容旧字段 webhook_env(值为 http 开头时视为 URL)。
|
||||
*/
|
||||
private static String resolveWebhookUrl(Map<String, Object> notify) {
|
||||
String url = str(notify, "webhook_url", "");
|
||||
if (url != null && !url.trim().isEmpty()) {
|
||||
return url.trim();
|
||||
}
|
||||
String legacy = str(notify, "webhook_env", "");
|
||||
if (legacy != null && legacy.trim().startsWith("http")) {
|
||||
return legacy.trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> asMap(Object obj) {
|
||||
if (obj instanceof Map) {
|
||||
return (Map<String, Object>) obj;
|
||||
}
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
private static List<Object> asList(Object obj) {
|
||||
if (obj instanceof List) {
|
||||
return (List<Object>) obj;
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private static List<String> strList(Object obj) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (obj instanceof List) {
|
||||
for (Object o : (List<?>) obj) {
|
||||
if (o != null) {
|
||||
result.add(String.valueOf(o));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String str(Map<String, Object> map, String key, String def) {
|
||||
Object v = map.get(key);
|
||||
return v == null ? def : String.valueOf(v);
|
||||
}
|
||||
|
||||
private static boolean bool(Map<String, Object> map, String key, boolean def) {
|
||||
Object v = map.get(key);
|
||||
if (v instanceof Boolean) {
|
||||
return (Boolean) v;
|
||||
}
|
||||
return v == null ? def : Boolean.parseBoolean(String.valueOf(v));
|
||||
}
|
||||
|
||||
private static double dbl(Map<String, Object> map, String key, double def) {
|
||||
Object v = map.get(key);
|
||||
if (v instanceof Number) {
|
||||
return ((Number) v).doubleValue();
|
||||
}
|
||||
return v == null ? def : Double.parseDouble(String.valueOf(v));
|
||||
}
|
||||
|
||||
private static long lng(Map<String, Object> map, String key, long def) {
|
||||
Object v = map.get(key);
|
||||
if (v instanceof Number) {
|
||||
return ((Number) v).longValue();
|
||||
}
|
||||
return v == null ? def : Long.parseLong(String.valueOf(v));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user