diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/Kv.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/Kv.java new file mode 100644 index 00000000..53dd364f --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/base/Kv.java @@ -0,0 +1,221 @@ +package fun.asgc.neutrino.core.base; + + +import java.io.Serializable; +import java.util.*; + +/** + * 用于统一参数配置 + * 1、有序性(适用于方法参数) + * 2、key为String类型时,忽略大小写、忽略分隔符(_、-),兼容启动命令传参 + 配置文件传参 + * 3、父级委托机制:如果存在父级,且当前实例查不到,则委托父级查找。(适用于配置优先级:启动参数 > 配置文件 > 默认值) + * @author: aoshiguchen + * @date: 2023/3/1 + */ +public class Kv implements Map , Serializable, Cloneable { + private LinkedHashMap _m; + private HashMap _k; + private Locale locale; + private String SEPARATOR = "_|-"; + private Kv parent; + + private Kv() { + this(null, 16, null); + } + + private Kv(Kv parent) { + this(parent, 16, null); + } + + public static Kv of() { + return new Kv<>(); + } + + public static Kv of(Kv parent) { + return new Kv(parent); + } + + public Kv(Kv parent, int initialCapacity, Locale locale) { + this.parent = parent; + this._m = new LinkedHashMap(initialCapacity) { + @Override + public boolean containsKey(Object key) { + return Kv.this.containsKey(key); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + boolean doRemove = Kv.this.removeEldestEntry(eldest); + if (doRemove) { + if (eldest.getKey() instanceof String) { + _k.remove(convertKey((String) eldest.getKey())); + } else { + _k.remove(eldest.getKey()); + } + } + return doRemove; + } + }; + this._k = new HashMap<>(initialCapacity); + this.locale = (locale != null ? locale : Locale.getDefault()); + } + + @Override + public int size() { + return this._m.size(); + } + + public int stackSize() { + return this._m.size() + (null == parent ? 0 : parent.stackSize()); + } + + @Override + public boolean isEmpty() { + return this._m.isEmpty(); + } + + public boolean isStackEmpty() { + boolean res = this._m.isEmpty(); + if (res && null != parent) { + res = parent.isStackEmpty(); + } + return res; + } + + @Override + public boolean containsKey(Object key) { + boolean convertKey = false; + if (key instanceof String) { + convertKey = this._k.containsKey(convertKey((String) key)); + } else { + convertKey = this._k.containsKey(key); + } + return convertKey; + } + + public boolean stackContainsKey(K k) { + return this.containsKey(k) || (null != parent && parent.containsKey(k)); + } + + @Override + public boolean containsValue(Object value) { + return this._m.containsValue(value); + } + + public boolean stackContainsValue(Object value) { + return this.containsValue(value) || (null != parent && parent.containsValue(value)); + } + + @Override + public V get(Object key) { + K k = null; + try { + k = (K) key; + } catch (Exception e) { + return null; + } + if (k instanceof String) { + k = this._k.get(convertKey((String) k)); + } + if (null != k) { + return this._m.get(k); + } + return null; + } + + public V stackGet(K k) { + V res = get(k); + if (null == res && null != parent) { + res = parent.stackGet(k); + } + return res; + } + + @Override + public V put(K key, V value) { + K k = key; + if (null != k && k instanceof String) { + k = (K) convertKey((String) key); + } + K oldKey = this._k.put(k, key); + if (null != oldKey && !oldKey.equals(key)) { + this._m.remove(oldKey); + } + return this._m.put(key, value); + } + + @Override + public V remove(Object key) { + K k1 = null; + try { + k1 = (K) key; + } catch (Exception e) { + return null; + } + K k2 = null; + if (k1 instanceof String) { + k1 = (K) convertKey((String) k1); + k2 = this._k.remove(k1); + } else { + k2 = this._k.remove(k1); + } + if (null != k2) { + return this._m.remove(k2); + } + return null; + } + + @Override + public void putAll(Map m) { + if (m.isEmpty()) { + return; + } + m.forEach(this::put); + } + + @Override + public void clear() { + this._k.clear(); + this._m.clear(); + } + + @Override + public Set keySet() { + return this._m.keySet(); + } + + @Override + public Collection values() { + return this._m.values(); + } + + @Override + public Set> entrySet() { + return this._m.entrySet(); + } + + public Locale getLocale() { + return this.locale; + } + + protected String convertKey(String key) { + return key.toLowerCase(getLocale()).replaceAll(SEPARATOR, ""); + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return false; + } + + @Override + protected Kv clone() { + Kv res = new Kv<>(); + if (null != parent) { + res.parent = parent.clone(); + } + res._m = (LinkedHashMap) _m.clone(); + res._k = (HashMap) _k.clone(); + res.locale = locale; + return res; + } + +} diff --git a/neutrino-core/src/test/java/fun/asgc/neutrino/core/base/KvTest.java b/neutrino-core/src/test/java/fun/asgc/neutrino/core/base/KvTest.java new file mode 100644 index 00000000..2c4dc879 --- /dev/null +++ b/neutrino-core/src/test/java/fun/asgc/neutrino/core/base/KvTest.java @@ -0,0 +1,49 @@ +package fun.asgc.neutrino.core.base; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author: aoshiguchen + * @date: 2023/3/1 + */ +public class KvTest { + @Test + public void test1() { + Kv kv1 = Kv.of(); + kv1.put("app.server.http.http-port", 8080); + kv1.put("app.server.http.jks_Path", "/123/456"); + + Kv kv2 = Kv.of(kv1); + kv2.put("app.server.http.httpPort", 8081); + kv2.put("app.server.http.context-path", "/"); + + Kv kv3 = Kv.of(kv2); + + Assert.assertTrue(!kv1.isEmpty()); + Assert.assertTrue(kv1.size() == 2); + Assert.assertTrue(kv1.stackSize() == 2); + Assert.assertTrue(kv1.get("app.server.http.http-port").equals(8080)); + Assert.assertTrue(kv1.get("app.server.http.httpPort").equals(8080)); + Assert.assertTrue(kv1.get("app.server.http.jks_Path").equals("/123/456")); + Assert.assertTrue(kv1.get("app.server.http.jksPath").equals("/123/456")); + + Assert.assertTrue(!kv2.isEmpty()); + Assert.assertTrue(kv2.size() == 2); + Assert.assertTrue(kv2.stackSize() == 4); + Assert.assertTrue(kv2.get("app.server.http.http-port").equals(8081)); + Assert.assertTrue(kv2.get("app.server.http.httpPort").equals(8081)); + Assert.assertTrue(kv2.get("app.server.http.jks-path") == null); + Assert.assertTrue(kv2.get("app.server.http.jksPath") == null); + Assert.assertTrue(kv2.get("app.server.http.context-path").equals("/")); + + Assert.assertTrue(kv2.stackGet("app.server.http.jks-path").equals("/123/456")); + Assert.assertTrue(kv2.stackGet("app.server.http.jksPath").equals("/123/456")); + Assert.assertTrue(kv2.stackGet("app.server.http.context-path").equals("/")); + + Assert.assertTrue(kv3.isEmpty()); + Assert.assertTrue(!kv3.isStackEmpty()); + Assert.assertTrue(kv3.size() == 0); + Assert.assertTrue(kv3.stackSize() == 4); + } +}