Repository: decorators-squad/eo-yaml
Pull request #207 (2020-03-22 16:00:41)

Reading of YamlStream
PR for #90

- ``YamlStream`` implements default methods for the Stream API
- ``ReadYamlStream``
- Implemented ``ReadYamlStream.values()`` with the help of ``StartMarkers`` lines decorator
- unit tests
- left puzzles to fix some special cases


Patch:
diff --git a/pom.xml b/pom.xml
index b2fec42..beb2607 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,7 +140,7 @@
                         </executions>
                         <configuration>
                             <excludes>
-                                <exclude>**/*RtYamlStreamBuilder$*.*</exclude>
+                                <exclude>**/YamlStream.*</exclude>
                             </excludes>
                         </configuration>
                     </plugin>
diff --git a/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java b/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java
index 81a7fc5..0b1535d 100644
--- a/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java
+++ b/src/main/java/com/amihaiemil/eoyaml/AllYamlLines.java
@@ -36,6 +36,7 @@ import java.util.Collection;
  * at the same indentation level and for that we use the decorator
  * {@link SameIndentationLevel}.
  * @checkstyle ExecutableStatementCount (400 lines)
+ * @checkstyle CyclomaticComplexity (400 lines)
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 1.0.0
@@ -94,7 +95,7 @@ final class AllYamlLines implements YamlLines {
                 }
                 break;
             case Nested.SEQUENCE:
