Repository: decorators-squad/eo-yaml (java)

Issue:
Support Comments When Building
When building YAML, we should allow the user to specify comments at each element. E.g.

```java
YamlMapping map = Yaml.createYamlMappingBuilder()
    .add("author", "amihaiemil", "reach him at amihaiemil@gmail.com")
    .build();
map.toString();
```
```yaml
# reach him at amihaiemil@gmail.com
author: amihaiemil
```

Pull request:
Building YamlMapping with Comments
Fixes #261

* Implemented building of comments in YamlMapping
* implemented printing of comments
* tested printing of built YamlMapping with comments
* left puzzles to continue implementation.

Patch:
diff --git a/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java
index dc7af02..bbc91b3 100644
--- a/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java
@@ -27,7 +27,9 @@
  */
 package com.amihaiemil.eoyaml;
 
+import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 
@@ -47,6 +49,27 @@ import java.util.Set;
  */
 public abstract class BaseYamlMapping
     extends BaseYamlNode implements YamlMapping {
+
+    /**
+     * Comments on top of the key:value pairs.
+     */
+    private List<Comment> keyComments;
+
+    /**
+     * Default ctor.
+     */
+    public BaseYamlMapping() {
+        this(new ArrayList<>());
+    }
+
+    /**
+     * Constructor.
+     * @param keyComments Comment on top of the key: value entries.
+     */
+    public BaseYamlMapping(final List<Comment> keyComments) {
+        this.keyComments = keyComments;
+    }
+
     @Override
     public final int hashCode() {
         int hash = 0;
@@ -136,6 +159,16 @@ public abstract class BaseYamlMapping
         return result;
     }
 
+    /**
+     * Return the comments on top of the key:value pairs.
+     * @return List of Comment.
+     */
+    public final List<Comment> keyComments() {
+        final List<Comment> comments = new ArrayList<>();
+        comments.addAll(this.keyComments);
+        return comments;
+    }
+
     /**
      * Indent this YamlMapping. This is a base method since indentation
      * logic should be identical for any kind of YamlMapping, regardless of
@@ -162,7 +195,9 @@ public abstract class BaseYamlMapping
             alignment.append(" ");
             spaces--;
         }
+        this.printPossibleComment(this.comment(), print, alignment.toString());
         for(final YamlNode key : this.keys()) {
+            this.printPossibleKeyComment(key, print, alignment.toString());
             print.append(alignment);
             final BaseYamlNode indKey = (BaseYamlNode) key;
             final BaseYamlNode value = (BaseYamlNode) this.value(key);
@@ -205,6 +240,52 @@ public abstract class BaseYamlMapping
         return printed;
     }
 
+    /**
+     * Add the comment referring to the mapping key, if any,
+     * to the print.
+     * @param key Key in the YamlMapping.
+     * @param print Print.
+     * @param alignment Alignment
+     */
+    private void printPossibleKeyComment(
+        final YamlNode key,
+        final StringBuilder print,
+        final String alignment
+    ) {
+        for(final Comment comment : this.keyComments) {
+            if(key.equals(comment.yamlNode())) {
+                this.printPossibleComment(comment, print, alignment);
+                break;
+            }
+        }
+    }
+
+    /**
+     * 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 print Printer StringBuilder.
+     * @param alignment Indentation.
+     */
+    private void printPossibleComment(
+        final Comment comment,
+        final StringBuilder print,
+        final String alignment
+    ) {
+        final String com = comment.value();
+        if(com.trim().length()!=0) {
+            String[] lines = com.split(System.lineSeparator());
+            for(final String line : lines) {
+                print
+                    .append(alignment)
+                    .append("# ")
+                    .append(line)
+                    .append(System.lineSeparator());
+            }
+        }
+    }
+
     /**
      * Print a Scalar. We need to check what kind of Scalar is it.
      * If it's a plain scalar, we print it on the same line. If it's
diff --git a/src/main/java/com/amihaiemil/eoyaml/BuiltComment.java b/src/main/java/com/amihaiemil/eoyaml/BuiltComment.java
new file mode 100644
index 0000000..75bfa59
--- /dev/null
+++ b/src/main/java/com/amihaiemil/eoyaml/BuiltComment.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2016-2020, Mihai Emil Andronache
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ *  list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright notice,
+ *  this list of conditions and the following disclaimer in the documentation
+ *  and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+package com.amihaiemil.eoyaml;
+
+/**
+ * This is a comment used when building YAML.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+class BuiltComment implements Comment {
+
+    /**
+     * Node to which this comment refers.
+     */
+    private final YamlNode node;
+
+    /**
+     * The actual comment's value.
+     */
+    private final String value;
+
+    /**
+     * Constructor.
+     * @param node Yaml node to which this comment refers.
+     * @param value The String comment.
+     */
+    BuiltComment(final YamlNode node, final String value) {
+        this.node = node;
+        this.value = value;
+    }
+
+    @Override
+    public YamlNode yamlNode() {
+        return this.node;
+    }
+
+    @Override
+    public String value() {
+        return this.value;
+    }
+}
diff --git a/src/main/java/com/amihaiemil/eoyaml/Comment.java b/src/main/java/com/amihaiemil/eoyaml/Comment.java
new file mode 100644
index 0000000..aff7134
--- /dev/null
+++ b/src/main/java/com/amihaiemil/eoyaml/Comment.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2016-2020, Mihai Emil Andronache
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ *  list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright notice,
+ *  this list of conditions and the following disclaimer in the documentation
+ *  and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+package com.amihaiemil.eoyaml;
+
+/**
+ * A YAML comment.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+public interface Comment {
+    /**
+     * Yaml node to which this comment refers.
+     * @return YamlNode.
+     */
+    YamlNode yamlNode();
+
+    /**
+     * The actual comment.
+     * @return String value or empty String if there is
+     *  no comment. This method should always return the
+     *  comment without the "#" at the beginning. The hash symbol
+     *  will be added when printing.
+     */
+    String value();
+}
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java
index 6dc07cc..040457a 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java
@@ -110,6 +110,19 @@ final class ReadYamlMapping extends BaseYamlMapping {
         return value;
     }
 
