1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-04 10:38:19 +08:00

feat(section): Add #path and #fullPath for sections

This commit is contained in:
2025-02-25 00:04:24 +08:00
parent 842cd78ce3
commit 6f28abebb9
20 changed files with 181 additions and 66 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.6</version> <version>4.0.7</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -8,6 +8,7 @@ import org.jetbrains.annotations.UnmodifiableView;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -30,6 +31,24 @@ public interface ConfigureSection {
@Contract(pure = true) @Contract(pure = true)
@Nullable ConfigureSection parent(); @Nullable ConfigureSection parent();
/**
* Get the current section's path from {@link #parent()} of this section.
*
* @return The current path of this section, if {@link #isRoot()}, return empty string.
*/
@NotNull String path();
/**
* Get the full path of this section.
*
* @return The full path of this section, if {@link #isRoot()}, return empty string.
*/
default @NotNull String fullPath() {
if (parent() == null) return "";
return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path();
}
/** /**
* Get the path separator for the section. * Get the path separator for the section.
* *
@@ -44,6 +63,7 @@ public interface ConfigureSection {
* *
* @return True if this section is a root section, false otherwise. * @return True if this section is a root section, false otherwise.
*/ */
@Contract(pure = true)
default boolean isRoot() { default boolean isRoot() {
return parent() == null; return parent() == null;
} }
@@ -57,6 +77,15 @@ public interface ConfigureSection {
return getValues(false).isEmpty(); return getValues(false).isEmpty();
} }
/**
* Gets the number of keys in this section.
*
* @return Number of keys in this section.
*/
default int size(boolean deep) {
return getKeys(deep).size();
}
/** /**
* Gets a set containing all keys in this section. * Gets a set containing all keys in this section.
* <p> * <p>
@@ -238,11 +267,39 @@ public interface ConfigureSection {
/** /**
* Creates a new {@link ConfigureSection} with specified values. * Creates a new {@link ConfigureSection} with specified values.
* <p> The {@link #parent()} of the new section will be this section. * <p> The {@link #parent()} of the new section will be this section.
* <p> This section will not be saved until {@link #set(String, Object)} is called.
* <p> If you want to create and use a section and set it to this section, use {@link #computeSection(String)}.
* *
* @param data The data to be used to create section. * @param data The data to be used to create section.
* @return Newly created section * @return Newly created section
*/ */
@NotNull ConfigureSection createSection(@NotNull Map<?, ?> data); @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data);
/**
* Creates a new {@link ConfigureSection} with specified values.
* <p> The {@link #parent()} of the new section will be this section.
*
* @param data The data to be used to create section.
* @return Newly created section
*/
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
return createSection(path, () -> {
Map<String, Object> map = new LinkedHashMap<>();
data.accept(map);
return map;
});
}
/**
* Creates a new {@link ConfigureSection} with specified values.
* <p> The {@link #parent()} of the new section will be this section.
*
* @param data The data to be used to create section.
* @return Newly created section
*/
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
return createSection(path, data.get());
}
/** /**
* Creates a new empty {@link ConfigureSection}. * Creates a new empty {@link ConfigureSection}.
@@ -250,8 +307,8 @@ public interface ConfigureSection {
* *
* @return Newly created section * @return Newly created section
*/ */
default @NotNull ConfigureSection createSection() { default @NotNull ConfigureSection createSection(@NotNull String path) {
return createSection(new LinkedHashMap<>()); return createSection(path, new LinkedHashMap<>());
} }
/** /**
@@ -262,17 +319,64 @@ public interface ConfigureSection {
* *
* @param path The path to get the section from. * @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist. * @return The section at the path, or a new section if it does not exist.
* @see #createSection() * @see #createSection(String, Map)
*/ */
default @NotNull ConfigureSection computeSection(@NotNull String path) { default @NotNull ConfigureSection computeSection(@NotNull String path) {
return computeSection(path, new LinkedHashMap<>());
}
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Map<?, ?> data) {
ConfigureSection current = getSection(path); ConfigureSection current = getSection(path);
if (current == null) { if (current == null) {
current = createSection(); current = createSection(path, data);
set(path, current); set(path, current);
} }
return current; return current;
} }
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
return computeSection(path, () -> {
Map<String, Object> map = new LinkedHashMap<>();
data.accept(map);
return map;
});
}
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
return computeSection(path, data.get());
}
/** /**
* Get the origin value of the path. * Get the origin value of the path.
* *
@@ -92,6 +92,11 @@ public abstract class ConfigureSource<
return null; return null;
} }
@Override
public @NotNull String path() {
return "";
}
@Override @Override
public @NotNull Map<String, Object> getValues(boolean deep) { public @NotNull Map<String, Object> getValues(boolean deep) {
return section().getValues(deep); return section().getValues(deep);
@@ -103,8 +108,8 @@ public abstract class ConfigureSource<
} }
@Override @Override
public @NotNull ConfigureSection createSection(@NotNull Map<?, ?> data) { public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return section().createSection(data); return section().createSection(path, data);
} }
@Override @Override
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.6</version> <version>4.0.7</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -36,6 +36,7 @@ public class UserRecord extends AbstractRecord {
} }
public static UserRecord deserialize(ConfigureSection section) { public static UserRecord deserialize(ConfigureSection section) {
System.out.println("> Deserializing -> " + section.fullPath());
String name = section.getString("name"); String name = section.getString("name");
if (name == null) throw new NullPointerException("name is null"); if (name == null) throw new NullPointerException("name is null");
String uuidString = section.getString("info.uuid"); String uuidString = section.getString("info.uuid");
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -10,9 +10,11 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
protected final @NotNull Map<String, Object> data; protected final @NotNull Map<String, Object> data;
protected final @Nullable R parent; protected final @Nullable R parent;
protected final @NotNull String path;
protected AbstractMapSection(@Nullable R parent) { protected AbstractMapSection(@Nullable R parent, @NotNull String path) {
this.parent = parent; this.parent = parent;
this.path = path;
this.data = new LinkedHashMap<>(); this.data = new LinkedHashMap<>();
} }
@@ -20,15 +22,17 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
for (Map.Entry<?, ?> entry : data.entrySet()) { for (Map.Entry<?, ?> entry : data.entrySet()) {
String key = (entry.getKey() == null) ? "" : entry.getKey().toString(); String key = (entry.getKey() == null) ? "" : entry.getKey().toString();
if (entry.getValue() instanceof Map) { if (entry.getValue() instanceof Map) {
this.data.put(key, createSection((Map<?, ?>) entry.getValue())); this.data.put(key, createSection(key, (Map<?, ?>) entry.getValue()));
} else if (entry.getValue() instanceof List) { } else if (entry.getValue() instanceof List) {
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
int index = 0;
for (Object obj : (List<?>) entry.getValue()) { for (Object obj : (List<?>) entry.getValue()) {
if (obj instanceof Map) { if (obj instanceof Map) {
list.add(createSection((Map<?, ?>) obj)); list.add(createSection(key + "[" + index + "]", (Map<?, ?>) obj));
} else { } else {
list.add(obj); list.add(obj);
} }
index++;
} }
this.data.put(key, list); this.data.put(key, list);
} else { } else {
@@ -40,11 +44,11 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
public abstract @NotNull R self(); public abstract @NotNull R self();
@Override @Override
public abstract @NotNull R createSection(@NotNull Map<?, ?> data); public abstract @NotNull R createSection(@NotNull String path, @NotNull Map<?, ?> data);
@Override @Override
public @NotNull R createSection() { public @NotNull String path() {
return createSection(new LinkedHashMap<>()); return this.path;
} }
@Override @Override
@@ -63,7 +67,12 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return data.isEmpty(); return this.data.isEmpty();
}
@Override
public int size(boolean deep) {
return deep ? getKeys(true).size() : this.data.size();
} }
@UnmodifiableView @UnmodifiableView
@@ -86,12 +95,12 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
@Override @Override
public @NotNull Map<String, Object> getValues(boolean deep) { public @NotNull Map<String, Object> getValues(boolean deep) {
return Collections.unmodifiableMap(deep ? mappingValues(this, null, true) : data()); return Collections.unmodifiableMap(deep ? mappingValues(this, null, true, String.valueOf(pathSeparator())) : data());
} }
@Override @Override
public void set(@NotNull String path, @Nullable Object value) { public void set(@NotNull String path, @Nullable Object value) {
if (value instanceof Map) value = createSection((Map<?, ?>) value); if (value instanceof Map) value = createSection(path, (Map<?, ?>) value);
R section = getSectionFor(path); R section = getSectionFor(path);
if (section == this) { if (section == this) {
@@ -125,7 +134,7 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
if (index == -1) return self(); if (index == -1) return self();
String root = path.substring(0, index); String root = path.substring(0, index);
return (R) data().computeIfAbsent(root, k -> createSection()); return (R) computeSection(root);
} }
/** /**
@@ -135,14 +144,14 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
* @param parent The parent path * @param parent The parent path
* @param deep If the mapping should be deep * @param deep If the mapping should be deep
*/ */
protected Map<String, Object> mappingValues(@NotNull AbstractMapSection<?> section, @Nullable String parent, boolean deep) { protected static Map<String, Object> mappingValues(@NotNull AbstractMapSection<?> section, @Nullable String parent, boolean deep, String pathSeparator) {
Map<String, Object> output = new LinkedHashMap<>(); Map<String, Object> output = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : section.data().entrySet()) { for (Map.Entry<String, Object> entry : section.data().entrySet()) {
String path = (parent == null ? "" : parent + pathSeparator()) + entry.getKey(); String path = (parent == null ? "" : parent + pathSeparator) + entry.getKey();
output.remove(path); output.remove(path);
output.put(path, entry.getValue()); output.put(path, entry.getValue());
if (deep && entry.getValue() instanceof AbstractMapSection<?>) { if (deep && entry.getValue() instanceof AbstractMapSection<?>) {
output.putAll(mappingValues((AbstractMapSection<?>) entry.getValue(), path, true)); output.putAll(mappingValues((AbstractMapSection<?>) entry.getValue(), path, true, pathSeparator));
} }
} }
return output; return output;
@@ -32,6 +32,11 @@ public class ImmutableSection implements ConfigureSection {
return this.parent; return this.parent;
} }
@Override
public @NotNull String path() {
return section().path();
}
private @NotNull ConfigureSection section() { private @NotNull ConfigureSection section() {
return section; return section;
} }
@@ -52,8 +57,8 @@ public class ImmutableSection implements ConfigureSection {
} }
@Override @Override
public @NotNull ConfigureSection createSection(@NotNull Map<?, ?> data) { public @NotNull ImmutableSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return new ImmutableSection(this, section().createSection(data)); return new ImmutableSection(this, section().createSection(path, data));
} }
@Override @Override
@@ -11,7 +11,7 @@ import java.util.function.Supplier;
public class MemorySection extends AbstractMapSection<MemorySection> { public class MemorySection extends AbstractMapSection<MemorySection> {
public static MemorySection of() { public static MemorySection of() {
return of((MemorySection) null); return of(new LinkedHashMap<>());
} }
public static MemorySection of(@NotNull Consumer<Map<String, Object>> data) { public static MemorySection of(@NotNull Consumer<Map<String, Object>> data) {
@@ -23,23 +23,23 @@ public class MemorySection extends AbstractMapSection<MemorySection> {
} }
public static MemorySection of(@NotNull Supplier<Map<?, ?>> data) { public static MemorySection of(@NotNull Supplier<Map<?, ?>> data) {
return of(data.get(), null); return of(data.get());
} }
public static MemorySection of(@NotNull Map<?, ?> data) { public static MemorySection of(@NotNull Map<?, ?> data) {
return of(data, null); return of(data, null, "");
} }
public static MemorySection of(@Nullable MemorySection parent) { public static MemorySection of(@Nullable MemorySection parent, @NotNull String path) {
return of(new LinkedHashMap<>(), parent); return of(new LinkedHashMap<>(), parent, path);
} }
public static MemorySection of(@NotNull Map<?, ?> data, @Nullable MemorySection parent) { public static MemorySection of(@NotNull Map<?, ?> data, @Nullable MemorySection parent, @NotNull String path) {
return new MemorySection(data, parent); return new MemorySection(data, parent, path);
} }
public MemorySection(@NotNull Map<?, ?> raw, @Nullable MemorySection parent) { public MemorySection(@NotNull Map<?, ?> raw, @Nullable MemorySection parent, @NotNull String path) {
super(parent); super(parent, path);
migrate(raw); migrate(raw);
} }
@@ -49,8 +49,8 @@ public class MemorySection extends AbstractMapSection<MemorySection> {
} }
@Override @Override
public @NotNull MemorySection createSection(@NotNull Map<?, ?> data) { public @NotNull MemorySection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return new MemorySection(data, this); return new MemorySection(data, this, path);
} }
} }
@@ -28,6 +28,11 @@ public class ShadedSection implements ConfigureSection {
return this.parent; return this.parent;
} }
@Override
public @NotNull String path() {
return this.template.path();
}
@Override @Override
public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) { public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) {
if (source == null) return template.getValues(deep); if (source == null) return template.getValues(deep);
@@ -95,11 +100,11 @@ public class ShadedSection implements ConfigureSection {
} }
@Override @Override
public @NotNull ConfigureSection createSection(@NotNull Map<?, ?> data) { public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
if (source == null) { if (source == null) {
return new ShadedSection(this, template, MemorySection.of(data)); return new ShadedSection(this, template, MemorySection.of(data));
} else { } else {
ConfigureSection section = source.createSection(data); ConfigureSection section = source.computeSection(path, data);
return new ShadedSection(this, template, section); return new ShadedSection(this, template, section);
} }
} }
@@ -9,19 +9,19 @@ import java.util.Map;
public class SourcedSection extends AbstractMapSection<SourcedSection> { public class SourcedSection extends AbstractMapSection<SourcedSection> {
public static @NotNull SourcedSection root(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source) { public static @NotNull SourcedSection root(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source) {
return new SourcedSection(source, new LinkedHashMap<>(), null); return new SourcedSection(source, new LinkedHashMap<>(), null, "");
} }
public static @NotNull SourcedSection root(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source, public static @NotNull SourcedSection root(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source,
@Nullable Map<?, ?> raw) { @Nullable Map<?, ?> raw) {
return new SourcedSection(source, raw == null ? new LinkedHashMap<>() : raw, null); return new SourcedSection(source, raw == null ? new LinkedHashMap<>() : raw, null, "");
} }
protected final @NotNull ConfigureSource<? extends SourcedSection, ?, ?> source; protected final @NotNull ConfigureSource<? extends SourcedSection, ?, ?> source;
public SourcedSection(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source, public SourcedSection(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source,
@NotNull Map<?, ?> raw, @Nullable SourcedSection parent) { @NotNull Map<?, ?> raw, @Nullable SourcedSection parent, @NotNull String path) {
super(parent); super(parent, path);
this.source = source; this.source = source;
migrate(raw); migrate(raw);
} }
@@ -41,8 +41,8 @@ public class SourcedSection extends AbstractMapSection<SourcedSection> {
} }
@Override @Override
public @NotNull SourcedSection createSection(@NotNull Map<?, ?> data) { public @NotNull SourcedSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return new SourcedSection(source(), data, this); return new SourcedSection(source(), data, this, path);
} }
} }
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+1 -7
View File
@@ -15,7 +15,7 @@
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>4.0.6</version> <version>4.0.7</version>
<modules> <modules>
<module>core</module> <module>core</module>
<module>features/section</module> <module>features/section</module>
@@ -89,12 +89,6 @@
<url>https://repo1.maven.org/maven2/</url> <url>https://repo1.maven.org/maven2/</url>
</repository> </repository>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/*</url>
</repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -8
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.6</version> <version>4.0.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -29,13 +29,6 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>yamlcommentwriter</artifactId>
<version>1.2.0</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>${project.parent.groupId}</groupId> <groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-feature-file</artifactId> <artifactId>easyconfiguration-feature-file</artifactId>
@@ -56,7 +56,7 @@ public class YAMLSource
@Override @Override
protected @NotNull YAMLSource self() { protected @NotNull YAMLSource self() {
return null; return this;
} }
@Override @Override
@@ -9,7 +9,6 @@ import org.junit.Test;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern;
public class YamlTests { public class YamlTests {