Repository: decorators-squad/eo-yaml
Pull request #530 (2022-11-26 11:44:24)

#529 allow empty keys in mapping; lighter indentation rules
PR for #529 

- Allow empty keys in mapping (this was previously throwing an indentation exaption):

```yaml
architect: amihaiemil
contributors:
# exception was being thrown at this line, because contributors: was not allowed to have empty value
developers:
  - amihaiemil
  - shaik
  - rultor
```

- Don't throw exception anymore if sequence under mapping key is not indented properly:

```yaml
architect: amihaiemil
contributors:
developers:
- amihaiemil # these 3 lines should normally have a deeper indentation
- shaik
- rultor
```
- Remove the ``2-by-2`` mandatory indentation. Indentation can now be however many spaces:

```yaml
architect: amihaiemil
contributors:
developers:
        - amihaiemil # this indentation works now, previously you would receive an indentation exception
        - shaik
        - rultor
```

Added TODO to remove the ``guessIndentation`` logic (not needed anymore and it is too unstable anyway). 



Patch:
diff --git a/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java b/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java
index 6218fc9..2b3001c 100644
--- a/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java
+++ b/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java
@@ -141,13 +141,16 @@ final class AllYamlLines implements YamlLines {
      * @param guessIndentation If true, we will guess the correct indentation,
      *  if any YAML line is misplaced.
      * @return Found YamlNode.
+     * @todo #529:60min Implement, test and use Edited YamlLine here.
+     *  A YamlLine to which we append some text and it keeps the indentation,
+     *  number etc of the original line.
      */
     private YamlNode mappingSequenceOrPlainScalar(
         final YamlLine prev,
         final boolean guessIndentation
     ) {
         YamlNode node = null;
-        final YamlLine first = new Skip(
+        final Iterator<YamlLine> nodeLines = new Skip(
             this,
             line -> line.number() <= prev.number(),
             line -> line.trimmed().startsWith("#"),
@@ -155,17 +158,34 @@ final class AllYamlLines implements YamlLines {
             line -> line.trimmed().startsWith("..."),
             line -> line.trimmed().startsWith("%"),
             line -> line.trimmed().startsWith("!!")
-        ).iterator().next();
-        Matcher matcher = SEQUENCE_OR_MAP.matcher(first.trimmed());
-        if (matcher.matches()) {
-            if (matcher.group(2) != null) {
-                node = new ReadYamlSequence(prev, this, guessIndentation);
-            } else if (matcher.group(4) != null) {
-                node = new ReadYamlMapping(prev.number(),
+        ).iterator();
+        final YamlLine first;
+        if(nodeLines.hasNext()) {
+            first = nodeLines.next();
+        } else {
+            first = new YamlLine.NullYamlLine();
+        }
+        if(prev.trimmed().endsWith(":")
+            && first.indentation() <= prev.indentation()
+            && !first.trimmed().startsWith("-")
+        ) {
+            node = new ReadPlainScalar(
+                this,
+                new RtYamlLine(prev.trimmed()
+                    + " null #" + prev.comment(), prev.number())
+            );
+        } else {
+            Matcher matcher = SEQUENCE_OR_MAP.matcher(first.trimmed());
+            if (matcher.matches()) {
+                if (matcher.group(2) != null) {
+                    node = new ReadYamlSequence(prev, this, guessIndentation);
+                } else if (matcher.group(4) != null) {
+                    node = new ReadYamlMapping(prev.number(),
                         prev, this, guessIndentation);
+                }
+            } else if (this.original().size() == 1) {
+                node = new ReadPlainScalar(this, first);
             }
-        } else if (this.original().size() == 1) {
-            node = new ReadPlainScalar(this, first);
         }
         if (node == null) {
             throw new YamlReadingException(
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlLine.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlLine.java
index 8383a0b..1ff0717 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlLine.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlLine.java
@@ -162,7 +162,7 @@ final class RtYamlLine implements YamlLine {
         if("---".equals(this.trimmed())) {
             result = false;
         } else {
-            final String specialCharacters = ":>|-?";
+            final String specialCharacters = "-?";
             final CharSequence prevLineLastChar =
                 this.trimmed().substring(this.trimmed().length() - 1);
             result = specialCharacters.contains(prevLineLastChar);
diff --git a/src/main/java/com/amihaiemil/eoyaml/WellIndented.java b/src/main/java/com/amihaiemil/eoyaml/WellIndented.java
index 4b2515b..a4d24ce 100644
--- a/src/main/java/com/amihaiemil/eoyaml/WellIndented.java
+++ b/src/main/java/com/amihaiemil/eoyaml/WellIndented.java
@@ -62,11 +62,6 @@ import java.util.List;
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 3.1.2
- * @todo #525:60m 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 {
 
@@ -109,6 +104,8 @@ final class WellIndented implements YamlLines {
      * correct.
      * @checkstyle LineLength (50 lines)
      * @return Iterator over these yaml lines.
+     * @todo #529:60min Remove the guessIndentation logic. It doesn't make
+     *  sense anymore and it's too unpredictible.
      */
     @Override
     public Iterator<YamlLine> iterator() {
@@ -127,39 +124,39 @@ final class WellIndented implements YamlLines {
                     }
                     int lineIndent = line.indentation();
                     if(previous.requireNestedIndentation()) {
-                        if(lineIndent != prevIndent + 2) {
+                        if(lineIndent < prevIndent + 2) {
+                            if (this.guessIndentation) {
+                                line = new Indented(line, prevIndent + 2);
+                            } else {
+                                throw new YamlIndentationException(
+                                    "Indentation of line " + (line.number() + 1)
+                                    + " [" + line.trimmed() + "]"
+                                    + " is not ok. It should be greater than the one"
+                                    + " of line " + (previous.number() + 1)
+                                    + " [" + previous.trimmed() + "]"
+                                    + " by at least 2 spaces."
+                                );
+                            }
+                        }
+                    } else {
+                        if(!"---".equals(previous.trimmed()) && lineIndent > prevIndent) {
                             final CharSequence prevLineLastChar =
-                                    previous.trimmed().substring(previous.trimmed().length() - 1);
-                            if (!">|".contains(prevLineLastChar)) {
+                                previous.trimmed().substring(previous.trimmed().length() - 1);
+                            if (!">|:".contains(prevLineLastChar)) {
                                 if (this.guessIndentation) {
-                                    line = new Indented(line, prevIndent + 2);
+                                    line = new Indented(line, prevIndent);
                                 } else {
                                     throw new YamlIndentationException(
-                                            "Indentation of line " + (line.number() + 1)
+                                        "Indentation of line " + (line.number() + 1)
                                             + " [" + line.trimmed() + "]"
-                                            + " is not ok. It should be greater than the one"
-                                            + " of line " + (previous.number() + 1)
-                                            + " [" + previous.trimmed() + "]"
-                                            + " by 2 spaces."
+                                            + " is greater than the one of line "
+                                            + (previous.number() + 1)
+                                            + " [" + previous.trimmed() + "]. "
+                                            + "It should be less or equal."
                                     );
                                 }
                             }
                         }
-                    } else {
-                        if(!"---".equals(previous.trimmed()) && lineIndent > prevIndent) {
-                            if(this.guessIndentation) {
-                                line = new Indented(line, prevIndent);
-                            } else {
-                                throw new YamlIndentationException(
-                                    "Indentation of line " + (line.number() + 1)
-                                  + " [" + line.trimmed() + "]"
-                                  + " is greater than the one of line "
-                                  + (previous.number() + 1)
-                                  + " [" + previous.trimmed() + "]. "
-                                  + "It should be less or equal."
-                                );
-                            }
-                        }
                     }
                 }
                 previous = line;


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/RtYamlLineTest.java b/src/test/java/com/amihaiemil/eoyaml/RtYamlLineTest.java
index f7c2a71..c5a89ba 100644
--- a/src/test/java/com/amihaiemil/eoyaml/RtYamlLineTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/RtYamlLineTest.java
@@ -63,14 +63,6 @@ public final class RtYamlLineTest {
      */
     @Test
     public void requiresNestedIndentation() {
-        MatcherAssert.assertThat(
-            new RtYamlLine("this:", 12).requireNestedIndentation(),
-            Matchers.is(true)
-        );
-        MatcherAssert.assertThat(
-            new RtYamlLine("this: |> ", 12).requireNestedIndentation(),
-            Matchers.is(true)
-        );
         MatcherAssert.assertThat(
             new RtYamlLine("this: |- ", 12).requireNestedIndentation(),
             Matchers.is(true)
diff --git a/src/test/java/com/amihaiemil/eoyaml/YamlIndentationTestCase.java b/src/test/java/com/amihaiemil/eoyaml/YamlIndentationTestCase.java
index 0237af9..ab9fb7c 100644
--- a/src/test/java/com/amihaiemil/eoyaml/YamlIndentationTestCase.java
+++ b/src/test/java/com/amihaiemil/eoyaml/YamlIndentationTestCase.java
@@ -40,28 +40,14 @@ import java.util.List;
 
 /**
  * Test cases regarding an input YAML's indentation, particularly
- * disabling validation of indentation and trying to guess the correct
- * one.
+ * cases in which the input is not indented properly, we have keys without
+ * values etc.
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 5.1.0
  */
 public final class YamlIndentationTestCase {
 
-    /**
-     * A Yaml mapping containing an unindented sequence throws exception
-     * because of bad indentation.
-     * @throws Exception If something goes wrong.
-     */
-    @Test (expected = YamlIndentationException.class)
-    public void unintendedSequenceException() throws Exception {
-        final YamlMapping map = Yaml.createYamlInput(
-            new File("src/test/resources/badSequenceIndentationInMapping.yml"),
-            Boolean.FALSE
-        ).readYamlMapping();
-        System.out.println(map);
-    }
-
     /**
      * A Yaml mapping containing an unindented sequence can be read
      * if we guess the correct indentation.
@@ -70,8 +56,7 @@ public final class YamlIndentationTestCase {
     @Test
     public void readsUnintendedSequenceException() throws Exception {
         final YamlMapping map = Yaml.createYamlInput(
-            new File("src/test/resources/badSequenceIndentationInMapping.yml"),
-            Boolean.TRUE
+            new File("src/test/resources/badSequenceIndentationInMapping.yml")
         ).readYamlMapping();
         final YamlSequence developers = map.yamlSequence("developers");
         MatcherAssert.assertThat(
@@ -93,47 +78,31 @@ public final class YamlIndentationTestCase {
     }
 
     /**
-     * A badly indented YAML mapping throws an exception.
-     * @throws Exception If something goes wrong.
-     */
-    @Test (expected = YamlIndentationException.class)
-    public void complainsOnBadlyIndentedMapping() throws Exception {
-        final YamlMapping map = Yaml.createYamlInput(
-            new File("src/test/resources/badMappingIndentation.yml"),
-            Boolean.FALSE
-        ).readYamlMapping();
-        System.out.println(map);
-    }
-
-    /**
-     * A badly indented YAML can be read if we try to guess the indentation.
+     * A badly indented YAML mapping, which also has an empty key,
+     * is read properly.
      * @throws Exception If something goes wrong.
      */
     @Test
-    public void readsBadlyIndentedMapping() throws Exception {
+    public void readsBadlyIndentedMappingWithEmptyKey() throws Exception {
         final YamlMapping map = Yaml.createYamlInput(
             new File("src/test/resources/badMappingIndentation.yml"),
-            Boolean.TRUE
+            Boolean.FALSE
         ).readYamlMapping();
         System.out.println(map);
         MatcherAssert.assertThat(
-            map.string("name"),
-            Matchers.equalTo("eo-yaml")
+            map.string("name"), Matchers.equalTo("eo-yaml")
         );
         MatcherAssert.assertThat(
-            map.yamlSequence("developers"),
-            Matchers.nullValue()
+            map.string("contributors"), Matchers.nullValue()
         );
         MatcherAssert.assertThat(
-            map.yamlMapping("contributors"),
-            Matchers.notNullValue()
+            map.value("contributors").comment().value(),
+            Matchers.equalTo("empty key...")
         );
-        final YamlSequence developers = map
-            .yamlMapping("contributors")
-            .yamlSequence("developers");
+        final YamlSequence developers = map.yamlSequence("developers");
         MatcherAssert.assertThat(
-            developers,
-            Matchers.iterableWithSize(3)
+            developers.size(),
+            Matchers.equalTo(3)
         );
         MatcherAssert.assertThat(
             developers.string(0),
@@ -149,6 +118,7 @@ public final class YamlIndentationTestCase {
         );
     }
 
+
     /**
      * A badly indented YAML sequence throws an exception.
      * @throws Exception If something goes wrong.
diff --git a/src/test/resources/badMappingIndentation.yml b/src/test/resources/badMappingIndentation.yml
index b1b4ae3..04d3e04 100644
--- a/src/test/resources/badMappingIndentation.yml
+++ b/src/test/resources/badMappingIndentation.yml
@@ -1,6 +1,6 @@
 name: eo-yaml
-contributors:
+contributors: # empty key...
 developers:
-- amihaiemil # Elements of this sequence should be indented by 4 spaces.
+- amihaiemil # Elements of this sequence should be indented deeper.
 - sherif
 - rultor
\ No newline at end of file