-                if(trimmed.length() == 1) {
+                if(trimmed.length() == 1 || "---".equals(trimmed)) {
                     final boolean elementSequence = this.iterator()
                         .next().trimmed().startsWith("-");
                     if(elementSequence) {
diff --git a/src/main/java/com/amihaiemil/eoyaml/ComparableYamlStream.java b/src/main/java/com/amihaiemil/eoyaml/ComparableYamlStream.java
index f075c8a..24eafd3 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ComparableYamlStream.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ComparableYamlStream.java
@@ -119,4 +119,9 @@ abstract class ComparableYamlStream implements YamlStream {
         }
         return result;
     }
+
+    @Override
+    public String toString() {
+        return this.indent(0);
+    }
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadYamlStream.java b/src/main/java/com/amihaiemil/eoyaml/ReadYamlStream.java
new file mode 100644
index 0000000..cbe599e
--- /dev/null
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadYamlStream.java
@@ -0,0 +1,74 @@
+/**
+ * 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.Collection;
+import java.util.List;
+
+/**
+ * Read YAML Stream of documents.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 3.1.4
+ */
+final class ReadYamlStream extends ComparableYamlStream {
+
+    /**
+     * Read lines of this YAML Stream.
+     */
+    private final YamlLines lines;
+
+    /**
+     * Constructor.
+     * @todo #90:30min Check and see how we can use the {@link WellIndented}
+     *  decorator here, to validate that the lines have correct indentation.
+     *  Maybe we will need to do some changes inside WellIndented, since in the
+     *  case of a YAML Stream, the lines after the Start Mark can be more
+     *  indented or they can have the same indentation as the mark.
+     * @param lines All YAML lines as they are read from the input.
+     */
+    ReadYamlStream(final AllYamlLines lines) {
+        this.lines = new StartMarkers(lines);
+    }
+
+    @Override
+    public Collection<YamlNode> values() {
+        final List<YamlNode> values = new ArrayList<>();
+        for(final YamlLine startDoc : this.lines) {
+            values.add(
+                this.lines
+                    .nested(startDoc.number())
+                    .toYamlNode(startDoc)
+            );
+        }
+        return values;
+
+    }
+
+}
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlInput.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlInput.java
index 63923dd..cee2538 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlInput.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlInput.java
@@ -65,6 +65,11 @@ final class RtYamlInput implements YamlInput {
         return new ReadYamlSequence(this.readInput());
     }
 
+    @Override
+    public YamlStream readYamlStream() throws IOException {
+        return new ReadYamlStream(this.readInput());
+    }
+
     /**
      * Read the input's lines.
      * @return All read YamlLines
diff --git a/src/main/java/com/amihaiemil/eoyaml/RtYamlStreamBuilder.java b/src/main/java/com/amihaiemil/eoyaml/RtYamlStreamBuilder.java
index cd3f05c..43a7412 100644
--- a/src/main/java/com/amihaiemil/eoyaml/RtYamlStreamBuilder.java
+++ b/src/main/java/com/amihaiemil/eoyaml/RtYamlStreamBuilder.java
@@ -28,28 +28,8 @@
 package com.amihaiemil.eoyaml;
 
 import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Optional;
-import java.util.Spliterator;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.IntFunction;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.function.ToDoubleFunction;
-import java.util.function.ToIntFunction;
-import java.util.function.ToLongFunction;
-import java.util.stream.Collector;
-import java.util.stream.DoubleStream;
-import java.util.stream.IntStream;
-import java.util.stream.LongStream;
-import java.util.stream.Stream;
 
 /**
  * YamlStreamBuilder implementation.
@@ -92,14 +72,9 @@ final class RtYamlStreamBuilder implements YamlStreamBuilder {
     public YamlStream build() {
         return new BuiltYamlStream(this.documents);
     }
-    
+
     /**
      * Built YamlStream.
-     * @checkstyle FinalParameters (200 lines)
-     * @checkstyle JavadocMethod (200 lines)
-     * @checkstyle LineLength (200 lines)
-     * @checkstyle ParameterName (200 lines)
-     * @checkstyle FinalParameters (200 lines)
      * @author Mihai Andronache (amihaiemil@gmail.com)
      * @version $Id$
      * @since 3.1.1
@@ -110,187 +85,19 @@ final class RtYamlStreamBuilder implements YamlStreamBuilder {
          * Documents as a List.
          */
         private Collection<YamlNode> documents;
-        
+
         /**
-         * The Stream of YAML Documents.
+         * Constructor.
+         * @param documents Added YamlNodes.
          */
-        private Stream<YamlNode> documentStream;
-        
         BuiltYamlStream(final Collection<YamlNode> documents) {
             this.documents = documents;
-            this.documentStream = documents.stream();
-        }
-        
-        public Iterator<YamlNode> iterator() {
-            return this.documentStream.iterator();
-        }
-
-        public Spliterator<YamlNode> spliterator() {
-            return this.documentStream.spliterator();
-        }
-
-        public boolean isParallel() {
-            return this.documentStream.isParallel();
-        }
-
-        public Stream<YamlNode> sequential() {
-            return this.documentStream.sequential();
-        }
-
-        public Stream<YamlNode> parallel() {
-            return this.documentStream.parallel();
-        }
-
-        public Stream<YamlNode> unordered() {
-            return this.documentStream.unordered();
-        }
-
-        public Stream<YamlNode> onClose(Runnable closeHandler) {
-            return this.documentStream.onClose(closeHandler);
-        }
-
-        public void close() {
-            this.documentStream.close();
-        }
-
-        public Stream<YamlNode> filter(Predicate<? super YamlNode> predicate) {
-            return this.documentStream.filter(predicate);
-        }
-
-        public <R> Stream<R> map(Function<? super YamlNode, ? extends R> mapper) {
-            return this.documentStream.map(mapper);
-        }
-
-        public IntStream mapToInt(ToIntFunction<? super YamlNode> mapper) {
-            return this.documentStream.mapToInt(mapper);
-        }
-
-        public LongStream mapToLong(ToLongFunction<? super YamlNode> mapper) {
-            return this.documentStream.mapToLong(mapper);
-        }
-
-        public DoubleStream mapToDouble(ToDoubleFunction<? super YamlNode> mapper) {
-            return this.documentStream.mapToDouble(mapper);
-        }
-
-        public <R> Stream<R> flatMap(Function<? super YamlNode, ? extends Stream<? extends R>> mapper) {
-            return this.documentStream.flatMap(mapper);
-        }
-
-        public IntStream flatMapToInt(Function<? super YamlNode, ? extends IntStream> mapper) {
-            return this.documentStream.flatMapToInt(mapper);
-        }
-
-        public LongStream flatMapToLong(Function<? super YamlNode, ? extends LongStream> mapper) {
-            return this.documentStream.flatMapToLong(mapper);
-        }
-
-        public DoubleStream flatMapToDouble(Function<? super YamlNode, ? extends DoubleStream> mapper) {
-            return this.documentStream.flatMapToDouble(mapper);
-        }
-
-        public Stream<YamlNode> distinct() {
-            return this.documentStream.distinct();
-        }
-
-        public Stream<YamlNode> sorted() {
-            return this.documentStream.sorted();
-        }
-
-        public Stream<YamlNode> sorted(Comparator<? super YamlNode> comparator) {
-            return this.documentStream.sorted(comparator);
-        }
-
-        public Stream<YamlNode> peek(Consumer<? super YamlNode> action) {
-            return this.documentStream.peek(action);
-        }
-
-        public Stream<YamlNode> limit(long maxSize) {
-            return this.documentStream.limit(maxSize);
-        }
-
-        public Stream<YamlNode> skip(long n) {
-            return this.documentStream.skip(n);
-        }
-
-        public void forEach(Consumer<? super YamlNode> action) {
-            this.documentStream.forEach(action);
-        }
-
-        public void forEachOrdered(Consumer<? super YamlNode> action) {
-            this.documentStream.forEachOrdered(action);
-        }
-
-        public Object[] toArray() {
-            return this.documentStream.toArray();
-        }
-
-        public <A> A[] toArray(IntFunction<A[]> generator) {
-            return this.documentStream.toArray(generator);
-        }
-
-        public YamlNode reduce(YamlNode identity, BinaryOperator<YamlNode> accumulator) {
-            return this.documentStream.reduce(identity, accumulator);
-        }
-
-        public Optional<YamlNode> reduce(BinaryOperator<YamlNode> accumulator) {
-            return this.documentStream.reduce(accumulator);
-        }
-
-        public <U> U reduce(U identity, BiFunction<U, ? super YamlNode, U> accumulator, BinaryOperator<U> combiner) {
-            return this.documentStream.reduce(identity, accumulator, combiner);
-        }
-
-        public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super YamlNode> accumulator,
-                BiConsumer<R, R> combiner) {
-            return this.documentStream.collect(supplier, accumulator, combiner);
-        }
-
-        public <R, A> R collect(Collector<? super YamlNode, A, R> collector) {
-            return this.documentStream.collect(collector);
-        }
-
-        public Optional<YamlNode> min(Comparator<? super YamlNode> comparator) {
-            return this.documentStream.min(comparator);
-        }
-
-        public Optional<YamlNode> max(Comparator<? super YamlNode> comparator) {
-            return this.documentStream.max(comparator);
-        }
-
-        public long count() {
-            return this.documentStream.count();
-        }
-
-        public boolean anyMatch(Predicate<? super YamlNode> predicate) {
-            return this.documentStream.anyMatch(predicate);
-        }
-
-        public boolean allMatch(Predicate<? super YamlNode> predicate) {
-            return this.documentStream.allMatch(predicate);
-        }
-
-        public boolean noneMatch(Predicate<? super YamlNode> predicate) {
-            return this.documentStream.noneMatch(predicate);
-        }
-
-        public Optional<YamlNode> findFirst() {
-            return this.documentStream.findFirst();
-        }
-
-        public Optional<YamlNode> findAny() {
-            return this.documentStream.findAny();
         }
 
         @Override
         public Collection<YamlNode> values() {
             return this.documents;
         }
-
-        @Override
-        public String toString() {
-            return this.indent(0);
-        }
     }
