diff --git a/src/main/java/org/cactoos/iterable/IterableOf.java b/src/main/java/org/cactoos/iterable/IterableOf.java index 886ad3d0c0..0a24e53502 100644 --- a/src/main/java/org/cactoos/iterable/IterableOf.java +++ b/src/main/java/org/cactoos/iterable/IterableOf.java @@ -25,8 +25,12 @@ import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; +import org.cactoos.Func; import org.cactoos.Scalar; +import org.cactoos.func.UncheckedFunc; import org.cactoos.iterator.IteratorOf; +import org.cactoos.scalar.StickyScalar; import org.cactoos.scalar.UncheckedScalar; /** @@ -65,6 +69,57 @@ public IterableOf(final Iterator list) { this(() -> list); } + /** + * Paged iterable. + *

+ * Elements will continue to be provided so long as {@code next} produces + * non-empty iterators. + * @param Custom iterator + * @param first First bag of elements + * @param next Subsequent bags of elements + */ + @SuppressWarnings( + { + "PMD.CallSuperInConstructor", + "PMD.ConstructorOnlyInitializesOrCallOtherConstructors" + } + ) + public > IterableOf( + final Scalar first, final Func next + ) { + // @checkstyle AnonInnerLengthCheck (30 lines) + this( + () -> new Iterator() { + private UncheckedScalar current = new UncheckedScalar<>( + new StickyScalar<>(first) + ); + private final UncheckedFunc subsequent = + new UncheckedFunc<>(next); + + @Override + public boolean hasNext() { + if (!this.current.value().hasNext()) { + final I next = this.subsequent.apply( + this.current.value() + ); + this.current = new UncheckedScalar<>( + new StickyScalar<>(() -> next) + ); + } + return this.current.value().hasNext(); + } + + @Override + public X next() { + if (this.hasNext()) { + return this.current.value().next(); + } + throw new NoSuchElementException(); + } + } + ); + } + /** * Ctor. * @param sclr The encapsulated iterator of x diff --git a/src/test/java/org/cactoos/iterable/IterableOfTest.java b/src/test/java/org/cactoos/iterable/IterableOfTest.java index 7e1a208c1c..105608e6ba 100644 --- a/src/test/java/org/cactoos/iterable/IterableOfTest.java +++ b/src/test/java/org/cactoos/iterable/IterableOfTest.java @@ -23,16 +23,24 @@ */ package org.cactoos.iterable; +import java.util.Iterator; +import org.cactoos.iterator.IteratorOf; +import org.cactoos.list.ListOf; import org.cactoos.scalar.LengthOf; +import org.cactoos.scalar.Ternary; import org.cactoos.text.TextOf; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; +import org.hamcrest.collection.IsIterableContainingInOrder; +import org.hamcrest.collection.IsIterableWithSize; +import org.hamcrest.core.IsEqual; import org.junit.Test; /** * Test case for {@link IterableOf}. * @since 0.12 * @checkstyle JavadocMethodCheck (500 lines) + * @checkstyle ClassDataAbstractionCoupling (2 lines) */ public final class IterableOfTest { @@ -73,5 +81,69 @@ public void convertsObjectsToIterable() { ); } -} + // @todo #971:30min Implement equals() and hashCode() on IterableEnvelope. + // Then, refactor this test so that only + // `new IsEqual<>(new Joined<>(first, second, third))` is required as a + // matcher. + @Test + @SuppressWarnings({"unchecked", "PMD.AvoidDuplicateLiterals"}) + public void containAllPagedContentInOrder() throws Exception { + final Iterable first = new IterableOf<>("one", "two"); + final Iterable second = new IterableOf<>("three", "four"); + final Iterable third = new IterableOf<>("five"); + final Iterable> service = new IterableOf<>( + first, second, third + ); + final Iterator> pages = service.iterator(); + MatcherAssert.assertThat( + "Must have all page values", + new IterableOf<>( + () -> pages.next().iterator(), + page -> new Ternary<>( + () -> pages.hasNext(), + () -> pages.next().iterator(), + () -> new IteratorOf() + ).value() + ), + new IsIterableContainingInOrder<>( + new ListOf<>( + new IsEqual<>("one"), + new IsEqual<>("two"), + new IsEqual<>("three"), + new IsEqual<>("four"), + new IsEqual<>("five") + ) + ) + ); + } + @Test + @SuppressWarnings("unchecked") + public void reportTotalPagedLength() throws Exception { + final Iterable first = new IterableOf<>("A", "five"); + final Iterable second = new IterableOf<>("word", "long"); + final Iterable third = new IterableOf<>("sentence"); + final Iterable> service = new IterableOf<>( + first, second, third + ); + final Iterator> pages = service.iterator(); + MatcherAssert.assertThat( + "Length must be equal to total number of elements", + new IterableOf<>( + () -> pages.next().iterator(), + page -> new Ternary<>( + () -> pages.hasNext(), + () -> pages.next().iterator(), + () -> new IteratorOf() + ).value() + ), + new IsIterableWithSize<>( + new IsEqual<>( + new LengthOf( + new Joined<>(first, second, third) + ).intValue() + ) + ) + ); + } +}