Repository: decorators-squad/eo-yaml
Pull request #280 (2020-04-02 18:09:10)

List<Comment> => Comments
PR for #272 

As a beginning to being able to read comments, we introduced interface ``Comments`` (which is an ``Iterable<Comment>``) instead of a ``List<Comment>`` and its first implementation ``BuiltComments`` (for building)

Reading comments will be performed via the ``ReadComments`` implementation (next PR).


Patch:
diff --git a/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java
index f8611b5..4e7a5d2 100644
--- a/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/BaseYamlMapping.java
@@ -50,21 +50,21 @@ public abstract class BaseYamlMapping
     /**
      * Comments referring the key:value pairs.
      */
-    private List<Comment> keyComments;
+    private Comments comments;
 
     /**
      * Default ctor.
      */
     public BaseYamlMapping() {
-        this(new LinkedList<>());
+        this(new Comments.Empty());
     }
 
     /**
      * Constructor.
-     * @param keyComments Comment referring to the key: value entries.
+     * @param comments Comments referring to the key: value entries.
      */
-    public BaseYamlMapping(final List<Comment> keyComments) {
-        this.keyComments = keyComments;
+    public BaseYamlMapping(final Comments comments) {
+        this.comments = comments;
     }
 
     @Override
@@ -157,10 +157,8 @@ public abstract class BaseYamlMapping
     }
 
     @Override