-    
+
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/StartMarkers.java b/src/main/java/com/amihaiemil/eoyaml/StartMarkers.java
new file mode 100644
index 0000000..de96de7
--- /dev/null
+++ b/src/main/java/com/amihaiemil/eoyaml/StartMarkers.java
@@ -0,0 +1,124 @@
+/**
+ * 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.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * StartMarkers. Decorator over some YamlLines, that
+ * only iterates over the YAML Document start markers (---).<br><br>
+ * It is used by {@link ReadYamlStream} to separate YAML documents.
+ * @author Mihai Andronache(amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 3.1.4
+ */
+final class StartMarkers implements YamlLines {
+
+    /**
+     * Yaml lines.
+     */
+    private YamlLines yamlLines;
+
+    /**
+     * Constructor.
+     * @param yamlLines The YAML Lines.
+     */
+    StartMarkers(final YamlLines yamlLines){
+        this.yamlLines = yamlLines;
+    }
+
+    /**
+     * Returns an iterator over these Yaml lines.
+     * It ignores the YAML Directives and Marker lines.
+     * @todo #90:30min This method has to be enhanced to also cover
+     *  the case when the first "---" is missing. A YAML Stream of documents
+     *  can also omit the Start Marker for the first YAML Document.
+     * @return Iterator over these yaml lines.
+     */
+    @Override
+    public Iterator<YamlLine> iterator() {
+        Iterator<YamlLine> iterator = this.yamlLines.iterator();
+        if (iterator.hasNext()) {
+            final List<YamlLine> startMarkers = new ArrayList<>();
+            while (iterator.hasNext()) {
+                final YamlLine current = iterator.next();
+                final String currentLine = current.trimmed();
+                if ("---".equals(currentLine)) {
+                    startMarkers.add(current);
+                }
+            }
+            iterator = startMarkers.iterator();
+        }
+        return iterator;
+    }
+
+    /**
+     * Lines which are nested after the given YamlLine. The given YamlLine
+     * has to be a Start Marker (---). "Nested lines" means all the lines
+     * which are after it, until Start Marker, End Marker (...) or end-of-file
+     * is met.
+     * @param after Number of a YamlLine
+     * @return YamlLines
+     */
+    @Override
+    public AllYamlLines nested(final int after) {
+        final YamlLine startLine = this.yamlLines.line(after);
+        if(!"---".equals(startLine.trimmed())) {
+            throw new IllegalStateException(
+                "Expected start of Yaml document on line " + (after + 1)
+              + " (---) while reading the Yaml stream. Instead, the line is ["
+              + startLine.trimmed() + "]."
+            );
+        }
+        final List<YamlLine> yamlDocLines = new ArrayList<>();
+        for(final YamlLine line : this.lines()) {
+            if(line.number() > after) {
+                final String current = line.trimmed();
+                if("---".equals(current) || "...".equals(current)) {
+                    break;
+                } else {
+                    yamlDocLines.add(line);
+                }
+            }
+        }
+        return new AllYamlLines(yamlDocLines);
+    }
+
+    @Override
+    public Collection<YamlLine> lines() {
+        return this.yamlLines.lines();
+    }
+
+    @Override
+    public YamlNode toYamlNode(final YamlLine prev) {
+        return this.yamlLines.toYamlNode(prev);
+    }
+}
diff --git a/src/main/java/com/amihaiemil/eoyaml/YamlInput.java b/src/main/java/com/amihaiemil/eoyaml/YamlInput.java
index 9487b1a..f6eacf4 100644
--- a/src/main/java/com/amihaiemil/eoyaml/YamlInput.java
+++ b/src/main/java/com/amihaiemil/eoyaml/YamlInput.java
@@ -50,18 +50,12 @@ public interface YamlInput {
      * @throws IOException if the input cannot be read for some reason
      */
     YamlSequence readYamlSequence() throws IOException;
-    
+
     /**
      * Read the given input as a Yaml stream.
      * @return Read YamlStream.
      * @throws IOException if the input cannot be read for some reason
      */
-    default YamlStream readYamlStream() throws IOException {
-        throw new UnsupportedOperationException(
-            "Not yet implemented. You can follow the development here "
-          + "https://github.com/decorators-squad/eo-yaml/issues/90. " 
-          + "You may also contribute if you have the time!" 
-        );
-    }
+    YamlStream readYamlStream() throws IOException;
 
 }
