From 4fd80faec2a2c7bcb5bae808c911cf0ce8ecc042 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Tue, 4 Jul 2023 16:06:22 +0200 Subject: [PATCH] #18 Export document via SnakeYAML nodes (draft) - Fix issues with new lines - Remove unused code --- .../resource/PropertyPathTraverser.java | 114 ++++---- .../configme/resource/YamlFileResource.java | 254 +++--------------- .../resource/YamlFileResourceOptions.java | 14 - .../jalu/configme/utils/CollectionUtils.java | 8 - .../beanmapper/BeanMapOnRootLevelTest.java | 3 + .../BeanWithCollectionOfBeanTypeTest.java | 8 +- .../resource/PropertyPathTraverserTest.java | 106 +++++--- .../resource/YamlFileResourceNewLineTest.java | 2 +- .../resource/YamlFileResourceOptionsTest.java | 7 +- .../resource/YamlFileResourceTest.java | 4 +- ...nested_chat_component_complex_expected.yml | 26 +- src/test/resources/config-export-expected.yml | 6 +- 12 files changed, 179 insertions(+), 373 deletions(-) diff --git a/src/main/java/ch/jalu/configme/resource/PropertyPathTraverser.java b/src/main/java/ch/jalu/configme/resource/PropertyPathTraverser.java index ef4be049..4e9c223c 100644 --- a/src/main/java/ch/jalu/configme/resource/PropertyPathTraverser.java +++ b/src/main/java/ch/jalu/configme/resource/PropertyPathTraverser.java @@ -1,7 +1,5 @@ package ch.jalu.configme.resource; -import ch.jalu.configme.configurationdata.ConfigurationData; -import ch.jalu.configme.utils.CollectionUtils; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -9,70 +7,57 @@ /** * Helper class for the export of properties: it keeps track of the previously traversed property - * and returns which path parts are new and defines the level of indentation. + * and returns which path parts are new. *