-    public final List<Comment> keyComments() {
-        final List<Comment> comments = new ArrayList<>();
-        comments.addAll(this.keyComments);
-        return comments;
+    public final Comments comments() {
+        return this.comments;
     }
 
     /**
@@ -191,7 +189,9 @@ public abstract class BaseYamlMapping
         }
         this.printPossibleComment(this.comment(), print, alignment.toString());
         for(final YamlNode key : this.keys()) {
-            this.printPossibleKeyComment(key, print, alignment.toString());
+            this.printPossibleComment(
+                this.comments().referringTo(key), print, alignment.toString()
+            );
             print.append(alignment);
             final BaseYamlNode indKey = (BaseYamlNode) key;
             final BaseYamlNode value = (BaseYamlNode) this.value(key);
@@ -234,26 +234,6 @@ 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
diff --git a/src/main/java/com/amihaiemil/eoyaml/BaseYamlSequence.java b/src/main/java/com/amihaiemil/eoyaml/BaseYamlSequence.java
index 3c3b2c8..95a6d99 100644
--- a/src/main/java/com/amihaiemil/eoyaml/BaseYamlSequence.java
+++ b/src/main/java/com/amihaiemil/eoyaml/BaseYamlSequence.java
@@ -49,20 +49,20 @@ public abstract class BaseYamlSequence
     /**
      * Comments referring the elements of this YamlSequence.
      */
-    private List<Comment> comments;
+    private Comments comments;
 
     /**
      * Default ctor.
      */
     public BaseYamlSequence() {
-        this(new LinkedList<>());
+        this(new Comments.Empty());
     }
 
     /**
      * Constructor.
      * @param comments Comments referring to the elements of the sequence.
      */
-    public BaseYamlSequence(final List<Comment> comments) {
+    public BaseYamlSequence(final Comments comments) {
         this.comments = comments;
     }
 
@@ -144,10 +144,8 @@ public abstract class BaseYamlSequence
     }
 
     @Override
-    public final List<Comment> comments() {
-        final List<Comment> all = new ArrayList<>();
-        all.addAll(this.comments);
-        return all;
+    public final Comments comments() {
+        return this.comments;
     }
 
     /**
@@ -178,7 +176,11 @@ public abstract class BaseYamlSequence
         }
         this.printPossibleComment(this.comment(), print, alignment.toString());
         for (final YamlNode node : this.values()) {
-            this.printPossibleElementComment(node, print, alignment.toString());
+            this.printPossibleComment(
+                this.comments.referringTo(node),
+                print,
+                alignment.toString()
+            );
             print
                 .append(alignment)
                 .append("-");
@@ -198,26 +200,6 @@ public abstract class BaseYamlSequence
         return printed;
     }
 
-    /**
-     * Add the comment referring to the sequence element, if any,
-     * to the print.
-     * @param key Key in the YamlMapping.
-     * @param print Print.
-     * @param alignment Alignment
-     */
-    private void printPossibleElementComment(
-        final YamlNode key,
-        final StringBuilder print,
-        final String alignment
-    ) {
-        for(final Comment comment : this.comments) {
-            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
diff --git a/src/main/java/com/amihaiemil/eoyaml/BuiltComments.java b/src/main/java/com/amihaiemil/eoyaml/BuiltComments.java
new file mode 100644
index 0000000..46ffdeb
--- /dev/null
+++ b/src/main/java/com/amihaiemil/eoyaml/BuiltComments.java
@@ -0,0 +1,77 @@
+/**
+ * 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 java.util.Iterator;
+import java.util.List;
+
+/**
+ * Built Comments. These are comments within a YamlNode which
+ * have been added by the user when building it!
+ *
+ * Use this class when building YAML. For reading YAML, there
+ * should be an analogue implementation working with YamlLines.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+final class BuiltComments implements Comments {
+
+    /**
+     * The list of comments.
+     */
+    private List<Comment> comments;
+
+    /**
+     * Constructor.
+     * @param comments The comments.
+     */
+    BuiltComments(final List<Comment> comments) {
+        this.comments = comments;
+    }
+
+    @Override
+    public Iterator<Comment> iterator() {
+        return this.comments.iterator();
+    }
+
+    @Override
+    public Comment referringTo(final YamlNode node) {
+        Comment found = null;
+        for(final Comment comment : this.comments) {
+            if(comment.yamlNode().equals(node)) {
+                found = comment;
+                break;
+            }
+        }
+        if(found == null) {
+            found = new BuiltComment(node, "");
+        }
+        return found;
+    }
+}
diff --git a/src/main/java/com/amihaiemil/eoyaml/Comments.java b/src/main/java/com/amihaiemil/eoyaml/Comments.java
new file mode 100644
index 0000000..697ef1d
--- /dev/null
+++ b/src/main/java/com/amihaiemil/eoyaml/Comments.java
@@ -0,0 +1,84 @@
+/**
+ * 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 java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * These are all the comments within a YAML node.<br><br>
+ * For example, in the case of a mapping, they are the comments
+ * referring to the key:value pairs. In the case of a sequence,
+ * these are the comments referring to its elements.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+public interface Comments extends Iterable<Comment> {
+
+    /**
+     * Get the Comment referring to the specified YamlNode.
+     * @param plainScalar The plain scalar YAML node as String.
+     * @return Comment.
+     */
+    default Comment referringTo(final String plainScalar) {
+        return this.referringTo(
+            Yaml.createYamlScalarBuilder()
+                .addLine(plainScalar)
+                .buildPlainScalar()
+        );
+    }
+
+    /**
+     * Get the Comment referring to the specified YamlNode.
+     * @param node YamlNode for which we search the referring comment.
+     * @return Comment.
+     */
+    Comment referringTo(final YamlNode node);
+
+    /**
+     * Empty comments. Use this as an alternative to null, when you have no
+     * Comments to give.
+     * @author Mihai Andronache (amihaiemil@gmail.com)
+     * @version $Id$
+     * @since 4.2.0
+     */
+    class Empty implements Comments {
+
+        @Override
+        public Comment referringTo(final YamlNode node) {
+            return new BuiltComment(node, "");
+        }
+
+        @Override
+        public Iterator<Comment> iterator() {
+            return new ArrayList<Comment>().iterator();
+        }
+    }
+
+}
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java
index 77fb426..f228c23 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlMapping.java
@@ -68,7 +68,7 @@ final class RtYamlMapping extends BaseYamlMapping {
         final List<Comment> keyComments,
         final String comment
     ) {
-        super(keyComments);
+        super(new BuiltComments(keyComments));
         this.mappings.putAll(entries);
         this.comment = new BuiltComment(this, comment);
     }
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlSequence.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlSequence.java
index 90185cf..b242de6 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlSequence.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlSequence.java
@@ -67,7 +67,7 @@ final class RtYamlSequence extends BaseYamlSequence {
         final List<Comment> comments,
         final String comment
     ) {
-        super(comments);
+        super(new BuiltComments(comments));
         this.nodes.addAll(elements);
         this.comment = new BuiltComment(this, comment);
     }
diff --git a/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java
index f7196f5..58c31af 100644
--- a/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/YamlMapping.java
@@ -31,7 +31,6 @@ 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;
 
 /**
@@ -397,8 +396,8 @@ public interface YamlMapping extends YamlNode {
     /**
      * Comments referring to the key:value pairs of this
      * mapping.
-     * @return List of Comment which is empty if there are
-     *  no comments.
+     * @return Comments or Comments.Empty() if there are no
+     *  comments to be returned.
      */
-    List<Comment> keyComments();
+    Comments comments();
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/YamlSequence.java b/src/main/java/com/amihaiemil/eoyaml/YamlSequence.java
index 4b4401c..4063632 100644
--- a/src/main/java/com/amihaiemil/eoyaml/YamlSequence.java
+++ b/src/main/java/com/amihaiemil/eoyaml/YamlSequence.java
@@ -32,7 +32,6 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeParseException;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 
 /**
  * A Yaml sequence.
@@ -106,10 +105,10 @@ public interface YamlSequence extends YamlNode, Iterable<YamlNode> {
 
     /**
      * Comments referring to the elements of this sequence.
-     * @return List of Comment which is empty if there are
-     *  no comments.
+     * @return Comments or Comments.Empty if there are no comments
+     *  to return.
      */
-    List<Comment> comments();
+    Comments comments();
 
     /**
      * Convenience method to directly read an integer value


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/BuiltCommentsTest.java b/src/test/java/com/amihaiemil/eoyaml/BuiltCommentsTest.java
new file mode 100644
index 0000000..ee2b23a
--- /dev/null
+++ b/src/test/java/com/amihaiemil/eoyaml/BuiltCommentsTest.java
@@ -0,0 +1,98 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link BuiltComments}.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 4.2.0
+ */
+public final class BuiltCommentsTest {
+
+    /**
+     * BuiltComments can iterate over the comments.
+     */
+    @Test
+    public void iteratesOverComments() {
+        final List<Comment> comments = new ArrayList<>();
+        comments.add(Mockito.mock(Comment.class));
+        comments.add(Mockito.mock(Comment.class));
+        comments.add(Mockito.mock(Comment.class));
+        final Comments iterable = new BuiltComments(comments);
+        int count = 0;
+        for(final Comment com : iterable) {
+            count++;
+        }
+        MatcherAssert.assertThat(count, Matchers.equalTo(3));
+    }
+
+    /**
+     * BuiltComments can return the Comment referring to a given node.
+     */
+    @Test
+    public void returnsCommentReferringToNode() {
+        final List<Comment> all = new ArrayList<>();
+        all.add(
+            new BuiltComment(
+                new PlainStringScalar("node1"),
+                "test comment1"
+            )
+        );
+        all.add(
+            new BuiltComment(
+                new PlainStringScalar("node2"),
+                "test comment2"
+            )
+        );
+        all.add(
+            new BuiltComment(
+                new PlainStringScalar("node3"),
+                "test comment3"
+            )
+        );
+        final Comments comments = new BuiltComments(all);
+        MatcherAssert.assertThat(
+            comments.referringTo("node2").value(),
+            Matchers.equalTo("test comment2")
+        );
+        MatcherAssert.assertThat(
+            comments.referringTo("missing").value(),
+            Matchers.isEmptyString()
+        );
+    }
+
+}