diff --git a/src/main/java/com/amihaiemil/eoyaml/YamlStream.java b/src/main/java/com/amihaiemil/eoyaml/YamlStream.java
index d137dfb..e829827 100644
--- a/src/main/java/com/amihaiemil/eoyaml/YamlStream.java
+++ b/src/main/java/com/amihaiemil/eoyaml/YamlStream.java
@@ -27,11 +27,21 @@
  */
 package com.amihaiemil.eoyaml;
 
-import java.util.Collection;
-import java.util.stream.Stream;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
 
 /**
- * A YAML Stream of documents. Documents are separated by 3 dashes (---).
+ * A YAML Stream of documents. Documents are separated by 3 dashes (---).<br>
+ * This interface also offers integrations with Java 8's Stream API.<br>
+ * All the methods have a default implementations based on the YamlNode
+ * values Collection.
+ *
+ * @checkstyle FinalParameters (400 lines)
+ * @checkstyle JavadocMethod (400 lines)
+ * @checkstyle LineLength (400 lines)
+ * @checkstyle ParameterName (400 lines)
+ * @checkstyle FinalParameters (400 lines)
  * @author Mihai Andronache (amihaiemil@gmail.com)
  * @version $Id$
  * @since 3.1.1
@@ -103,4 +113,166 @@ public interface YamlStream extends YamlNode, Stream<YamlNode> {
         return printed;
     }
 
+    default Iterator<YamlNode> iterator() {
+        return this.values().stream().iterator();
+    }
+
+    default Spliterator<YamlNode> spliterator() {
+        return this.values().stream().spliterator();
+    }
+
+    default boolean isParallel() {
+        return this.values().stream().isParallel();
+    }
+
+    default Stream<YamlNode> sequential() {
+        return this.values().stream().sequential();
+    }
+
+    default Stream<YamlNode> parallel() {
+        return this.values().stream().parallel();
+    }
+
+    default Stream<YamlNode> unordered() {
+        return this.values().stream().unordered();
+    }
+
+    default Stream<YamlNode> onClose(Runnable closeHandler) {
+        return this.values().stream().onClose(closeHandler);
+    }
+
+    default void close() {
+        this.values().stream().close();
+    }
+
+    default Stream<YamlNode> filter(Predicate<? super YamlNode> predicate) {
+        return this.values().stream().filter(predicate);
+    }
+
+    default <R> Stream<R> map(Function<? super YamlNode, ? extends R> mapper) {
+        return this.values().stream().map(mapper);
+    }
+
+    default IntStream mapToInt(ToIntFunction<? super YamlNode> mapper) {
+        return this.values().stream().mapToInt(mapper);
+    }
+
+    default LongStream mapToLong(ToLongFunction<? super YamlNode> mapper) {
+        return this.values().stream().mapToLong(mapper);
+    }
+
+    default DoubleStream mapToDouble(ToDoubleFunction<? super YamlNode> mapper) {
+        return this.values().stream().mapToDouble(mapper);
+    }
+
+    default <R> Stream<R> flatMap(Function<? super YamlNode, ? extends Stream<? extends R>> mapper) {
+        return this.values().stream().flatMap(mapper);
+    }
+
+    default IntStream flatMapToInt(Function<? super YamlNode, ? extends IntStream> mapper) {
+        return this.values().stream().flatMapToInt(mapper);
+    }
+
+    default LongStream flatMapToLong(Function<? super YamlNode, ? extends LongStream> mapper) {
+        return this.values().stream().flatMapToLong(mapper);
+    }
+
+    default DoubleStream flatMapToDouble(Function<? super YamlNode, ? extends DoubleStream> mapper) {
+        return this.values().stream().flatMapToDouble(mapper);
+    }
+
+    default Stream<YamlNode> distinct() {
+        return this.values().stream().distinct();
+    }
+
+    default Stream<YamlNode> sorted() {
+        return this.values().stream().sorted();
+    }
+
+    default Stream<YamlNode> sorted(Comparator<? super YamlNode> comparator) {
+        return this.values().stream().sorted(comparator);
+    }
+
+    default Stream<YamlNode> peek(Consumer<? super YamlNode> action) {
+        return this.values().stream().peek(action);
+    }
+
+    default Stream<YamlNode> limit(long maxSize) {
+        return this.values().stream().limit(maxSize);
+    }
+
+    default Stream<YamlNode> skip(long n) {
+        return this.values().stream().skip(n);
+    }
+
+    default void forEach(Consumer<? super YamlNode> action) {
+        this.values().stream().forEach(action);
+    }
+
+    default void forEachOrdered(Consumer<? super YamlNode> action) {
+        this.values().stream().forEachOrdered(action);
+    }
+
+    default Object[] toArray() {
+        return this.values().stream().toArray();
+    }
+
+    default <A> A[] toArray(IntFunction<A[]> generator) {
+        return this.values().stream().toArray(generator);
+    }
+
+    default YamlNode reduce(YamlNode identity, BinaryOperator<YamlNode> accumulator) {
+        return this.values().stream().reduce(identity, accumulator);
+    }
+
+    default Optional<YamlNode> reduce(BinaryOperator<YamlNode> accumulator) {
+        return this.values().stream().reduce(accumulator);
+    }
+
+    default <U> U reduce(U identity, BiFunction<U, ? super YamlNode, U> accumulator, BinaryOperator<U> combiner) {
+        return this.values().stream().reduce(identity, accumulator, combiner);
+    }
+
+    default <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super YamlNode> accumulator,
+                         BiConsumer<R, R> combiner) {
+        return this.values().stream().collect(supplier, accumulator, combiner);
+    }
+
+    default <R, A> R collect(Collector<? super YamlNode, A, R> collector) {
+        return this.values().stream().collect(collector);
+    }
+
+    default Optional<YamlNode> min(Comparator<? super YamlNode> comparator) {
+        return this.values().stream().min(comparator);
+    }
+
+    default Optional<YamlNode> max(Comparator<? super YamlNode> comparator) {
+        return this.values().stream().max(comparator);
+    }
+
+    default long count() {
+        return this.values().stream().count();
+    }
+
+    default boolean anyMatch(Predicate<? super YamlNode> predicate) {
+        return this.values().stream().anyMatch(predicate);
+    }
+
+    default boolean allMatch(Predicate<? super YamlNode> predicate) {
+        return this.values().stream().allMatch(predicate);
+    }
+
+    default boolean noneMatch(Predicate<? super YamlNode> predicate) {
+        return this.values().stream().noneMatch(predicate);
+    }
+
+    default Optional<YamlNode> findFirst() {
+        return this.values().stream().findFirst();
+    }
+
+    default Optional<YamlNode> findAny() {
+        return this.values().stream().findAny();
+    }
+
+
 }


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/ReadYamlStreamTest.java b/src/test/java/com/amihaiemil/eoyaml/ReadYamlStreamTest.java
new file mode 100644
index 0000000..27f1a2f
--- /dev/null
+++ b/src/test/java/com/amihaiemil/eoyaml/ReadYamlStreamTest.java
@@ -0,0 +1,175 @@
+/**
+ * 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.Ignore;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link ReadYamlStream}.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 3.1.4
+ */
+public final class ReadYamlStreamTest {
+
+    /**
+     * If no lines are provided, then ReadYamlStream should be empty.
+     */
+    @Test
+    public void worksWithNoLines() {
+        final YamlStream stream = new ReadYamlStream(
+            new AllYamlLines(new ArrayList<>())
+        );
+        MatcherAssert.assertThat(stream.values(), Matchers.emptyIterable());
+    }
+
+    /**
+     * If an empty Yaml stream is provided,
+     * then ReadYamlStream should be empty.
+     * @todo #90:30min Fix this test case. The problem is that,
+     *  by the time .toYamlNode(...) is called, the YamlLines
+     *  iterable is actually empty and iterator.next() throws an
+     *  exception. A more general solution should be provided, we
+     *  should actually have an EmptyYamlNode implementation, or
+     *  something else, to represent the case of a YamlNode with no
+     *  lines.
+     */
+    @Test
+    @Ignore
+    public void worksWithEmptyStream() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("...", 1));
+        final YamlStream stream = new ReadYamlStream(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(stream.values(), Matchers.emptyIterable());
+    }
+
+    /**
+     * ReadYamlStream can represent a stream of YAML docs which
+     * are separated only by the Start Marker.
+     */
+    @Test
+    public void worksWithOnlyStartMarkers() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("---", 3));
+        lines.add(new RtYamlLine("  time: 16:00", 4));
+        lines.add(new RtYamlLine("  temperature: 20C", 5));
+        lines.add(new RtYamlLine("---", 6));
+        lines.add(new RtYamlLine("  time: 22:00", 7));
+        lines.add(new RtYamlLine("  temperature: 15C", 8));
+        final YamlStream stream = new ReadYamlStream(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(stream.values(), Matchers.iterableWithSize(3));
+    }
+
+    /**
+     * ReadYamlStream can represent a stream of YAML docs which
+     * are separated only by the Start Marker and have the same
+     * indentation level as it.
+     */
+    @Test
+    public void worksWithOnlyStartMarkersSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        lines.add(new RtYamlLine("---", 3));
+        lines.add(new RtYamlLine("time: 16:00", 4));
+        lines.add(new RtYamlLine("temperature: 20C", 5));
+        lines.add(new RtYamlLine("---", 6));
+        lines.add(new RtYamlLine("time: 22:00", 7));
+        lines.add(new RtYamlLine("temperature: 15C", 8));
+        final YamlStream stream = new ReadYamlStream(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(stream.values(), Matchers.iterableWithSize(3));
+    }
+
+    /**
+     * ReadYamlStream can represent a stream of YAML docs which
+     * are separated by the Start, as well as End Markers.
+     */
+    @Test
+    public void worksWithStartAndEndMarkers() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 3));
+        lines.add(new RtYamlLine("---", 4));
+        lines.add(new RtYamlLine("  time: 16:00", 5));
+        lines.add(new RtYamlLine("  temperature: 20C", 6));
+        lines.add(new RtYamlLine("...", 7));
+        lines.add(new RtYamlLine("---", 8));
+        lines.add(new RtYamlLine("  time: 22:00", 9));
+        lines.add(new RtYamlLine("  temperature: 15C", 10));
+        lines.add(new RtYamlLine("...", 11));
+        final YamlStream stream = new ReadYamlStream(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(stream.values(), Matchers.iterableWithSize(3));
+    }
+
+    /**
+     * ReadYamlStream can represent a stream of YAML docs which
+     * are separated by the Start, as well as End Markers, and have
+     * the same indentation level.
+     */
+    @Test
+    public void worksWithStartAndEndMarkersSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 3));
+        lines.add(new RtYamlLine("---", 4));
+        lines.add(new RtYamlLine("time: 16:00", 5));
+        lines.add(new RtYamlLine("temperature: 20C", 6));
+        lines.add(new RtYamlLine("...", 7));
+        lines.add(new RtYamlLine("---", 8));
+        lines.add(new RtYamlLine("time: 22:00", 9));
+        lines.add(new RtYamlLine("temperature: 15C", 10));
+        lines.add(new RtYamlLine("...", 11));
+        final YamlStream stream = new ReadYamlStream(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(stream.values(), Matchers.iterableWithSize(3));
+    }
+}
diff --git a/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java b/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
index cec133b..a1aea7f 100644
--- a/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
@@ -27,11 +27,10 @@
  */
 package com.amihaiemil.eoyaml;
 
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
+import java.io.*;
 import java.util.Set;
 