- * For example if the property for path {@code config.datasource.mysql.type} was exported and we now + * For example, if the property for path {@code config.datasource.mysql.type} was exported and we now * encounter the property for path {@code config.datasource.driver.version}, the newly encountered * sections are {@code driver} and {@code version}. */ -public class PropertyPathTraverser { // todo: delete? +public class PropertyPathTraverser { - private final ConfigurationData configurationData; /** Contains all path elements besides the last, e.g. {datasource, mysql} for "datasource.mysql.table". */ - private List parentPathElements = new ArrayList<>(0); + private String parentPathElements; private boolean isFirstProperty = true; - public PropertyPathTraverser(@NotNull ConfigurationData configurationData) { - this.configurationData = configurationData; - } - - /** - * Returns all path elements for the given property that have not been traversed yet. - * - * @param pathElements all elements that make up the path of the property - * @return the new path elements - */ - public @NotNull List getPathElements(@NotNull List pathElements) { - List commonPathParts = CollectionUtils.filterCommonStart( - parentPathElements, pathElements.subList(0, pathElements.size() - 1)); - List newPathParts = CollectionUtils.getRange(pathElements, commonPathParts.size()); - - parentPathElements = pathElements.subList(0, pathElements.size() - 1); + public @NotNull List getPathElements(@NotNull String path) { + String[] pathParts = path.split("\\."); + int totalParts = pathParts.length; + int indentationOfFirstNewPart = returnIndentationOfFirstNewPathPart(path); + parentPathElements = path; + + StringBuilder fullPathBuilder = new StringBuilder(); + List pathElements = new ArrayList<>(totalParts); + int indentation = 0; + for (int i = 0; i < totalParts; ++i) { + fullPathBuilder.append(pathParts[i]); + PathElement element = new PathElement(indentation, pathParts[i], fullPathBuilder.toString(), isFirstProperty); + element.setEndOfPath(i == totalParts - 1); + element.setFirstOfGroup(indentationOfFirstNewPart == indentation); + pathElements.add(element); - int indentationLevel = commonPathParts.size(); - String prefix = commonPathParts.isEmpty() ? "" : String.join(".", commonPathParts) + "."; - return convertToPathElements(indentationLevel, prefix, newPathParts); - } - - private @NotNull List convertToPathElements(int indentation, @NotNull String prefix, - @NotNull List elements) { - List pathElements = new ArrayList<>(elements.size()); - for (String element : elements) { - List comments = isFirstProperty - ? getCommentsIncludingRoot(prefix + element) - : configurationData.getCommentsForSection(prefix + element); - pathElements.add(new PathElement(indentation, element, comments, isFirstProperty)); - isFirstProperty = false; - prefix += element + "."; ++indentation; + fullPathBuilder.append("."); + isFirstProperty = false; } - pathElements.get(0).setFirstOfGroup(true); + parentPathElements = path; return pathElements; } - private @NotNull List getCommentsIncludingRoot(@NotNull String path) { - List rootComments = configurationData.getCommentsForSection(""); - if ("".equals(path)) { - return rootComments; + private int returnIndentationOfFirstNewPathPart(String path) { + if (parentPathElements == null) { + return 0; } - List sectionComments = configurationData.getCommentsForSection(path); - // One or the other list might be empty, but we only do this once so we can ignore performance considerations - if (sectionComments.isEmpty()) { - return rootComments; + + int minLength = Math.min(parentPathElements.length(), path.length()); + int i = 0; + int indentation = 0; + while (i < minLength && path.charAt(i) == parentPathElements.charAt(i)) { + if (path.charAt(i) == '.') { + ++indentation; + } + ++i; } - List allComments = new ArrayList<>(rootComments); - allComments.addAll(sectionComments); - return allComments; + return indentation; } /** @@ -83,15 +68,16 @@ public static class PathElement { private final int indentationLevel; private final String name; - private List comments; + private final String fullPath; private final boolean isFirstElement; private boolean isFirstOfGroup; + private boolean isEndOfPath; - public PathElement(int indentationLevel, @NotNull String name, @NotNull List comments, + public PathElement(int indentationLevel, @NotNull String name, @NotNull String fullPath, boolean isFirstElement) { this.indentationLevel = indentationLevel; this.name = name; - this.comments = comments; + this.fullPath = fullPath; this.isFirstElement = isFirstElement; } @@ -103,17 +89,8 @@ public int getIndentationLevel() { return name; } - public @NotNull List getComments() { - return comments; - } - - public void addComments(@NotNull List commentsToAdd) { - if (!commentsToAdd.isEmpty()) { - if (comments.getClass() != ArrayList.class) { - comments = new ArrayList<>(comments); - } - comments.addAll(commentsToAdd); - } + public @NotNull String getFullPath() { + return fullPath; } public boolean isFirstElement() { @@ -127,6 +104,13 @@ public boolean isFirstOfGroup() { void setFirstOfGroup(boolean firstOfGroup) { isFirstOfGroup = firstOfGroup; } - } + public boolean isEndOfPath() { + return isEndOfPath; + } + + void setEndOfPath(boolean isEndOfPath) { + this.isEndOfPath = isEndOfPath; + } + } } diff --git a/src/main/java/ch/jalu/configme/resource/YamlFileResource.java b/src/main/java/ch/jalu/configme/resource/YamlFileResource.java index d7a0f2c6..a8274260 100644 --- a/src/main/java/ch/jalu/configme/resource/YamlFileResource.java +++ b/src/main/java/ch/jalu/configme/resource/YamlFileResource.java @@ -22,12 +22,10 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -38,13 +36,11 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static ch.jalu.configme.utils.CollectionUtils.getLastElementOrThrow; - public class YamlFileResource implements PropertyResource { private final Path path; private final @NotNull YamlFileResourceOptions options; - private final String indentationSpace; + private final int indentationSize; private @Nullable Yaml yamlObject; public YamlFileResource(@NotNull Path path) { @@ -54,7 +50,7 @@ public YamlFileResource(@NotNull Path path) { public YamlFileResource(@NotNull Path path, @NotNull YamlFileResourceOptions options) { this.path = path; this.options = options; - this.indentationSpace = options.getIndentation(); + this.indentationSize = options.getIndentationSize(); } /** @@ -117,7 +113,7 @@ Node toNodeRecursive() { } MappingNode mappingNode = new MappingNode(Tag.MAP, entryNodes, DumperOptions.FlowStyle.BLOCK); - List comLines = createCommentLines(comments.stream()); + List comLines = createCommentLines(comments); mappingNode.setBlockComments(comLines); return mappingNode; } @@ -128,23 +124,23 @@ public void exportProperties(@NotNull ConfigurationData configurationData) { List> properties = configurationData.getProperties(); Container root = new Container(configurationData.getCommentsForSection("")); + PropertyPathTraverser pathTraverser = new PropertyPathTraverser(); + for (Property property : properties) { Object exportValue = getExportValue(property, configurationData); if (exportValue != null) { Container container = root; - String[] pathElems = property.getPath().split("\\."); - for (int i = 0; i < pathElems.length - 1; ++i) { - String pathElem = pathElems[i]; - final int j = i; - container = container.getChildContainer(pathElem, () -> { - String fulPath = IntStream.rangeClosed(0, j) - .mapToObj(c -> pathElems[c]) - .collect(Collectors.joining(".")); - return configurationData.getCommentsForSection(fulPath); - }); + List pathElements = pathTraverser.getPathElements(property.getPath()); + for (PathElement pathElement : pathElements) { + if (pathElement.isEndOfPath()) { + int emptyLines = options.getNumberOfEmptyLinesBefore(pathElement); + container.putValue(pathElement.getName(), + toYamlNode(exportValue, property.getPath(), configurationData, emptyLines)); + } else { + container = container.getChildContainer(pathElement.getName(), + () -> getCommentsForPathElement(configurationData, pathElement)); + } } - container.putValue(pathElems[pathElems.length - 1], - toYamlNode(exportValue, property.getPath(), configurationData)); } } @@ -155,7 +151,6 @@ public void exportProperties(@NotNull ConfigurationData configurationData) { rootNode = root.toNodeRecursive(); } - try (OutputStream os = Files.newOutputStream(path); OutputStreamWriter writer = new OutputStreamWriter(os, options.getCharset())) { getYamlObject().serialize(rootNode, writer); @@ -166,22 +161,35 @@ public void exportProperties(@NotNull ConfigurationData configurationData) { } } - // todo: empty line config no longer respected - private Node toYamlNode(Object obj, String path, ConfigurationData configurationData) { + @NotNull + private List getCommentsForPathElement(@NotNull ConfigurationData configurationData, + @NotNull PathElement pathElement) { + return Stream.concat( + IntStream.range(0, options.getNumberOfEmptyLinesBefore(pathElement)).mapToObj(i -> "\n"), + configurationData.getCommentsForSection(pathElement.getFullPath()).stream()) + .collect(Collectors.toList()); + } + + private Node toYamlNode(Object obj, String path, ConfigurationData configurationData, int emptyLines) { Object value = obj; + List newLines = Collections.emptyList(); List additionalComments = Collections.emptyList(); if (obj instanceof ValueWithComments) { ValueWithComments valueWithComments = (ValueWithComments) obj; value = valueWithComments.getValue(); additionalComments = valueWithComments.getComments(); } + if (emptyLines > 0) { + newLines = IntStream.range(0, emptyLines).mapToObj(i -> "\n").collect(Collectors.toList()); + } + if (value instanceof Enum) { value = ((Enum) value).name(); } if (value instanceof Number || value instanceof String || value instanceof Boolean) { List confDataComments = configurationData.getCommentsForSection(path); - List comLines = createCommentLines(Stream.concat(confDataComments.stream(), additionalComments.stream())); + List comLines = createCommentLines(newLines, confDataComments, additionalComments); Tag tag = Tag.STR; if (value instanceof Integer || value instanceof Short || value instanceof Long) { @@ -198,7 +206,7 @@ private Node toYamlNode(Object obj, String path, ConfigurationData configuration } else if (value instanceof Iterable || value instanceof Object[]) { List confDataComments = configurationData.getCommentsForSection(path); - List comLines = createCommentLines(Stream.concat(confDataComments.stream(), additionalComments.stream())); + List comLines = createCommentLines(newLines, confDataComments, additionalComments); Stream stream; if (value instanceof Iterable) { @@ -210,7 +218,7 @@ private Node toYamlNode(Object obj, String path, ConfigurationData configuration List values = stream .map(val -> // todo counter - toYamlNode(val, String.join(".", combinePathElementsAndMapEntryKey(pathParts, "0")), configurationData)) + toYamlNode(val, String.join(".", combinePathElementsAndMapEntryKey(pathParts, "0")), configurationData, 0)) .collect(Collectors.toList()); SequenceNode sequenceNode = new SequenceNode(Tag.SEQ, values, DumperOptions.FlowStyle.BLOCK); @@ -218,7 +226,7 @@ private Node toYamlNode(Object obj, String path, ConfigurationData configuration return sequenceNode; } else if (value instanceof Map) { List confDataComments = configurationData.getCommentsForSection(path); - List comLines = createCommentLines(Stream.concat(confDataComments.stream(), additionalComments.stream())); + List comLines = createCommentLines(newLines, confDataComments, additionalComments); List pathParts = Arrays.asList(path.split("\\.")); List nodeEntries = new ArrayList<>(); @@ -226,7 +234,7 @@ private Node toYamlNode(Object obj, String path, ConfigurationData configuration ScalarNode keyNode = new ScalarNode(Tag.STR, entry.getKey(), null, null, DumperOptions.ScalarStyle.PLAIN); Node valueNode = toYamlNode(entry.getValue(), - String.join(".", combinePathElementsAndMapEntryKey(pathParts, entry.getKey())), configurationData); + String.join(".", combinePathElementsAndMapEntryKey(pathParts, entry.getKey())), configurationData, 0); if (!valueNode.getBlockComments().isEmpty()) { keyNode.setBlockComments(valueNode.getBlockComments()); valueNode.setBlockComments(Collections.emptyList()); @@ -243,9 +251,10 @@ private Node toYamlNode(Object obj, String path, ConfigurationData configuration } } - @NotNull - private static List createCommentLines(Stream commentEntries) { - return commentEntries + @SafeVarargs + private static @NotNull List createCommentLines(@NotNull List... commentEntries) { + return Arrays.stream(commentEntries) + .flatMap(List::stream) .map(comment -> comment.equals("\n") ? new CommentLine(null, null, "", CommentType.BLANK_LINE) : new CommentLine(null, null, " " + comment, CommentType.BLOCK)) @@ -262,87 +271,6 @@ private static List createCommentLines(Stream commentEntrie return path.toFile(); } - /** - * Exports the given value at the provided path. - * - * @param writer the file writer to write with - * @param pathTraverser the path traverser (e.g. keeps track of which path elements are new) - * @param pathElements all elements that make up the path to the value - * @param exportValue the value to export - * @throws IOException . - */ - protected void exportValue(@NotNull Writer writer, @NotNull PropertyPathTraverser pathTraverser, - @NotNull List pathElements, @Nullable Object exportValue) throws IOException { - if (exportValue == null) { - return; - } - - Object value = exportValue; - List additionalComments = Collections.emptyList(); - if (exportValue instanceof ValueWithComments) { - value = ((ValueWithComments) exportValue).getValue(); - additionalComments = ((ValueWithComments) exportValue).getComments(); - } - - if (value instanceof Map && !((Map) value).isEmpty()) { - final Map mapValue = (Map) value; - - for (Map.Entry entry : mapValue.entrySet()) { - List pathElementsForEntry = combinePathElementsAndMapEntryKey(pathElements, entry.getKey()); - exportValue(writer, pathTraverser, pathElementsForEntry, entry.getValue()); - } - } else { - List newPathElements = pathTraverser.getPathElements(pathElements); - if (!additionalComments.isEmpty()) { - getLastElementOrThrow(newPathElements).addComments(additionalComments); - } - final boolean isRootProperty = newPathElements.size() == 1 && "".equals(newPathElements.get(0).getName()); - - for (PathElement pathElement : newPathElements) { - writeIndentingBetweenLines(writer, pathElement); - writeComments(writer, pathElement.getIndentationLevel(), pathElement); - writer.append(getNewLineIfNotFirstElement(pathElement)); - if (!isRootProperty) { - writer.append(indent(pathElement.getIndentationLevel())) - .append(escapePathElementIfNeeded(pathElement.getName())) - .append(":"); - } - } - if (!isRootProperty) { - writer.append(" "); - } - - writer.append(toYamlIndented(value, newPathElements.get(newPathElements.size() - 1).getIndentationLevel())); - } - } - - /** - * Writes the given comment lines as YAML comments at the given indentation level. - * - * @param writer the writer to write with - * @param indentation the level at which the comment lines should be indented - * @param pathElement the path element for which the comments are being generated - * @throws IOException . - */ - protected void writeComments(@NotNull Writer writer, int indentation, - @NotNull PathElement pathElement) throws IOException { - if (pathElement.getComments().isEmpty()) { - return; - } - - String lineStart = pathElement.isFirstElement() ? "" : "\n"; - String commentStart = indent(indentation) + "# "; - for (String comment : pathElement.getComments()) { - writer.append(lineStart); - lineStart = "\n"; - - if (!"\n".equals(comment)) { - writer.append(commentStart) - .append(comment); - } - } - } - /** * Combines two path element sources to a new list of path elements: the list of path elements that were given * from the parent context and the map entry key from which one or more path elements should be derived. @@ -365,108 +293,6 @@ protected List combinePathElementsAndMapEntryKey(List parentPath .collect(Collectors.toList()); } - private void writeIndentingBetweenLines(@NotNull Writer writer, - @NotNull PathElement pathElement) throws IOException { - int numberOfEmptyLines = options.getNumberOfEmptyLinesBefore(pathElement); - for (int i = 0; i < numberOfEmptyLines; ++i) { - writer.append("\n"); - } - } - - private @NotNull String getNewLineIfNotFirstElement(@NotNull PathElement pathElement) { - return pathElement.isFirstElement() && pathElement.getComments().isEmpty() ? "" : "\n"; - } - - /** - * Returns the value in its YAML representation with an indentation of the given level. Proper indentation - * should be applied to all lines except for the first one (such that this method's return value can simply - * be appended to a properly indented property prefix like {@code name:}). - * - * @param value the value to convert to YAML - * @param indent level of indentation to use - * @return the value as YAML at the given indentation level - */ - protected @NotNull String toYamlIndented(@Nullable Object value, int indent) { - String representation = toYaml(value); - String[] lines = representation.split("\\n"); - return String.join("\n" + indent(indent), lines); - } - - /** - * Returns the YAML representation for the given value (belonging to the given value). - * This method returns the YAML representation of the value only (does not include the key) - * with no indentation (will be applied afterwards with the appropriate level). - * - * @param value the value to transform as YAML - * @return the YAML representation of the value - */ - protected String toYaml(@Nullable Object value) { - if (value instanceof String) { - return getYamlObject().dump(value); - } - - if (value instanceof Collection) { - return streamToYaml(((Collection) value).stream()); - } else if (value instanceof Object[]) { - return streamToYaml(Arrays.stream((Object[]) value)); - } else if (value instanceof ValueWithComments) { - ValueWithComments vwc = (ValueWithComments) value; - StringBuilder result = new StringBuilder(); - vwc.getComments().forEach(com -> result.append("\n# ").append(com)); - result.append("\n- ").append(getYamlObject().dump(vwc.getValue())); - return result.toString(); - } - - return getYamlObject().dump(value); - } - - protected String streamToYaml(Stream stream) { - StringBuilder result = new StringBuilder(); - Yaml yaml = getYamlObject(); - stream.forEach(elem -> { - if (elem instanceof ValueWithComments) { - ValueWithComments vwc = (ValueWithComments) elem; - vwc.getComments().forEach(com -> result.append("\n# ").append(com)); - result.append("\n- ").append(yaml.dump(vwc.getValue())); - } else { - result.append("\n- ").append(yaml.dump(elem)); - } - }); - - if (result.length() == 0) { - return "[]"; - } - return result.toString(); - } - - /** - * Returns a String of whitespace for indentation in YAML at the given level. - * - * @param level the desired level of indentation - * @return whitespace to prepend to a line for proper indentation - */ - protected String indent(int level) { - switch (level) { - case 0: return ""; - case 1: return indentationSpace; - case 2: return indentationSpace + indentationSpace; - case 3: return indentationSpace + indentationSpace + indentationSpace; - case 4: return indentationSpace + indentationSpace + indentationSpace + indentationSpace; - case 5: return indentationSpace + indentationSpace + indentationSpace + indentationSpace + indentationSpace; - default: // proceed - } - - final StringBuilder result = new StringBuilder(level * indentationSpace.length()); - for (int i = 0; i < level; ++i) { - result.append(indentationSpace); - } - return result.toString(); - } - - protected @NotNull String escapePathElementIfNeeded(@NotNull String path) { - return getYamlObject().dump(path).trim(); - } - /** * Called at the end of {@link #exportProperties}, regardless whether the execution was successful or not. */ @@ -491,7 +317,7 @@ protected void onWriteComplete() { options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); options.setAllowUnicode(true); options.setProcessComments(true); - options.setIndent(indentationSpace.length()); // todo + options.setIndent(indentationSize); return new Yaml(options); } diff --git a/src/main/java/ch/jalu/configme/resource/YamlFileResourceOptions.java b/src/main/java/ch/jalu/configme/resource/YamlFileResourceOptions.java index 76e55986..d0e8939e 100644 --- a/src/main/java/ch/jalu/configme/resource/YamlFileResourceOptions.java +++ b/src/main/java/ch/jalu/configme/resource/YamlFileResourceOptions.java @@ -53,20 +53,6 @@ public boolean splitDotPaths() { return splitDotPaths; } - /** - * @return the indentation to use for one level - */ - public @NotNull String getIndentation() { - if (indentationSize == 4) { - return " "; - } - StringBuilder sb = new StringBuilder(indentationSize); - for (int i = 0; i < indentationSize; ++i) { - sb.append(" "); - } - return sb.toString(); - } - protected final @Nullable ToIntFunction getIndentFunction() { return numberOfLinesBeforeFunction; } diff --git a/src/main/java/ch/jalu/configme/utils/CollectionUtils.java b/src/main/java/ch/jalu/configme/utils/CollectionUtils.java index eb7547ad..c886e8a3 100644 --- a/src/main/java/ch/jalu/configme/utils/CollectionUtils.java +++ b/src/main/java/ch/jalu/configme/utils/CollectionUtils.java @@ -48,12 +48,4 @@ private CollectionUtils() { } return commonStart; } - - public static @NotNull T getLastElementOrThrow(@NotNull List list) { - int lastIndex = list.size() - 1; - if (lastIndex < 0) { - throw new IllegalStateException("Expected non-empty list to get last element"); - } - return list.get(lastIndex); - } } diff --git a/src/test/java/ch/jalu/configme/beanmapper/BeanMapOnRootLevelTest.java b/src/test/java/ch/jalu/configme/beanmapper/BeanMapOnRootLevelTest.java index 99d814f8..8573aabf 100644 --- a/src/test/java/ch/jalu/configme/beanmapper/BeanMapOnRootLevelTest.java +++ b/src/test/java/ch/jalu/configme/beanmapper/BeanMapOnRootLevelTest.java @@ -74,6 +74,7 @@ void shouldHaveEmptyMapIfFileIsEmptyMap() throws IOException { @Test void shouldLoadMapFromFile() throws IOException { + // given String yaml = "medium:\n" + "\n name: \"med\"\n" + "\n lore:\n" @@ -96,6 +97,7 @@ void shouldLoadMapFromFile() throws IOException { @Test void shouldExportValuesAsEmptyMap() throws IOException { + // given String yaml = "medium:\n" + "\n name: \"med\"\n" + "\n lore:\n" @@ -118,6 +120,7 @@ void shouldExportValuesAsEmptyMap() throws IOException { @Test void shouldExportValuesAsEmptyMapIncludingComments() throws IOException { + // given String yaml = "medium:\n" + "\n name: \"med\"\n" + "\n lore:\n" diff --git a/src/test/java/ch/jalu/configme/beanmapper/BeanWithCollectionOfBeanTypeTest.java b/src/test/java/ch/jalu/configme/beanmapper/BeanWithCollectionOfBeanTypeTest.java index 1ae72854..a32495f7 100644 --- a/src/test/java/ch/jalu/configme/beanmapper/BeanWithCollectionOfBeanTypeTest.java +++ b/src/test/java/ch/jalu/configme/beanmapper/BeanWithCollectionOfBeanTypeTest.java @@ -74,16 +74,13 @@ void shouldSerializeProperly() throws IOException { List lines = Files.readAllLines(file); assertThat(lines, contains( "message-key:", - " # HTML color", " color: blue", " text: outside", " extra:", - " - # HTML color", - " color: green", + " - color: green", " text: inner1", " extra: []", - " - # HTML color", - " color: blue", + " - color: blue", " text: inner2", " extra: []" )); @@ -156,7 +153,6 @@ public static final class PropertyHolder implements SettingsHolder { public static class ChatComponent { - @Comment("HTML color") private String color; private String text; private List extra = new ArrayList<>(); diff --git a/src/test/java/ch/jalu/configme/resource/PropertyPathTraverserTest.java b/src/test/java/ch/jalu/configme/resource/PropertyPathTraverserTest.java index 9ad32990..70f8c012 100644 --- a/src/test/java/ch/jalu/configme/resource/PropertyPathTraverserTest.java +++ b/src/test/java/ch/jalu/configme/resource/PropertyPathTraverserTest.java @@ -1,98 +1,118 @@ package ch.jalu.configme.resource; -import ch.jalu.configme.configurationdata.ConfigurationData; import org.junit.jupiter.api.Test; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; /** * Test for {@link PropertyPathTraverser}. */ class PropertyPathTraverserTest { - private ConfigurationData configurationData = mock(ConfigurationData.class); - - private PropertyPathTraverser propertyPathTraverser = new PropertyPathTraverser(configurationData); + private final PropertyPathTraverser propertyPathTraverser = new PropertyPathTraverser(); @Test void shouldReturnPathElements() { - // given - given(configurationData.getCommentsForSection("")).willReturn(Collections.singletonList("root comment")); - given(configurationData.getCommentsForSection("prop.test")).willReturn(Collections.singletonList("prop.test comment")); - - // when - List result = propertyPathTraverser.getPathElements(Arrays.asList("prop", "test")); + // given / when + List result = propertyPathTraverser.getPathElements("prop.test"); // then assertThat(result, hasSize(2)); + assertThat(result.get(0).getName(), equalTo("prop")); - assertThat(result.get(0).getComments(), contains("root comment")); + assertThat(result.get(0).getFullPath(), equalTo("prop")); assertThat(result.get(0).getIndentationLevel(), equalTo(0)); + assertThat(result.get(0).isFirstElement(), equalTo(true)); + assertThat(result.get(0).isFirstOfGroup(), equalTo(true)); + assertThat(result.get(0).isEndOfPath(), equalTo(false)); + assertThat(result.get(1).getName(), equalTo("test")); - assertThat(result.get(1).getComments(), contains("prop.test comment")); + assertThat(result.get(1).getFullPath(), equalTo("prop.test")); assertThat(result.get(1).getIndentationLevel(), equalTo(1)); + assertThat(result.get(1).isFirstElement(), equalTo(false)); + assertThat(result.get(1).isFirstOfGroup(), equalTo(false)); + assertThat(result.get(1).isEndOfPath(), equalTo(true)); } @Test void shouldCombineRootCommentWithThatOfParent() { - // given - given(configurationData.getCommentsForSection("")).willReturn(Arrays.asList("root1", "root2")); - given(configurationData.getCommentsForSection("prop")).willReturn(Collections.singletonList("prop 1")); - - // when - List result = propertyPathTraverser.getPathElements(Arrays.asList("prop", "test")); + // given / when + List result = propertyPathTraverser.getPathElements("prop.test"); // then assertThat(result, hasSize(2)); + assertThat(result.get(0).getName(), equalTo("prop")); - assertThat(result.get(0).getComments(), contains("root1", "root2", "prop 1")); + assertThat(result.get(0).getFullPath(), equalTo("prop")); assertThat(result.get(0).getIndentationLevel(), equalTo(0)); + assertThat(result.get(0).isFirstElement(), equalTo(true)); + assertThat(result.get(0).isFirstOfGroup(), equalTo(true)); + assertThat(result.get(0).isEndOfPath(), equalTo(false)); + assertThat(result.get(1).getName(), equalTo("test")); - assertThat(result.get(1).getComments(), empty()); + assertThat(result.get(1).getFullPath(), equalTo("prop.test")); assertThat(result.get(1).getIndentationLevel(), equalTo(1)); + assertThat(result.get(1).isFirstElement(), equalTo(false)); + assertThat(result.get(1).isFirstOfGroup(), equalTo(false)); + assertThat(result.get(1).isEndOfPath(), equalTo(true)); } @Test void shouldHandleEmptyStringPath() { - // given - given(configurationData.getCommentsForSection("")).willReturn(Arrays.asList("c1", "d2", "e3")); - - // when - List result = propertyPathTraverser.getPathElements(Collections.singletonList("")); + // given / when + List result = propertyPathTraverser.getPathElements(""); // then assertThat(result, hasSize(1)); assertThat(result.get(0).getName(), equalTo("")); - assertThat(result.get(0).getComments(), contains("c1", "d2", "e3")); + assertThat(result.get(0).getFullPath(), equalTo("")); assertThat(result.get(0).getIndentationLevel(), equalTo(0)); + assertThat(result.get(0).isFirstElement(), equalTo(true)); + assertThat(result.get(0).isFirstOfGroup(), equalTo(true)); + assertThat(result.get(0).isEndOfPath(), equalTo(true)); } @Test - void shouldReturnOnlyNewElements() { + void shouldReturnAllElements() { // given - given(configurationData.getCommentsForSection("some.longer.path")).willReturn(Collections.singletonList("The comment")); - propertyPathTraverser.getPathElements(Arrays.asList("some", "longer", "test")); + propertyPathTraverser.getPathElements("some.longer.test"); // when - List result = propertyPathTraverser.getPathElements(Arrays.asList("some", "longer", "path", "value")); + List result = propertyPathTraverser.getPathElements("some.longer.path.value"); // then - assertThat(result, hasSize(2)); - assertThat(result.get(0).getName(), equalTo("path")); - assertThat(result.get(0).getComments(), contains("The comment")); - assertThat(result.get(0).getIndentationLevel(), equalTo(2)); - assertThat(result.get(1).getName(), equalTo("value")); - assertThat(result.get(1).getComments(), empty()); - assertThat(result.get(1).getIndentationLevel(), equalTo(3)); + assertThat(result, hasSize(4)); + + assertThat(result.get(0).getName(), equalTo("some")); + assertThat(result.get(0).getFullPath(), equalTo("some")); + assertThat(result.get(0).getIndentationLevel(), equalTo(0)); + assertThat(result.get(0).isFirstElement(), equalTo(false)); // because "some.longer.test" was already visited + assertThat(result.get(0).isFirstOfGroup(), equalTo(false)); + assertThat(result.get(0).isEndOfPath(), equalTo(false)); + + assertThat(result.get(1).getName(), equalTo("longer")); + assertThat(result.get(1).getFullPath(), equalTo("some.longer")); + assertThat(result.get(1).getIndentationLevel(), equalTo(1)); + assertThat(result.get(1).isFirstElement(), equalTo(false)); + assertThat(result.get(1).isFirstOfGroup(), equalTo(false)); + assertThat(result.get(1).isEndOfPath(), equalTo(false)); + + assertThat(result.get(2).getName(), equalTo("path")); + assertThat(result.get(2).getFullPath(), equalTo("some.longer.path")); + assertThat(result.get(2).getIndentationLevel(), equalTo(2)); + assertThat(result.get(2).isFirstElement(), equalTo(false)); + assertThat(result.get(2).isFirstOfGroup(), equalTo(true)); + assertThat(result.get(2).isEndOfPath(), equalTo(false)); + + assertThat(result.get(3).getName(), equalTo("value")); + assertThat(result.get(3).getFullPath(), equalTo("some.longer.path.value")); + assertThat(result.get(3).getIndentationLevel(), equalTo(3)); + assertThat(result.get(3).isFirstElement(), equalTo(false)); + assertThat(result.get(3).isFirstOfGroup(), equalTo(false)); + assertThat(result.get(3).isEndOfPath(), equalTo(true)); } } diff --git a/src/test/java/ch/jalu/configme/resource/YamlFileResourceNewLineTest.java b/src/test/java/ch/jalu/configme/resource/YamlFileResourceNewLineTest.java index 5eb1f368..3a5c374d 100644 --- a/src/test/java/ch/jalu/configme/resource/YamlFileResourceNewLineTest.java +++ b/src/test/java/ch/jalu/configme/resource/YamlFileResourceNewLineTest.java @@ -138,7 +138,7 @@ void shouldExportWithRootCommentAndNewLineEverywhere() throws IOException { "", "second:", " a: 2", - "", + " ", " # b comm", " b: 3")); } diff --git a/src/test/java/ch/jalu/configme/resource/YamlFileResourceOptionsTest.java b/src/test/java/ch/jalu/configme/resource/YamlFileResourceOptionsTest.java index 4a1a1897..a15b4c9f 100644 --- a/src/test/java/ch/jalu/configme/resource/YamlFileResourceOptionsTest.java +++ b/src/test/java/ch/jalu/configme/resource/YamlFileResourceOptionsTest.java @@ -6,7 +6,6 @@ import java.nio.charset.StandardCharsets; import java.util.function.ToIntFunction; -import static java.util.Collections.emptyList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -32,10 +31,9 @@ void shouldKeepConfiguredValues() { // then assertThat(options.getCharset(), equalTo(StandardCharsets.UTF_16BE)); assertThat(options.getIndentFunction(), equalTo(lineFunction)); - PathElement pathElement = new PathElement(3, "test", emptyList(), false); + PathElement pathElement = new PathElement(3, "test", "test", false); assertThat(options.getNumberOfEmptyLinesBefore(pathElement), equalTo(3)); assertThat(options.getIndentationSize(), equalTo(2)); - assertThat(options.getIndentation(), equalTo(" ")); assertThat(options.splitDotPaths(), equalTo(false)); } @@ -48,8 +46,7 @@ void shouldCreateOptionsWithDefaults() { assertThat(options.getCharset(), equalTo(StandardCharsets.UTF_8)); assertThat(options.getIndentFunction(), nullValue()); assertThat(options.getIndentationSize(), equalTo(4)); - assertThat(options.getIndentation(), equalTo(" ")); - PathElement pathElement = new PathElement(3, "test", emptyList(), false); + PathElement pathElement = new PathElement(3, "test", "test", false); assertThat(options.getNumberOfEmptyLinesBefore(pathElement), equalTo(0)); assertThat(options.splitDotPaths(), equalTo(true)); } diff --git a/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java b/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java index 5fb34775..9a96493e 100644 --- a/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java +++ b/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java @@ -381,10 +381,10 @@ void shouldExportWithCustomIndentationSize() throws IOException { " - beige", " - gray", " dustLevel: 2.4", - "", + " ", " # Cool features", "", - " # Contains cool settings", + " # Contains cool settings", " cool:", " # Enable cool features?", " enabled: true", diff --git a/src/test/resources/beanmapper/nested_chat_component_complex_expected.yml b/src/test/resources/beanmapper/nested_chat_component_complex_expected.yml index 04a85728..ae9ec040 100644 --- a/src/test/resources/beanmapper/nested_chat_component_complex_expected.yml +++ b/src/test/resources/beanmapper/nested_chat_component_complex_expected.yml @@ -2,15 +2,16 @@ message-key: color: green text: outside extra: - - color: yellow - text: inner1 - extra: [] - - color: blue - text: inner2 - extra: - - color: red - text: level2 text + - color: yellow + text: inner1 extra: [] + - color: blue + text: inner2 + extra: + - color: red + text: level2 text + extra: [] + # Optional: additional info conditionalElem: color: orange text: orange extension @@ -24,15 +25,16 @@ message-key: med: color: gray text: med text - extra: - - color: green - text: med child - extra: [] + extra: + - color: green + text: med child + extra: [] conditionals: {} high: color: black text: high text extra: [] + # Optional: additional info conditionalElem: color: teal text: teal addition diff --git a/src/test/resources/config-export-expected.yml b/src/test/resources/config-export-expected.yml index 637adb08..eed9deae 100644 --- a/src/test/resources/config-export-expected.yml +++ b/src/test/resources/config-export-expected.yml @@ -25,10 +25,10 @@ features: - beige - gray dustLevel: 2.4 - + # Cool features - # Contains cool settings + # Contains cool settings cool: # Enable cool features? enabled: true @@ -42,4 +42,4 @@ security: forbiddenNames: - admin - staff - - moderator + - moderator \ No newline at end of file