Repository: decorators-squad/eo-yaml
Pull request #312 (2020-04-11 16:56:29)

Print "null" For Empty/Null YamlNodes
PR for #134 
* If a value is null or empty, print "null"
* added unit tests


Patch:
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlPrinter.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlPrinter.java
index efc61f4..6cb5d4e 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlPrinter.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlPrinter.java
@@ -62,10 +62,10 @@ final class RtYamlPrinter implements YamlPrinter {
             this.printScalar((Scalar) node, 0);
             this.writer.append(System.lineSeparator()).append("...");
         } else if(node instanceof YamlSequence) {
-            this.printPossibleComment(node.comment(), "");
+            this.printPossibleComment(node, "");
             this.printSequence((YamlSequence) node, 0);
         } else if(node instanceof YamlMapping) {
-            this.printPossibleComment(node.comment(), "");
+            this.printPossibleComment(node, "");
             this.printMapping((YamlMapping) node, 0);
         } else if(node instanceof YamlStream) {
             this.printStream((YamlStream) node, 0);
@@ -92,10 +92,10 @@ final class RtYamlPrinter implements YamlPrinter {
         final Iterator<YamlNode> valuesIt = stream.values().iterator();
         while(valuesIt.hasNext()) {
             final YamlNode document = valuesIt.next();
-            this.writer.append(indent)
-                    .append("---")
-                    .append(newLine);
-            this.printNode(document, indentation + 2);
+            this.writer
+                .append(indent)
+                .append("---");
+            this.printNode(document, true, indentation + 2);
             if(valuesIt.hasNext()) {
                 this.writer.append(newLine);
             }
@@ -122,37 +122,28 @@ final class RtYamlPrinter implements YamlPrinter {
         final Iterator<YamlNode> keysIt = mapping.keys().iterator();
         while(keysIt.hasNext()) {
             final YamlNode key = keysIt.next();
-            this.printPossibleComment(
-                key.comment(), alignment.toString()
-            );
+            this.printPossibleComment(key, alignment.toString());
             this.writer.append(alignment);
-            final BaseYamlNode indKey = (BaseYamlNode) key;
-            final BaseYamlNode value = (BaseYamlNode) mapping.value(key);
+            final YamlNode value = mapping.value(key);
             if(!(value instanceof Scalar)) {
-                this.printPossibleComment(
-                    value.comment(), alignment.toString()
-                );
+                this.printPossibleComment(value, alignment.toString());
             }
-            if(indKey instanceof Scalar) {
-                this.printScalar((Scalar) indKey, 0);
+            if(key instanceof Scalar) {
+                this.printScalar((Scalar) key, 0);
                 this.writer
                     .append(":");
             } else {
                 this.writer
-                    .append("?")
-                    .append(newLine);
-                this.printNode(indKey, indentation + 2);
+                    .append("?");
+                this.printNode(key, true, indentation + 2);
                 this.writer.append(newLine)
                     .append(alignment)
                     .append(":");
             }
             if (value instanceof Scalar) {
-                this.writer.append(" ");
-                this.printScalar((Scalar) value, 0);
+                this.printNode(value, false, 0);
             } else  {
-                this.writer
-                    .append(newLine);
-                this.printNode(value, indentation + 2);
+                this.printNode(value, true, indentation + 2);
             }
             if(keysIt.hasNext()) {
                 this.writer.append(newLine);
@@ -183,15 +174,14 @@ final class RtYamlPrinter implements YamlPrinter {
             if (node instanceof Scalar) {
                 this.writer
                     .append(alignment)
-                    .append("- ");
-                this.printScalar((Scalar) node, 0);
+                    .append("-");
+                this.printNode(node, false, 0);
             } else  {
-                this.printPossibleComment(node.comment(), alignment.toString());
+                this.printPossibleComment(node, alignment.toString());
                 this.writer
                     .append(alignment)
-                    .append("-")
-                    .append(newLine);
-                this.printNode(node, indentation + 2);
+                    .append("-");
+                this.printNode(node, true, indentation + 2);
             }
             if(valuesIt.hasNext()) {
                 this.writer.append(newLine);
@@ -253,25 +243,35 @@ final class RtYamlPrinter implements YamlPrinter {
     }
 
     /**
-     * Convenience method to chose the right printer by looking at the
-     * type of the YamlNode. This method should be used when printing children
+     * This method should be used when printing children
      * nodes of a complex Node (mapping, scalar, stream etc).
      * @param node YAML Node to print.
+     * @param onNewLine Should the child node be printed on a new line?
      * @param indentation Indentation of the print.
      * @throws IOException If any I/O error occurs.
      */
     private void printNode(
         final YamlNode node,
+        final boolean onNewLine,
         final int indentation
     ) throws IOException {
-        if(node instanceof Scalar) {
-            this.printScalar((Scalar) node, indentation);
-        } else if (node instanceof YamlSequence) {
-            this.printSequence((YamlSequence) node, indentation);
-        } else if (node instanceof YamlMapping) {
-            this.printMapping((YamlMapping) node, indentation);
-        } else if (node instanceof YamlStream) {
-            this.printStream((YamlStream) node, indentation);
+        if(node == null || ((BaseYamlNode) node).isEmpty()) {
+            this.writer.append(" ").append("null");
+        } else {
+            if (onNewLine) {
+                this.writer.append(System.lineSeparator());
+            } else {
+                this.writer.append(" ");
+            }
+            if (node instanceof Scalar) {
+                this.printScalar((Scalar) node, indentation);
+            } else if (node instanceof YamlSequence) {
+                this.printSequence((YamlSequence) node, indentation);
+            } else if (node instanceof YamlMapping) {
+                this.printMapping((YamlMapping) node, indentation);
+            } else if (node instanceof YamlStream) {
+                this.printStream((YamlStream) node, indentation);
+            }
         }
     }
 
@@ -279,23 +279,25 @@ final class RtYamlPrinter implements YamlPrinter {
      * Print a comment. Make sure to split the lines if there are more
      * lines separated by NewLine and also add a '# ' in front of each
      * line.
-     * @param comment Comment.
+     * @param node Node containing the Comment.
      * @param alignment Indentation.
      * @throws IOException If any I/O problem occurs.
      */
     private void printPossibleComment(
-        final Comment comment,
+        final YamlNode node,
         final String alignment
     ) throws IOException {
-        final String com = comment.value();
-        if(com.trim().length()!=0) {
-            String[] lines = com.split(System.lineSeparator());
-            for(final String line : lines) {
-                this.writer
-                    .append(alignment)
-                    .append("# ")
-                    .append(line)
-                    .append(System.lineSeparator());
+        if(node != null) {
+            final String com = node.comment().value();
+            if (com.trim().length() != 0) {
+                String[] lines = com.split(System.lineSeparator());
+                for (final String line : lines) {
+                    this.writer
+                            .append(alignment)
+                            .append("# ")
+                            .append(line)
+                            .append(System.lineSeparator());
+                }
             }
         }
     }


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/ReadYamlMappingTest.java b/src/test/java/com/amihaiemil/eoyaml/ReadYamlMappingTest.java
index 125e442..d4b93e4 100644
--- a/src/test/java/com/amihaiemil/eoyaml/ReadYamlMappingTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/ReadYamlMappingTest.java
@@ -40,7 +40,7 @@ import org.junit.Test;
 
 /**
  * Unit tests for {@link ReadYamlMapping}.
- * @checkstyle MethodName (1000 lines)
+ * @checkstyle MethodName (1500 lines)
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 1.0.0
@@ -494,8 +494,22 @@ public final class ReadYamlMappingTest {
         lines.add(new RtYamlLine("  some: mapping", 2));
         lines.add(new RtYamlLine("third: something", 3));
         final YamlMapping map = new ReadYamlMapping(new AllYamlLines(lines));
-        final YamlNode missing = map.value(new PlainStringScalar("notthere"));
-        MatcherAssert.assertThat(missing, Matchers.nullValue());
+        MatcherAssert.assertThat(
+            map.value(new PlainStringScalar("notthere")),
+            Matchers.nullValue()
+        );
+        MatcherAssert.assertThat(
+            map.string("notthere"),
+            Matchers.nullValue()
+        );
+        MatcherAssert.assertThat(
+            map.yamlMapping("notthere"),
+            Matchers.nullValue()
+        );
+        MatcherAssert.assertThat(
+            map.yamlSequence("notthere"),
+            Matchers.nullValue()
+        );
     }
 
     /**
diff --git a/src/test/java/com/amihaiemil/eoyaml/ReadYamlSequenceTest.java b/src/test/java/com/amihaiemil/eoyaml/ReadYamlSequenceTest.java
index 06da3c1..0651f54 100644
--- a/src/test/java/com/amihaiemil/eoyaml/ReadYamlSequenceTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/ReadYamlSequenceTest.java
@@ -211,6 +211,103 @@ public final class ReadYamlSequenceTest {
         MatcherAssert.assertThat(seq, Matchers.iterableWithSize(3));
     }
 
+    /**
+     * ReadYamlSequence can return null on a misread scalar.
+     */
+    @Test
+    public void returnsNullOnMisreadScalar(){
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("- ", 0));
+        lines.add(new RtYamlLine("  beta: somethingElse", 1));
+        lines.add(new RtYamlLine("- scalar", 2));
+        lines.add(new RtYamlLine("- ", 3));
+        lines.add(new RtYamlLine("  alfa: ", 4));
+        lines.add(new RtYamlLine("    fourth: some", 5));
+        lines.add(new RtYamlLine("    key: value", 6));
+        final YamlSequence sequence = new ReadYamlSequence(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            sequence.string(0),
+            Matchers.nullValue()
+        );
+    }
+
+    /**
+     * ReadYamlSequence can return null on a misread sequence.
+     */
+    @Test
+    public void returnsNullOnMisreadSequence(){
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("- ", 0));
+        lines.add(new RtYamlLine("  beta: somethingElse", 1));
+        lines.add(new RtYamlLine("- scalar", 2));
+        lines.add(new RtYamlLine("- ", 3));
+        lines.add(new RtYamlLine("  alfa: ", 4));
+        lines.add(new RtYamlLine("    fourth: some", 5));
+        lines.add(new RtYamlLine("    key: value", 6));
+        final YamlSequence sequence = new ReadYamlSequence(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            sequence.yamlSequence(0),
+            Matchers.nullValue()
+        );
+    }
+
+    /**
+     * ReadYamlSequence can return null on a misread sequence.
+     */
+    @Test
+    public void returnsNullOnMisreadMapping(){
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("- ", 0));
+        lines.add(new RtYamlLine("  beta: somethingElse", 1));
+        lines.add(new RtYamlLine("- scalar", 2));
+        lines.add(new RtYamlLine("- ", 3));
+        lines.add(new RtYamlLine("  alfa: ", 4));
+        lines.add(new RtYamlLine("    fourth: some", 5));
+        lines.add(new RtYamlLine("    key: value", 6));
+        final YamlSequence sequence = new ReadYamlSequence(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            sequence.yamlMapping(1),
+            Matchers.nullValue()
+        );
+    }
+
+    /**
+     * ReadYamlSequence can return null if the specified index does not
+     * exist.
+     */
+    @Test
+    public void returnsNullOnOutOfIndex(){
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("- ", 0));
+        lines.add(new RtYamlLine("  beta: somethingElse", 1));
+        lines.add(new RtYamlLine("- scalar", 2));
+        lines.add(new RtYamlLine("- ", 3));
+        lines.add(new RtYamlLine("  alfa: ", 4));
+        lines.add(new RtYamlLine("    fourth: some", 5));
+        lines.add(new RtYamlLine("    key: value", 6));
+        final YamlSequence sequence = new ReadYamlSequence(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            sequence.yamlMapping(10),
+            Matchers.nullValue()
+        );
+        MatcherAssert.assertThat(
+            sequence.string(10),
+            Matchers.nullValue()
+        );
+        MatcherAssert.assertThat(
+            sequence.yamlSequence(10),
+            Matchers.nullValue()
+        );
+    }
+
     /**
      * An empty ReadYamlSequence can be printed.
      * @throws Exception if something goes wrong
diff --git a/src/test/java/com/amihaiemil/eoyaml/YamlMappingPrintTest.java b/src/test/java/com/amihaiemil/eoyaml/YamlMappingPrintTest.java
index acd3264..521de7a 100644
--- a/src/test/java/com/amihaiemil/eoyaml/YamlMappingPrintTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/YamlMappingPrintTest.java
@@ -133,6 +133,135 @@ public final class YamlMappingPrintTest {
         );
     }
 
+    /**
+     * An empty YamlSequence value is printed as null.
+     */
+    @Test
+    public void printsEmptySequenceAsNull() {
+        final YamlMapping map = Yaml.createYamlMappingBuilder()
+            .add("key", "value1")
+            .add("seq", Yaml.createYamlSequenceBuilder().build())
+            .add("anotherKey", "value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("key: value1").append(System.lineSeparator())
+            .append("seq: null").append(System.lineSeparator())
+            .append("anotherKey: value2");
+        MatcherAssert.assertThat(
+            map.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An null YamlSequence value is printed as null.
+     */
+    @Test
+    public void printsNullSequenceAsNull() {
+        final YamlSequence seq = null;
+        final YamlMapping map = Yaml.createYamlMappingBuilder()
+            .add("key", "value1")
+            .add("seq", seq)
+            .add("anotherKey", "value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("key: value1").append(System.lineSeparator())
+            .append("seq: null").append(System.lineSeparator())
+            .append("anotherKey: value2");
+        MatcherAssert.assertThat(
+            map.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An empty YamlMapping value is printed as null.
+     */
+    @Test
+    public void printsEmptyMappingAsNull() {
+        final YamlMapping map = Yaml.createYamlMappingBuilder()
+            .add("key", "value1")
+            .add("map", Yaml.createYamlMappingBuilder().build())
+            .add("anotherKey", "value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("key: value1").append(System.lineSeparator())
+            .append("map: null").append(System.lineSeparator())
+            .append("anotherKey: value2");
+        MatcherAssert.assertThat(
+            map.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An null YamlMapping value is printed as null.
+     */
+    @Test
+    public void printsNullMappingAsNull() {
+        final YamlMapping nullMap = null;
+        final YamlMapping map = Yaml.createYamlMappingBuilder()
+            .add("key", "value1")
+            .add("map", nullMap)
+            .add("anotherKey", "value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("key: value1").append(System.lineSeparator())
+            .append("map: null").append(System.lineSeparator())
+            .append("anotherKey: value2");
+        MatcherAssert.assertThat(
+            map.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An empty Scalar value is printed as null.
+     */
+    @Test
+    public void printsEmptyScalarAsNull() {
+        final YamlMapping map = Yaml.createYamlMappingBuilder()
+            .add("key", "value1")
+            .add("scalar", Yaml.createYamlScalarBuilder().buildPlainScalar())
+            .add("anotherKey", "value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("key: value1").append(System.lineSeparator())
+            .append("scalar: null").append(System.lineSeparator())
+            .append("anotherKey: value2");
+        MatcherAssert.assertThat(
+            map.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An null Scalar value is printed as null.
+     */
+    @Test
+    public void printsNullScalarAsNull() {
+        final Scalar nullScalar = null;
+        final YamlMapping map = Yaml.createYamlMappingBuilder()
+            .add("key", "value1")
+            .add("scalar", nullScalar)
+            .add("anotherKey", "value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("key: value1").append(System.lineSeparator())
+            .append("scalar: null").append(System.lineSeparator())
+            .append("anotherKey: value2");
+        MatcherAssert.assertThat(
+            map.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
     /**
      * Read a test resource file's contents.
      * @param fileName File to read.
diff --git a/src/test/java/com/amihaiemil/eoyaml/YamlSequencePrintTest.java b/src/test/java/com/amihaiemil/eoyaml/YamlSequencePrintTest.java
index c113270..278b876 100644
--- a/src/test/java/com/amihaiemil/eoyaml/YamlSequencePrintTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/YamlSequencePrintTest.java
@@ -114,6 +114,135 @@ public final class YamlSequencePrintTest {
         );
     }
 
+    /**
+     * An empty YamlSequence value is printed as null.
+     */
+    @Test
+    public void printsEmptySequenceAsNull() {
+        final YamlSequence sequence = Yaml.createYamlSequenceBuilder()
+            .add("value1")
+            .add(Yaml.createYamlSequenceBuilder().build())
+            .add("value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("- value1").append(System.lineSeparator())
+            .append("- null").append(System.lineSeparator())
+            .append("- value2");
+        MatcherAssert.assertThat(
+            sequence.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An null YamlSequence value is printed as null.
+     */
+    @Test
+    public void printsNullSequenceAsNull() {
+        final YamlSequence seq = null;
+        final YamlSequence sequence = Yaml.createYamlSequenceBuilder()
+            .add("value1")
+            .add(seq)
+            .add("value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("- value1").append(System.lineSeparator())
+            .append("- null").append(System.lineSeparator())
+            .append("- value2");
+        MatcherAssert.assertThat(
+            sequence.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An empty YamlMapping value is printed as null.
+     */
+    @Test
+    public void printsEmptyMappingAsNull() {
+        final YamlSequence sequence = Yaml.createYamlSequenceBuilder()
+            .add("value1")
+            .add(Yaml.createYamlMappingBuilder().build())
+            .add("value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("- value1").append(System.lineSeparator())
+            .append("- null").append(System.lineSeparator())
+            .append("- value2");
+        MatcherAssert.assertThat(
+            sequence.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An null YamlMapping value is printed as null.
+     */
+    @Test
+    public void printsNullMappingAsNull() {
+        final YamlMapping nullMap = null;
+        final YamlSequence sequence = Yaml.createYamlSequenceBuilder()
+            .add("value1")
+            .add(nullMap)
+            .add("value2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("- value1").append(System.lineSeparator())
+            .append("- null").append(System.lineSeparator())
+            .append("- value2");
+        MatcherAssert.assertThat(
+            sequence.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An empty Scalar value is printed as null.
+     */
+    @Test
+    public void printsEmptyScalarAsNull() {
+        final YamlSequence sequence = Yaml.createYamlSequenceBuilder()
+            .add("scalar1")
+            .add(Yaml.createYamlScalarBuilder().buildPlainScalar())
+            .add("scalar2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("- scalar1").append(System.lineSeparator())
+            .append("- null").append(System.lineSeparator())
+            .append("- scalar2");
+        MatcherAssert.assertThat(
+            sequence.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
+    /**
+     * An null Scalar value is printed as null.
+     */
+    @Test
+    public void printsNullScalarAsNull() {
+        final Scalar nullScalar = null;
+        final YamlSequence sequence = Yaml.createYamlSequenceBuilder()
+            .add("scalar1")
+            .add(nullScalar)
+            .add("scalar2")
+            .build();
+        final StringBuilder expected = new StringBuilder();
+        expected
+            .append("- scalar1").append(System.lineSeparator())
+            .append("- null").append(System.lineSeparator())
+            .append("- scalar2");
+        MatcherAssert.assertThat(
+            sequence.toString(),
+            Matchers.equalTo(expected.toString())
+        );
+    }
+
     /**
      * Read a test resource file's contents.
      * @param fileName File to read.