+import org.apache.commons.io.IOUtils;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.Test;
@@ -311,14 +310,78 @@ public final class RtYamlInputTest {
     }
 
     /**
-     * Reading of YamlStream is not yet implemented, an exception should
-     * be thrown.
+     * A Yaml stream of mappings can be read.
      * @throws Exception If something goes wrong.
      */
-    @Test (expected = UnsupportedOperationException.class)
-    public void readingYamlStreamNotImplemented() throws Exception {
-        final YamlInput input = Yaml.createYamlInput("");
-        input.readYamlStream();
+    @Test
+    public void readsStreamOfMappings() throws Exception {
+        final YamlInput input = Yaml.createYamlInput(
+            new FileInputStream(
+                new File("src/test/resources/streamOfMappings.yml")
+            )
+        );
+        final YamlStream read = input.readYamlStream();
+        System.out.print(read);
+        MatcherAssert.assertThat(
+            read.toString(),
+            Matchers.equalTo(this.readTestResource("streamOfMappings.yml"))
+        );
+    }
+
+    /**
+     * A Yaml stream of sequences can be read.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void readsStreamOfSequences() throws Exception {
+        final YamlInput input = Yaml.createYamlInput(
+            new FileInputStream(
+                new File("src/test/resources/streamOfSequences.yml")
+            )
+        );
+        final YamlStream read = input.readYamlStream();
+        System.out.print(read);
+        MatcherAssert.assertThat(
+            read.toString(),
+            Matchers.equalTo(this.readTestResource("streamOfSequences.yml"))
+        );
+    }
+
+    /**
+     * A stream of mixed YAML documents can be read.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void readsMixedStream() throws Exception {
+        final YamlInput input = Yaml.createYamlInput(
+            new FileInputStream(
+                new File("src/test/resources/streamMixed.yml")
+            )
+        );
+        final YamlStream read = input.readYamlStream();
+        System.out.print(read);
+        MatcherAssert.assertThat(
+            read.toString(),
+            Matchers.equalTo(this.readTestResource("streamMixed.yml"))
+        );
+    }
+
+    /**
+     * Read a test resource file's contents.
+     * @param fileName File to read.
+     * @return File's contents as String.
+     * @throws FileNotFoundException If somethig is wrong.
+     * @throws IOException If somethig is wrong.
+     */
+    private String readTestResource(final String fileName)
+        throws FileNotFoundException, IOException {
+        return new String(
+            IOUtils.toByteArray(
+                new FileInputStream(
+                    new File("src/test/resources/" + fileName)
+                )
+            )
+        );
     }
 
 }
