Repository: decorators-squad/eo-yaml
Pull request #622 (2024-04-13 16:21:54)

#618 comment() implemented in Flow nodes
PR for #618 
First flow nodes can now fetch their parent comment (comment referring to them) or the document comment if they are the only nodes in the file.


Patch:
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadFlowMapping.java b/src/main/java/com/amihaiemil/eoyaml/ReadFlowMapping.java
index 6bc75c7..b8c9195 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ReadFlowMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadFlowMapping.java
@@ -41,11 +41,19 @@ import java.util.Set;
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 8.0.0
- * @todo #615:60min Implement the comment() method properly (at the moment
- *  it always assumes there is no comment).
  */
 final class ReadFlowMapping extends BaseYamlMapping {
 
+    /**
+     * All the lines of the YAML document.
+     */
+    private final AllYamlLines all;
+
+    /**
+     * Previous line just before the one where this flow sequence starts.
+     */
+    private final YamlLine previous;
+
     /**
      * The entries of this flow mapping as String.
      */
@@ -84,7 +92,9 @@ final class ReadFlowMapping extends BaseYamlMapping {
                 ),
                 '{',
                 '}'
-            ).line(previous.number() < 0 ? 0 : previous.number() + 1)
+            ).line(previous.number() < 0 ? 0 : previous.number() + 1),
+            previous,
+            lines
         );
     }
 
