diff --git a/features/section/src/main/java/cc/carm/lib/configuration/source/section/ShadedSection.java b/features/section/src/main/java/cc/carm/lib/configuration/source/section/ShadedSection.java index 28d9a45..1e21cf3 100644 --- a/features/section/src/main/java/cc/carm/lib/configuration/source/section/ShadedSection.java +++ b/features/section/src/main/java/cc/carm/lib/configuration/source/section/ShadedSection.java @@ -4,66 +4,122 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; +import java.util.function.Supplier; public class ShadedSection implements ConfigureSection { - protected final @NotNull ConfigureSection section; - protected final @NotNull ConfigureSection template; + protected @NotNull ConfigureSection template; + protected @Nullable ConfigureSection source; - public ShadedSection(@NotNull ConfigureSection section, @Nullable ConfigureSection template) { - this.section = section; + public ShadedSection(@NotNull ConfigureSection template, @Nullable ConfigureSection source) { this.template = template; + this.source = source; } @Override - public @Nullable ShadedSection parent() { + public @Nullable ConfigureSection parent() { return null; } @Override public @NotNull @UnmodifiableView Map getValues(boolean deep) { - if (deep) { - Map values = new LinkedHashMap<>(template.getValues(true)); - values.putAll(section.getValues(true)); - return values; + if (source == null) { + return template.getValues(deep); } + // 本函数为,当 getValues 时,递归合并 source 和 template + return merge(template, source).getValues(deep); + } - return Collections.emptyMap(); + private ConfigureSection merge(ConfigureSection templateSection, ConfigureSection valueSection) { + MemorySection merged = MemorySection.of(); + List existingKey = new ArrayList<>(); + + for (Map.Entry entry : valueSection.getValues(false).entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof ConfigureSection) { + ConfigureSection subSectionFromValue = (ConfigureSection) value; + ConfigureSection subSectionFromTemplate = (ConfigureSection) templateSection.get(key); + if(subSectionFromTemplate == null) { + merged.set(key, value); + } else { + merged.set(key, merge(subSectionFromTemplate, subSectionFromValue)); + } + } else { + merged.set(key, value); + } + existingKey.add(key); + } + + for (Map.Entry entry : templateSection.getValues(false).entrySet()) { + if (existingKey.contains(entry.getKey())) { + continue; + } + merged.set(entry.getKey(), entry.getValue()); + } + + return merged; } @Override public void set(@NotNull String path, @Nullable Object value) { - if (value instanceof ConfigureSection) { - + ConfigureSection targetSection = (ConfigureSection) value; + for (Map.Entry entry : targetSection.getValues(true).entrySet()) { + set(path+pathSeparator()+entry.getKey(), entry.getValue()); + } + return; + } else if (Objects.equals(get(path), value)){ + remove(path); + return; } - } - - @Override - public @Nullable Object get(@NotNull String path) { - - if (section.contains(path)) { - - - return section.get(path); - } - - return null; + Optional.ofNullable(source).ifPresent(source -> source.set(path, value)); } @Override public void remove(@NotNull String path) { - section.remove(path); + Optional.ofNullable(source).ifPresent(source -> source.remove(path)); } @Override public @NotNull ConfigureSection createSection(@NotNull Map data) { - return null; + throw new UnsupportedOperationException("not supported yet"); } + @Override + public @Nullable Object get(@NotNull String path) { + if (source == null) { + return getFromTemplate(path); + } + + Object value = source.get(path); + if (value == null) { + return getFromTemplate(path); + } + if (value instanceof ConfigureSection) { + ConfigureSection templateSection = (ConfigureSection) template.get(path); + if (templateSection == null) { + return value; + } else { + return new ShadedSection(templateSection, (ConfigureSection) value); + } + } + return value; + } + + public @Nullable Object getFromTemplate(@NotNull String path) { + Object value = template.get(path); + if (value instanceof ConfigureSection) { + return new ShadedSection((ConfigureSection) value, null); + } else { + return value; + } + } + + public ConfigureSection getSource() { + return source; + } } \ No newline at end of file diff --git a/features/section/src/test/java/test/section/ShadeTest.java b/features/section/src/test/java/test/section/ShadeTest.java index b1a182e..acfe9b9 100644 --- a/features/section/src/test/java/test/section/ShadeTest.java +++ b/features/section/src/test/java/test/section/ShadeTest.java @@ -2,6 +2,7 @@ package test.section; import cc.carm.lib.configuration.source.section.ConfigureSection; import cc.carm.lib.configuration.source.section.MemorySection; +import cc.carm.lib.configuration.source.section.ShadedSection; import org.junit.Test; import java.util.Arrays; @@ -22,7 +23,10 @@ public class ShadeTest { data.put("gender", "male"); Map address = new LinkedHashMap<>(); address.put("Hotel", "Nanjing Road 101"); - address.put("Parent", "BeijingRoad 404"); + address.put("Store", "Beijing Road 404"); + Map mapInside = new LinkedHashMap<>(); + mapInside.put("InsideKeyExample", "InsideValueExample"); + address.put("Inside", mapInside); data.put("addresses", address); data.put("cards", Arrays.asList("00000", "11111", "22222")); }); @@ -31,13 +35,43 @@ public class ShadeTest { Map address = new LinkedHashMap<>(); address.put("NewOne", "Guangdong Road 505"); data.put("addresses", address); + Map mapInside = new LinkedHashMap<>(); + mapInside.put("AnotherInsideKey", "AnotherInsideValue"); + address.put("Inside", mapInside); data.put("cards", Arrays.asList("33333", "55555")); // 应当直接覆盖原先的List }); + ShadedSection root = new ShadedSection(template, source); + System.out.println("age: "+root.get("age")); + System.out.println("addresses: "); + for (Map.Entry entry : root.getSection("addresses").getValues(false).entrySet()) { + System.out.println(" "+entry.getKey()+": "+entry.getValue()); + if (entry.getValue() instanceof ConfigureSection) { + for(Map.Entry inner : ((ConfigureSection) entry.getValue()).getValues(false).entrySet()) { + System.out.println(" "+inner.getKey()+": "+inner.getValue()); + } + } + } + System.out.println("cards: "+root.getList("cards")); + System.out.println("\n----------------------\n"); + System.out.println("Deep Search Test"); + System.out.println("addresses: "); + for (Map.Entry entry : root.getSection("addresses").getValues(true).entrySet()) { + System.out.println(" "+entry.getKey()+": "+entry.getValue()); + } + System.out.println("\n----------------------\n"); + root.set("addresses", MemorySection.of(map->{ + map.put("Hotel", "Nanjing Road 101"); + map.put("Store", "Beijing Road banned"); + })); + + for (Map.Entry entry : source.getValues(true).entrySet()) { + System.out.println(" "+entry.getKey()+": "+entry.getValue()); + } } }