Repository: decorators-squad/eo-yaml
Pull request #527 (2022-11-20 21:19:34)

Fixes for #525
Fixed bug in empty block/folded scalars when they are nested children.

Left todo to allow keys without values in mappings.


Patch:
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java b/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java
index 8aa92f5..f38e7c0 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java
@@ -177,7 +177,7 @@ final class ReadPlainScalar extends BaseScalar {
      */
     private String unescape(final String value) {
         final String unescaped;
-        if(value == null || value.length()<=2) {
+        if(value == null) {
             unescaped = value;
         } else {
             if (value.startsWith("\"") && value.endsWith("\"")) {
diff --git a/src/main/java/com/amihaiemil/eoyaml/WellIndented.java b/src/main/java/com/amihaiemil/eoyaml/WellIndented.java
index 89562b6..6d88126 100644
--- a/src/main/java/com/amihaiemil/eoyaml/WellIndented.java
+++ b/src/main/java/com/amihaiemil/eoyaml/WellIndented.java
@@ -62,7 +62,13 @@ import java.util.List;
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 3.1.2
- *
+ * @todo #525:3h Please make sure to allow keys with empty values in mappings.
+ *  At the moment, if we cannot find the value on the same line, we always
+ *  look beneath the key for more-indented lines to be treated as the value,
+ *  but this is not always the case. This should be a valid YAML:
+ *  key1: value1
+ *  key2:
+ *  key3: value3
  */
 final class WellIndented implements YamlLines {
 
@@ -126,7 +132,7 @@ final class WellIndented implements YamlLines {
                         if(lineIndent != prevIndent + 2) {
                             final CharSequence prevLineLastChar =
                                     previous.trimmed().substring(previous.trimmed().length() - 1);
-                            if (!(">|".contains(prevLineLastChar) && lineIndent == prevIndent)) {
+                            if (!">|".contains(prevLineLastChar)) {
                                 if (this.guessIndentation) {
                                     line = new Indented(line, prevIndent + 2);
                                 } else {


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java b/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
index 8c09708..5ff4b24 100644
--- a/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
@@ -46,7 +46,7 @@ import java.util.Set;
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 1.0.1
- *
+ * @checkstyle ExecutableStatementCount (2000 lines)
  */
 public final class RtYamlInputTest {
 
@@ -1069,6 +1069,119 @@ public final class RtYamlInputTest {
                                  Matchers.equalTo("test"));
     }
 
+    /**
+     * Unit test for issue 525 (empty scalars part).
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void shouldReadEmptyScalarsProperly() throws IOException {
+        final String filename = "issue_525_emptyEntries.yml";
+
+        final YamlMapping read = new RtYamlInput(
+            new FileInputStream("src/test/resources/" + filename)
+        ).readYamlMapping();
+        MatcherAssert.assertThat(read.type(), Matchers.equalTo(Node.MAPPING));
+        MatcherAssert.assertThat(
+            read.keys(),
+            Matchers.hasSize(1)
+        );
+
+        final YamlSequence aSequence = read.yamlSequence("a_sequence");
+        MatcherAssert.assertThat(
+            aSequence.type(),
+            Matchers.equalTo(Node.SEQUENCE)
+        );
+        MatcherAssert.assertThat(
+            aSequence.size(),
+            Matchers.equalTo(3)
+        );
+
+        final Iterator<YamlNode> iterator = aSequence.iterator();
+
+        final YamlNode firstItem = iterator.next();
+        MatcherAssert.assertThat(
+            firstItem.type(),
+            Matchers.equalTo(Node.MAPPING)
+        );
+        YamlNode aMapping = firstItem.asMapping().value("a_mapping");
+        MatcherAssert.assertThat(
+                aMapping.type(),
+                Matchers.equalTo(Node.MAPPING)
+        );
+        final YamlNode emptyBlockScalar = aMapping.asMapping()
+            .value("empty_block_scalar");
+        MatcherAssert.assertThat(
+            emptyBlockScalar.type(),
+            Matchers.equalTo(Node.SCALAR));
+        MatcherAssert.assertThat(
+            emptyBlockScalar.asScalar().value(),
+            Matchers.isEmptyString());
+        final YamlNode emptyFoldedScalar = aMapping.asMapping()
+            .value("empty_folded_scalar");
+        MatcherAssert.assertThat(
+            emptyFoldedScalar.type(),
+            Matchers.equalTo(Node.SCALAR));
+        MatcherAssert.assertThat(
+            emptyFoldedScalar.asScalar().value(),
+            Matchers.isEmptyString());
+        final YamlNode normalScalar = aMapping.asMapping()
+            .value("normal_scalar");
+        MatcherAssert.assertThat(
+            normalScalar.type(),
+            Matchers.equalTo(Node.SCALAR));
+        MatcherAssert.assertThat(
+            normalScalar.asScalar().value(),
+            Matchers.equalTo("example"));
+
+        final YamlNode secondItem = iterator.next();
+        MatcherAssert.assertThat(
+                secondItem.type(),
+                Matchers.equalTo(Node.MAPPING));
+        final YamlNode value = secondItem.asMapping()
+            .value("key");
+        MatcherAssert.assertThat(value.type(), Matchers.equalTo(Node.SCALAR));
+        MatcherAssert.assertThat(
+            value.asScalar().value(),
+            Matchers.equalTo("value"));
+
+        final YamlNode thirdItem = iterator.next();
+        MatcherAssert.assertThat(
+                thirdItem.type(),
+                Matchers.equalTo(Node.MAPPING));
+        YamlNode anotherMapping = thirdItem.asMapping()
+            .value("another_mapping");
+        MatcherAssert.assertThat(
+                anotherMapping.type(),
+                Matchers.equalTo(Node.MAPPING));
+        final YamlNode emptyScalarApp = anotherMapping.asMapping()
+            .value("empty_scalar_app");
+        MatcherAssert.assertThat(
+            emptyScalarApp.type(),
+            Matchers.equalTo(Node.SCALAR)
+        );
+        MatcherAssert.assertThat(
+            emptyScalarApp.asScalar().value(),
+            Matchers.isEmptyString());
+        final YamlNode emptyScalarQuotes = anotherMapping.asMapping()
+            .value("empty_scalar_quotes");
+        MatcherAssert.assertThat(
+            emptyScalarQuotes.type(),
+            Matchers.equalTo(Node.SCALAR)
+        );
+        MatcherAssert.assertThat(
+            emptyScalarQuotes.asScalar().value(),
+            Matchers.isEmptyString());
+        final YamlNode nullScalar = anotherMapping.asMapping()
+            .value("null_scalar");
+        MatcherAssert.assertThat(
+            nullScalar.type(),
+            Matchers.equalTo(Node.SCALAR)
+        );
+        MatcherAssert.assertThat(
+            nullScalar.asScalar().value(),
+            Matchers.nullValue());
+    }
+
     /**
      * Read a test resource file's contents.
      * @param fileName File to read.
diff --git a/src/test/resources/issue_525_emptyEntries.yml b/src/test/resources/issue_525_emptyEntries.yml
new file mode 100644
index 0000000..03d4bea
--- /dev/null
+++ b/src/test/resources/issue_525_emptyEntries.yml
@@ -0,0 +1,10 @@
+a_sequence:
+  - a_mapping:
+      empty_block_scalar: |
+      empty_folded_scalar: >
+      normal_scalar: example
+  - key: value
+  - another_mapping:
+      empty_scalar_app: ''
+      empty_scalar_quotes: ""
+      null_scalar: null