+    /**
+     * Return this mapping's comment.
+     * @todo #261:30min Continue with the support for comments by implementing
+     *  the reading of comments in the case of a YamlMapping. Comments for
+     *  key:value pairs are already implemented in BaseYamlMapping.
+     * @return Comment.
+     */
+    @Override
+    public Comment comment() {
+        final Comment empty = new BuiltComment(this, "");
+        return empty;
+    }
+
     @Override
     public YamlMapping yamlMapping(final YamlNode key) {
         final YamlMapping found;
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java
index 6faf20f..e2d5534 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java
@@ -38,6 +38,11 @@ import java.util.*;
  */
 final class RtYamlMapping extends BaseYamlMapping {
 
+    /**
+     * Comments on top of this mapping.
+     */
+    private Comment comment;
+
     /**
      * Key:value linked map (maintains the order of insertion).
      */
@@ -49,7 +54,23 @@ final class RtYamlMapping extends BaseYamlMapping {
      * @param entries Entries contained in this mapping.
      */
     RtYamlMapping(final Map<YamlNode, YamlNode> entries) {
+        this(entries, new ArrayList<>(), "");
+    }
+
+    /**
+     * Ctor.
+     * @param entries Entries contained in this mapping.
+     * @param keyComments Comments on top of the key: value pairs.
+     * @param comment Comment on top of this YamlMapping.
+     */
+    RtYamlMapping(
+        final Map<YamlNode, YamlNode> entries,
+        final List<Comment> keyComments,
+        final String comment
+    ) {
+        super(keyComments);
         this.mappings.putAll(entries);
+        this.comment = new BuiltComment(this, comment);
     }
 
     @Override
@@ -69,6 +90,11 @@ final class RtYamlMapping extends BaseYamlMapping {
         return this.mappings.get(key);
     }
 
+    @Override
+    public Comment comment() {
+        return this.comment;
+    }
+
     @Override
     public YamlMapping yamlMapping(final YamlNode key) {
         final YamlNode value = this.mappings.get(key);
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlMappingBuilder.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlMappingBuilder.java
index 774d7db..36daee3 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlMappingBuilder.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlMappingBuilder.java
@@ -28,6 +28,8 @@
 package com.amihaiemil.eoyaml;
 
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -45,50 +47,50 @@ final class RtYamlMappingBuilder implements YamlMappingBuilder {
      */
     private final Map<YamlNode, YamlNode> pairs;
 
+    /**
+     * Comments on top of the key:value pairs.
+     */
+    private final List<Comment> comments;
+
     /**
      * Default ctor.
      */
     RtYamlMappingBuilder() {
-        this(new LinkedHashMap<>());
+        this(new LinkedHashMap<>(), new LinkedList<>());
     }
 
     /**
      * Constructor.
      * @param pairs Pairs used in building the YamlMapping.
+     * @param comments Comments on top of the key:value pairs.
      */
-    RtYamlMappingBuilder(final Map<YamlNode, YamlNode> pairs) {
+    RtYamlMappingBuilder(
+        final Map<YamlNode, YamlNode> pairs,
+        final List<Comment> comments
+    ) {
         this.pairs = pairs;
+        this.comments = comments;
     }
 
     @Override
-    public YamlMappingBuilder add(final String key, final String value) {
-        return this.add(
-            new PlainStringScalar(key),
-            new PlainStringScalar(value)
-        );
-    }
-
-    @Override
-    public YamlMappingBuilder add(final YamlNode key, final String value) {
-        return this.add(key, new PlainStringScalar(value));
-    }
-
-    @Override
-    public YamlMappingBuilder add(final String key, final YamlNode value) {
-        return this.add(new PlainStringScalar(key), value);
-    }
-
-    @Override
-    public YamlMappingBuilder add(final YamlNode key, final YamlNode value) {
+    public YamlMappingBuilder add(
+        final YamlNode key,
+        final YamlNode value,
+        final String comment
+    ) {
         final Map<YamlNode, YamlNode> withAddedPair = new LinkedHashMap<>();
         withAddedPair.putAll(this.pairs);
         withAddedPair.put(key, value);
-        return new RtYamlMappingBuilder(withAddedPair);
+
+        final List<Comment> withAddedComment = new LinkedList<>();
+        withAddedComment.addAll(this.comments);
+        withAddedComment.add(new BuiltComment(key, comment));
+        return new RtYamlMappingBuilder(withAddedPair, withAddedComment);
     }
 
     @Override
-    public YamlMapping build() {
-        return new RtYamlMapping(this.pairs);
+    public YamlMapping build(final String comment) {
+        return new RtYamlMapping(this.pairs, this.comments, comment);
     }
 
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlScalarBuilder.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlScalarBuilder.java
index 2b388bb..71fa865 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlScalarBuilder.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlScalarBuilder.java
@@ -70,7 +70,7 @@ final class RtYamlScalarBuilder implements YamlScalarBuilder {
 
     @Override
     public Scalar buildPlainScalar() {
-        final String plain = this.lines.stream().map(
+        final String plain = this.lines.stream().filter(line -> line!=null).map(
             line -> line.replaceAll(System.lineSeparator(), " ")
         ).collect(Collectors.joining(" "));
         return new PlainStringScalar(plain);
diff --git a/src/main/java/com/amihaiemil/eoyaml/StrictYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/StrictYamlMapping.java
index 100161e..fdbc45a 100644
--- a/src/main/java/com/amihaiemil/eoyaml/StrictYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/StrictYamlMapping.java
@@ -137,4 +137,9 @@ public final class StrictYamlMapping extends BaseYamlMapping {
         }
         return found;
     }
+
+    @Override
+    public Comment comment() {
+        return this.decorated.comment();
+    }
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java
index 9065da8..6a49b10 100644
--- a/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java
@@ -31,6 +31,7 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeParseException;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -386,4 +387,17 @@ public interface YamlMapping extends YamlNode {
         }
         return null;
     }
+
+    /**
+     * Comment referring to this YamlMapping.
+     * @return Comment.
+     */
+    Comment comment();
+
+    /**
+     * Comments referring to the key:value pairs of this
+     * mapping.
+     * @return List of Comment.
+     */
+    List<Comment> keyComments();
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/YamlMappingBuilder.java b/src/main/java/com/amihaiemil/eoyaml/YamlMappingBuilder.java
index ba762f4..a8b97b5 100644
--- a/src/main/java/com/amihaiemil/eoyaml/YamlMappingBuilder.java
+++ b/src/main/java/com/amihaiemil/eoyaml/YamlMappingBuilder.java
@@ -41,7 +41,28 @@ public interface YamlMappingBuilder {
      * @param value String
      * @return This builder
      */
-    YamlMappingBuilder add(final String key, final String value);
+    default YamlMappingBuilder add(final String key, final String value) {
+        return this.add(key, value, "");
+    }
+
+    /**
+     * Add a pair to the mapping.
+     * @param key String
+     * @param value String
+     * @param comment Comment that will appear on top of the pair.
+     * @return This builder
+     */
+    default YamlMappingBuilder add(
+        final String key,
+        final String value,
+        final String comment
+    ) {
+        return this.add(
+            Yaml.createYamlScalarBuilder().addLine(key).buildPlainScalar(),
+            Yaml.createYamlScalarBuilder().addLine(value).buildPlainScalar(),
+            comment
+        );
+    }
 
     /**
      * Add a pair to the mapping.
@@ -49,27 +70,93 @@ public interface YamlMappingBuilder {
      * @param value String
      * @return This builder
      */
-    YamlMappingBuilder add(final YamlNode key, final String value);
+    default YamlMappingBuilder add(final YamlNode key, final String value) {
+        return this.add(key, value, "");
+    }
 
     /**
      * Add a pair to the mapping.
      * @param key YamlNode (sequence or mapping)
+     * @param value String
+     * @param comment Comment that will appear on top of the pair.
+     * @return This builder
+     */
+    default YamlMappingBuilder add(
+        final YamlNode key,
+        final String value,
+        final String comment
+    ) {
+        return this.add(
+            key,
+            Yaml.createYamlScalarBuilder().addLine(value).buildPlainScalar(),
+            comment
+        );
+    }
+
+    /**
+     * Add a pair to the mapping.
+     * @param key String
      * @param value YamlNode (sequence or mapping)
      * @return This builder
      */
-    YamlMappingBuilder add(final YamlNode key, final YamlNode value);
+    default YamlMappingBuilder add(final String key, final YamlNode value) {
+        return this.add(key, value, "");
+    }
 
     /**
      * Add a pair to the mapping.
      * @param key String
      * @param value YamlNode (sequence or mapping)
+     * @param comment Comment that will appear on top of the pair.
      * @return This builder
      */
-    YamlMappingBuilder add(final String key, final YamlNode value);
+    default YamlMappingBuilder add(
+        final String key,
+        final YamlNode value,
+        final String comment
+    ) {
+        return this.add(
+            Yaml.createYamlScalarBuilder().addLine(key).buildPlainScalar(),
+            value,
+            comment
+        );
+    }
+
+    /**
+     * Add a pair to the mapping.
+     * @param key YamlNode (sequence or mapping)
+     * @param value YamlNode (sequence or mapping)
+     * @return This builder
+     */
+    default YamlMappingBuilder add(final YamlNode key, final YamlNode value) {
+        return this.add(key, value, "");
+    }
+
+    /**
+     * Add a pair to the mapping.
+     * @param key YamlNode (sequence or mapping)
+     * @param value YamlNode (sequence or mapping)
+     * @param comment Comment that will appear on top of this pair.
+     * @return This builder
+     */
+    YamlMappingBuilder add(
+        final YamlNode key,
+        final YamlNode value,
+        final String comment
+    );
+
+    /**
+     * Build the YamlMapping.
+     * @return Built YamlMapping.
+     */
+    default YamlMapping build() {
+        return this.build("");
+    }
 
     /**
      * Build the YamlMapping.
+     * @param comment Comment on top of the YamlMapping.
      * @return Built YamlMapping.
      */
-    YamlMapping build();
+    YamlMapping build(final String comment);
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/extensions/MergedYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/extensions/MergedYamlMapping.java
index b347440..0078936 100644
--- a/src/main/java/com/amihaiemil/eoyaml/extensions/MergedYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/extensions/MergedYamlMapping.java
@@ -51,6 +51,10 @@ import java.util.function.Supplier;
  *         true|false
  *     );
  * </pre>
+ * @todo #261:30min Find a way to also "merge" the comments from the mappings'
+ *  key:value pairs. The comment referring to the YamlMapping itself will
+ *  either be taken from the original YamlMapping or a new one can be accepted
+ *  via the constructor.
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 4.1.0
@@ -137,6 +141,11 @@ public final class MergedYamlMapping extends BaseYamlMapping {
         return this.merged.value(key);
     }
 
+    @Override
+    public Comment comment() {
+        return this.merged.comment();
+    }
+
     @Override
     public YamlMapping yamlMapping(final YamlNode key) {
         return this.merged.yamlMapping(key);


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/BuiltCommentTest.java b/src/test/java/com/amihaiemil/eoyaml/BuiltCommentTest.java
new file mode 100644
index 0000000..627ad87
--- /dev/null
+++ b/src/test/java/com/amihaiemil/eoyaml/BuiltCommentTest.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2016-2020, Mihai Emil Andronache
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ *  list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright notice,
+ *  this list of conditions and the following disclaimer in the documentation
+ *  and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+package com.amihaiemil.eoyaml;
+
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Unit tests for {@link BuiltComment}.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+public final class BuiltCommentTest {
+
+    /**
+     * BuiltComment can return the encapsulated node.
+     */
+    @Test
+    public void returnsYamlNode() {
+        final YamlNode node = Mockito.mock(YamlNode.class);
+        MatcherAssert.assertThat(
+            new BuiltComment(node, "test").yamlNode(),
+            Matchers.is(node)
+        );
+    }
+
+    /**
+     * BuiltComment can return the encapsulated String comment.
+     */
+    @Test
+    public void returnsValue() {
+        MatcherAssert.assertThat(
+            new BuiltComment(Mockito.mock(YamlNode.class), "test").value(),
+            Matchers.equalTo("test")
+        );
+    }
+}
diff --git a/src/test/java/com/amihaiemil/eoyaml/YamlMappingCommentsPrintTest.java b/src/test/java/com/amihaiemil/eoyaml/YamlMappingCommentsPrintTest.java
new file mode 100644
index 0000000..bd67c22
--- /dev/null
+++ b/src/test/java/com/amihaiemil/eoyaml/YamlMappingCommentsPrintTest.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2016-2020, Mihai Emil Andronache
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ *  list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright notice,
+ *  this list of conditions and the following disclaimer in the documentation
+ *  and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+package com.amihaiemil.eoyaml;
+
+import org.apache.commons.io.IOUtils;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Test cases for printing a YamlMapping together with its
+ * added or read comments.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+public final class YamlMappingCommentsPrintTest {
+
+    /**
+     * A built YamlMapping with added comments is being
+     * printed properly.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void printsBuiltYamlMappingWithComments() throws Exception {
+        final YamlMapping commented = Yaml.createYamlMappingBuilder()
+            .add("architect", "mihai", "the Dictator")
+            .add(
+                "developers",
+                Yaml.createYamlSequenceBuilder()
+                    .add("rultor")
+                    .add("salikjan")
+                    .add("sherif")
+                    .build(),
+                "all the contributors here"
+            )
+            .add(
+                "name",
+                "eo-yaml",
+                "name of the project"
+            ).build("YamlMapping for test");
+        System.out.println(commented);
+        MatcherAssert.assertThat(
+            commented.toString(),
+            Matchers.equalTo(
+                this.readExpected("commentedMapping.yml")
+            )
+        );
+    }
+
+    /**
+     * Read a test resource file's contents.
+     * @param fileName File to read.
+     * @return File's contents as String.
+     * @throws FileNotFoundException If something is wrong.
+     * @throws IOException If something is wrong.
+     */
+    private String readExpected(final String fileName)
+        throws FileNotFoundException, IOException {
+        return new String(
+            IOUtils.toByteArray(
+                new FileInputStream(
+                    new File(
+                        "src/test/resources/" + fileName
+                    )
+                )
+            )
+        );
+    }
+}
diff --git a/src/test/resources/commentedMapping.yml b/src/test/resources/commentedMapping.yml
index d7433ec..12b6dbe 100644
--- a/src/test/resources/commentedMapping.yml
+++ b/src/test/resources/commentedMapping.yml
@@ -1,7 +1,10 @@
-# Yaml for test
+# YamlMapping for test
+# the Dictator
 architect: mihai
-developers: # all the contributors here
-  - rultor # bot used for devops
+# all the contributors here
+developers:
+  - rultor
   - salikjan
   - sherif
+# name of the project
 name: eo-yaml
\ No newline at end of file