diff --git a/src/test/java/com/amihaiemil/eoyaml/StartMarkersTest.java b/src/test/java/com/amihaiemil/eoyaml/StartMarkersTest.java
new file mode 100644
index 0000000..a1428f9
--- /dev/null
+++ b/src/test/java/com/amihaiemil/eoyaml/StartMarkersTest.java
@@ -0,0 +1,454 @@
+/**
+ * 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.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Unit tests for {@link StartMarkers}.
+ * @author Mihai Andronache(amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 3.1.4
+ */
+public final class StartMarkersTest {
+
+    /**
+     * StartMarkers can return the encapsulated lines.
+     */
+    @Test
+    @SuppressWarnings("unchecked")
+    public void returnsLines() {
+        final YamlLines lines = Mockito.mock(YamlLines.class);
+        final Collection<YamlLine> collection = Mockito.mock(Collection.class);
+        Mockito.when(lines.lines()).thenReturn(collection);
+        MatcherAssert.assertThat(
+            new StartMarkers(lines).lines(),
+            Matchers.is(collection)
+        );
+    }
+
+    /**
+     * StartMarkers can turn itself into YamlNode.
+     */
+    @Test
+    public void turnsIntoYamlNode() {
+        final YamlLines lines = Mockito.mock(YamlLines.class);
+        final YamlLine prev = Mockito.mock(YamlLine.class);
+        final YamlNode result = Mockito.mock(YamlNode.class);
+        Mockito.when(lines.toYamlNode(prev)).thenReturn(result);
+        MatcherAssert.assertThat(
+            new StartMarkers(lines).toYamlNode(prev),
+            Matchers.is(result)
+        );
+    }
+
+    /**
+     * StartMarkers works when there are actually no lines.
+     */
+    @Test
+    public void iteratesOverNoLines() {
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(new ArrayList<>())
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(0)
+        );
+    }
+
+    /**
+     * StartMarkers can iterate when there is only one document, with no
+     * End Marker.
+     */
+    @Test
+    public void iteratesOnlyOneDocument() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(1)
+        );
+        final Iterator<YamlLine> iterator = smLines.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+    }
+
+    /**
+     * StartMarkers can iterate when there is only one document, with an
+     * End Marker.
+     */
+    @Test
+    public void iteratesOnlyOneDocumentWithEndMarker() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 2));
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(1)
+        );
+        final Iterator<YamlLine> iterator = smLines.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+    }
+
+    /**
+     * The iterator returned by StartMarkers only covers the lines
+     * representing a start marker (---).
+     */
+    @Test
+    public void iteratesOnlyOverStartMarkers() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 3));
+        lines.add(new RtYamlLine("---", 4));
+        lines.add(new RtYamlLine("  time: 16:00", 5));
+        lines.add(new RtYamlLine("  temperature: 20C", 6));
+        lines.add(new RtYamlLine("...", 7));
+        lines.add(new RtYamlLine("---", 8));
+        lines.add(new RtYamlLine("  time: 22:00", 9));
+        lines.add(new RtYamlLine("  temperature: 15C", 10));
+        lines.add(new RtYamlLine("...", 11));
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(3)
+        );
+        final Iterator<YamlLine> iterator = smLines.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+    }
+
+    /**
+     * The iterator returned by StartMarkers only covers the lines
+     * representing a start marker (---). In this test case,
+     * there are no document End Markers (...);
+     */
+    @Test
+    public void iteratesOnlyOverStartMarkersNoElipses() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("---", 3));
+        lines.add(new RtYamlLine("  time: 16:00", 4));
+        lines.add(new RtYamlLine("  temperature: 20C", 5));
+        lines.add(new RtYamlLine("---", 6));
+        lines.add(new RtYamlLine("  time: 22:00", 7));
+        lines.add(new RtYamlLine("  temperature: 15C", 8));
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(3)
+        );
+        final Iterator<YamlLine> iterator = smLines.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+    }
+
+    /**
+     * The iterator returned by StartMarkers only covers the lines
+     * representing a start marker (---). Also, in this test case,
+     * the YAML documents have the same indentation level as the
+     * markers.
+     */
+    @Test
+    public void iteratesOnlyOverStartMarkersSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 3));
+        lines.add(new RtYamlLine("---", 4));
+        lines.add(new RtYamlLine("time: 16:00", 5));
+        lines.add(new RtYamlLine("temperature: 20C", 6));
+        lines.add(new RtYamlLine("...", 7));
+        lines.add(new RtYamlLine("---", 8));
+        lines.add(new RtYamlLine("time: 22:00", 9));
+        lines.add(new RtYamlLine("temperature: 15C", 10));
+        lines.add(new RtYamlLine("...", 11));
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(3)
+        );
+        final Iterator<YamlLine> iterator = smLines.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+    }
+
+    /**
+     * The iterator returned by StartMarkers only covers the lines
+     * representing a start marker (---). In this test case,
+     * there are no document End Markers (...). Also, the YAML Documents
+     * have the same indentation level as the markers.
+     */
+    @Test
+    public void iteratesOnlyOverStartMarkersNoElipsesSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        lines.add(new RtYamlLine("---", 3));
+        lines.add(new RtYamlLine("time: 16:00", 4));
+        lines.add(new RtYamlLine("temperature: 20C", 5));
+        lines.add(new RtYamlLine("---", 6));
+        lines.add(new RtYamlLine("time: 22:00", 7));
+        lines.add(new RtYamlLine("temperature: 15C", 8));
+        final YamlLines smLines = new StartMarkers(
+            new AllYamlLines(lines)
+        );
+        MatcherAssert.assertThat(
+            smLines,
+            Matchers.iterableWithSize(3)
+        );
+        final Iterator<YamlLine> iterator = smLines.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("---")
+        );
+    }
+
+    /**
+     * StartMarkers can get the lines between a Start and an End marker.
+     */
+    @Test
+    public void getsNestedLines() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 2));
+        final AllYamlLines mapping = new StartMarkers(
+            new AllYamlLines(lines)
+        ).nested(0);
+        MatcherAssert.assertThat(mapping, Matchers.iterableWithSize(2));
+        final Iterator<YamlLine> iterator = mapping.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("time: 12:00")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("temperature: 25C")
+        );
+    }
+
+    /**
+     * StartMarkers can get the lines between a Start and an End marker.
+     * The nested lines have the same indentation as the markers.
+     */
+    @Test
+    public void getsNestedLinesSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        lines.add(new RtYamlLine("...", 2));
+        final AllYamlLines mapping = new StartMarkers(
+            new AllYamlLines(lines)
+        ).nested(0);
+        MatcherAssert.assertThat(mapping, Matchers.iterableWithSize(2));
+        final Iterator<YamlLine> iterator = mapping.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("time: 12:00")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("temperature: 25C")
+        );
+    }
+
+    /**
+     * StartMarkers can get the lines between a Start Marker and end-of-file.
+     */
+    @Test
+    public void getsNestedLinesEof() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        final AllYamlLines mapping = new StartMarkers(
+            new AllYamlLines(lines)
+        ).nested(0);
+        MatcherAssert.assertThat(mapping, Matchers.iterableWithSize(2));
+        final Iterator<YamlLine> iterator = mapping.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("time: 12:00")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("temperature: 25C")
+        );
+    }
+
+    /**
+     * StartMarkers can get the lines between a Start marker and end-of-file.
+     * The nested lines have the same indentation as the markers.
+     */
+    @Test
+    public void getsNestedLinesEofSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        final AllYamlLines mapping = new StartMarkers(
+            new AllYamlLines(lines)
+        ).nested(0);
+        MatcherAssert.assertThat(mapping, Matchers.iterableWithSize(2));
+        final Iterator<YamlLine> iterator = mapping.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("time: 12:00")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("temperature: 25C")
+        );
+    }
+
+    /**
+     * StartMarkers can get the lines between two Start markers.
+     */
+    @Test
+    public void getsNestedBeforeNewStart() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("  time: 12:00", 1));
+        lines.add(new RtYamlLine("  temperature: 25C", 2));
+        lines.add(new RtYamlLine("---", 3));
+        final AllYamlLines mapping = new StartMarkers(
+            new AllYamlLines(lines)
+        ).nested(0);
+        MatcherAssert.assertThat(mapping, Matchers.iterableWithSize(2));
+        final Iterator<YamlLine> iterator = mapping.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("time: 12:00")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("temperature: 25C")
+        );
+    }
+
+    /**
+     * StartMarkers can get the lines between two Start markers.
+     * The nested lines have the same indentation as the markers.
+     */
+    @Test
+    public void getsNestedLinesBeforeNewStartSameIndentation() {
+        final List<YamlLine> lines = new ArrayList<>();
+        lines.add(new RtYamlLine("---", 0));
+        lines.add(new RtYamlLine("time: 12:00", 1));
+        lines.add(new RtYamlLine("temperature: 25C", 2));
+        lines.add(new RtYamlLine("---", 3));
+        final AllYamlLines mapping = new StartMarkers(
+            new AllYamlLines(lines)
+        ).nested(0);
+        MatcherAssert.assertThat(mapping, Matchers.iterableWithSize(2));
+        final Iterator<YamlLine> iterator = mapping.iterator();
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("time: 12:00")
+        );
+        MatcherAssert.assertThat(
+            iterator.next().trimmed(),
+            Matchers.equalTo("temperature: 25C")
+        );
+    }
+}