@@ -92,8 +102,14 @@ final class ReadFlowMapping extends BaseYamlMapping {
      * Constructor.
      * @param folded All the YAML lines of this flow mapping,
      *  folded into a single one.
+     * @param previous Line previous to where this flow mapping starts.
+     * @param all All the lines of the YAML document.
      */
-    ReadFlowMapping(final YamlLine folded) {
+    ReadFlowMapping(
+        final YamlLine folded, final YamlLine previous, final AllYamlLines all
+    ) {
+        this.previous = previous;
+        this.all = all;
         this.entries = new StringEntries(folded);
         this.folded = folded;
     }
@@ -123,7 +139,33 @@ final class ReadFlowMapping extends BaseYamlMapping {
 
     @Override
     public Comment comment() {
-        return new BuiltComment(this, "");
+        boolean documentComment = this.previous.number() < 0;
+        //@checkstyle LineLength (50 lines)
+        return new ReadComment(
+            new Backwards(
+                new FirstCommentFound(
+                    new Backwards(
+                        new Skip(
+                            this.all,
+                            line -> {
+                                final boolean skip;
+                                if(documentComment) {
+                                    skip = line.number() >= this.folded.number();
+                                } else {
+                                    skip = line.number() >= this.previous.number();
+                                }
+                                return skip;
+                            },
+                            line -> line.trimmed().startsWith("..."),
+                            line -> line.trimmed().startsWith("%"),
+                            line -> line.trimmed().startsWith("!!")
+                        )
+                    ),
+                    documentComment
+                )
+            ),
+            this
+        );
     }
 
     /**
@@ -135,11 +177,15 @@ final class ReadFlowMapping extends BaseYamlMapping {
         final YamlNode yaml;
         if (node.startsWith("[")) {
             yaml = new ReadFlowSequence(
-                new RtYamlLine(node, this.folded.number())
+                new RtYamlLine(node, this.folded.number()),
+                this.previous,
+                this.all
             );
         } else if (node.startsWith("{")) {
             yaml = new ReadFlowMapping(
-                new RtYamlLine(node, this.folded.number())
+                new RtYamlLine(node, this.folded.number()),
+                this.previous,
+                this.all
             );
         } else {
             yaml = new PlainStringScalar(node.trim());
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadFlowSequence.java b/src/main/java/com/amihaiemil/eoyaml/ReadFlowSequence.java
index 1ebec36..daf472e 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ReadFlowSequence.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadFlowSequence.java
@@ -40,6 +40,16 @@ import java.util.List;
  */
 final class ReadFlowSequence extends BaseYamlSequence {
 
+    /**
+     * All the lines of the document.
+     */
+    private final AllYamlLines all;
+
+    /**
+     * Previous line just before the one where this flow sequence starts.
+     */
+    private final YamlLine previous;
+
     /**
      * The entries of this flow sequence as String.
      */
@@ -61,14 +71,14 @@ final class ReadFlowSequence extends BaseYamlSequence {
     /**
      * Ctor.
      * @param previous Line just before the start of this flow sequence.
-     * @param lines All lines of the YAML document.
+     * @param all All lines of the YAML document.
      * @checkstyle AvoidInlineConditionals (30 lines)
      */
-    ReadFlowSequence(final YamlLine previous, final AllYamlLines lines) {
+    ReadFlowSequence(final YamlLine previous, final AllYamlLines all) {
         this(
             new CollapsedFlowLines(
                 new Skip(
-                    lines,
+                    all,
                     line -> line.number() <= previous.number(),
                     line -> line.trimmed().startsWith("#"),
                     line -> line.trimmed().startsWith("---"),
@@ -78,7 +88,9 @@ final class ReadFlowSequence extends BaseYamlSequence {
                 ),
                 '[',
                 ']'
-            ).line(previous.number() < 0 ? 0 : previous.number() + 1)
+            ).line(previous.number() < 0 ? 0 : previous.number() + 1),
+            previous,
+            all
         );
     }
 
@@ -86,8 +98,14 @@ final class ReadFlowSequence extends BaseYamlSequence {
      * Constructor.
      * @param folded All the YAML lines of this flow sequence,
      *  folded into a single one.
+     * @param previous Line previous to where this flow mapping starts.
+     * @param all All the lines of the YAML document.
      */
-    ReadFlowSequence(final YamlLine folded) {
+    ReadFlowSequence(
+        final YamlLine folded, final YamlLine previous, final AllYamlLines all
+    ) {
+        this.previous = previous;
+        this.all = all;
         this.entries = new StringNodes(folded);
         this.folded = folded;
     }
@@ -99,13 +117,17 @@ final class ReadFlowSequence extends BaseYamlSequence {
             if (node.startsWith("[")) {
                 kids.add(
                     new ReadFlowSequence(
-                        new RtYamlLine(node, this.folded.number())
+                        new RtYamlLine(node, this.folded.number()),
+                        this.previous,
+                        this.all
                     )
                 );
             } else if(node.startsWith("{")) {
                 kids.add(
                     new ReadFlowMapping(
-                        new RtYamlLine(node, this.folded.number())
+                        new RtYamlLine(node, this.folded.number()),
+                        this.previous,
+                        this.all
                     )
                 );
             } else {
@@ -119,7 +141,33 @@ final class ReadFlowSequence extends BaseYamlSequence {
 
     @Override
     public Comment comment() {
-        return new BuiltComment(this, "");
+        boolean documentComment = this.previous.number() < 0;
+        //@checkstyle LineLength (50 lines)
+        return new ReadComment(
+            new Backwards(
+                new FirstCommentFound(
+                    new Backwards(
+                        new Skip(
+                            this.all,
+                            line -> {
+                                final boolean skip;
+                                if(documentComment) {
+                                    skip = line.number() >= this.folded.number();
+                                } else {
+                                    skip = line.number() >= this.previous.number();
+                                }
+                                return skip;
+                            },
+                            line -> line.trimmed().startsWith("..."),
+                            line -> line.trimmed().startsWith("%"),
+                            line -> line.trimmed().startsWith("!!")
+                        )
+                    ),
+                    documentComment
+                )
+            ),
+            this
+        );
     }
 
     /**


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/ReadFlowMappingTestCase.java b/src/test/java/com/amihaiemil/eoyaml/ReadFlowMappingTestCase.java
index f19c9fc..c7c9580 100644
--- a/src/test/java/com/amihaiemil/eoyaml/ReadFlowMappingTestCase.java
+++ b/src/test/java/com/amihaiemil/eoyaml/ReadFlowMappingTestCase.java
@@ -30,8 +30,6 @@ package com.amihaiemil.eoyaml;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.Test;
-import org.mockito.Mockito;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
@@ -46,16 +44,50 @@ import java.util.Set;
 public final class ReadFlowMappingTestCase {
 
     /**
-     * YamlMapping in flow format has no comment.
+     * ReadFlowMapping can return the document comment.
      */
     @Test
-    public void hasNoComment() {
+    public void hasDocumentComment() {
         final YamlMapping map = new ReadFlowMapping(
-            Mockito.mock(YamlLine.class)
+            new RtYamlLine("{a: b, c: d, e: f}", 2),
+            new RtYamlLine("---", 1),
+            new AllYamlLines(
+                Arrays.asList(
+                    new RtYamlLine("# this is a flow mapping document", 0),
+                    new RtYamlLine("---", 1),
+                    new RtYamlLine("{a: b, c: d, e: f}", 2)
+                )
+            )
+        );
+        MatcherAssert.assertThat(
+            map.comment().value(),
+            Matchers.equalTo("this is a flow mapping document")
+        );
+        MatcherAssert.assertThat(
+            map.comment().yamlNode(),
+            Matchers.is(map)
+        );
+    }
+
+    /**
+     * ReadFlowMapping can return the comment referring to it.
+     */
+    @Test
+    public void hasOwnNodeComment() {
+        final YamlMapping map = new ReadFlowMapping(
+            new RtYamlLine("{a: {i: j}, c: d, e: f}", 2),
+            new RtYamlLine("flow:", 1),
+            new AllYamlLines(
+                Arrays.asList(
+                    new RtYamlLine("# this comment about the 'flow' map", 0),
+                    new RtYamlLine("flow:", 1),
+                    new RtYamlLine("  {a: {i: j}, c: d, e: f}", 2)
+                )
+            )
         );
         MatcherAssert.assertThat(
             map.comment().value(),
-            Matchers.isEmptyString()
+            Matchers.equalTo("this comment about the 'flow' map")
         );
         MatcherAssert.assertThat(
             map.comment().yamlNode(),
diff --git a/src/test/java/com/amihaiemil/eoyaml/ReadFlowSequenceTestCase.java b/src/test/java/com/amihaiemil/eoyaml/ReadFlowSequenceTestCase.java
index ca4b720..78e32e0 100644
--- a/src/test/java/com/amihaiemil/eoyaml/ReadFlowSequenceTestCase.java
+++ b/src/test/java/com/amihaiemil/eoyaml/ReadFlowSequenceTestCase.java
@@ -32,6 +32,7 @@ import org.hamcrest.Matchers;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
@@ -45,16 +46,50 @@ import java.util.Iterator;
 public final class ReadFlowSequenceTestCase {
 
     /**
-     * Sequences in flow/json style have no comment.
+     * ReadFlowSequence can return the document comment.
      */
     @Test
-    public void hasNoComment() {
+    public void hasDocumentComment() {
         final YamlSequence seq = new ReadFlowSequence(
-            Mockito.mock(YamlLine.class)
+            new RtYamlLine("[{a: b}, {c: d}, {e: f}]", 2),
+            new RtYamlLine("---", 1),
+            new AllYamlLines(
+                Arrays.asList(
+                    new RtYamlLine("# this is a flow sequence document", 0),
+                    new RtYamlLine("---", 1),
+                    new RtYamlLine("[{a: b}, {c: d}, {e: f}]", 2)
+                )
+            )
         );
         MatcherAssert.assertThat(
             seq.comment().value(),
-            Matchers.isEmptyString()
+            Matchers.equalTo("this is a flow sequence document")
+        );
+        MatcherAssert.assertThat(
+            seq.comment().yamlNode(),
+            Matchers.is(seq)
+        );
+    }
+
+    /**
+     * ReadFlowMapping can return the comment referring to it.
+     */
+    @Test
+    public void hasOwnNodeComment() {
+        final YamlSequence seq = new ReadFlowSequence(
+            new RtYamlLine("[{a: b}, {c: d}, {e: f}]", 2),
+            new RtYamlLine("flow_seq:", 1),
+            new AllYamlLines(
+                Arrays.asList(
+                    new RtYamlLine("# this comment about the 'flow' seq", 0),
+                    new RtYamlLine("flow:", 1),
+                    new RtYamlLine("  [{a: b}, {c: d}, {e: f}]", 2)
+                )
+            )
+        );
+        MatcherAssert.assertThat(
+            seq.comment().value(),
+            Matchers.equalTo("this comment about the 'flow' seq")
         );
         MatcherAssert.assertThat(
             seq.comment().yamlNode(),
@@ -68,7 +103,9 @@ public final class ReadFlowSequenceTestCase {
     @Test
     public void hasNoValues() {
         final YamlSequence seq = new ReadFlowSequence(
-            new RtYamlLine("[]", 0)
+            new RtYamlLine("[]", 0),
+            Mockito.mock(YamlLine.class),
+            new AllYamlLines(new ArrayList<>())
         );
         MatcherAssert.assertThat(
             seq.values(),
@@ -82,7 +119,9 @@ public final class ReadFlowSequenceTestCase {
     @Test
     public void hasOnlyScalars() {
         final YamlSequence seq = new ReadFlowSequence(
-            new RtYamlLine("[a, b, c, d:e]", 0)
+            new RtYamlLine("[a, b, c, d:e]", 0),
+            Mockito.mock(YamlLine.class),
+            new AllYamlLines(new ArrayList<>())
         );
         final Collection<YamlNode> values = seq.values();
         MatcherAssert.assertThat(values, Matchers.iterableWithSize(4));
@@ -111,7 +150,9 @@ public final class ReadFlowSequenceTestCase {
     @Test
     public void hasOnlySequences() {
         final YamlSequence seq = new ReadFlowSequence(
-            new RtYamlLine("[[a, b], [c,\"[a]\",'b]['], [d]]", 0)
+            new RtYamlLine("[[a, b], [c,\"[a]\",'b]['], [d]]", 0),
+            Mockito.mock(YamlLine.class),
+            new AllYamlLines(new ArrayList<>())
         );
         System.out.println(seq);
         MatcherAssert.assertThat(
@@ -161,7 +202,9 @@ public final class ReadFlowSequenceTestCase {
         final YamlSequence seq = new ReadFlowSequence(
             new RtYamlLine(
                 "[scalar, \"u][\", 'v}{', 'escalar', [a, b], other, [d]]", 0
-            )
+            ),
+            Mockito.mock(YamlLine.class),
+            new AllYamlLines(new ArrayList<>())
         );
         System.out.println(seq);
         MatcherAssert.assertThat(