-
Notifications
You must be signed in to change notification settings - Fork 4
Add Suppliers2 and Iterators2 to io.spine.util
#953
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
1a1df9c
Add `Suppliers2` and `Iterators2` to `io.spine.util`
alexander-yevsyukov 87e12c7
Bump version -> `2.0.0-SNAPSHOT.425`
alexander-yevsyukov cfafe8c
Update dependency reports
alexander-yevsyukov e677b1d
Update `config`
alexander-yevsyukov 07b0564
Add task document
alexander-yevsyukov 7a92406
Address `ErrorProne` and PMD warnings
alexander-yevsyukov 5effa41
Address `ErrorProne` warnings
alexander-yevsyukov 93753a3
Update build time
alexander-yevsyukov 3f9096c
Align the `Iterators2.filter()` signature with the package style
alexander-yevsyukov a517ec2
Remove the unused `Boolean.TRUE` static import
alexander-yevsyukov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| --- | ||
| slug: add-suppliers2-iterators2 | ||
| branch: add-suppliers2-and-iterators2 | ||
| owner: claude | ||
| status: in-review | ||
| started: 2026-07-16 | ||
| --- | ||
|
|
||
| ## Goal | ||
|
|
||
| `Suppliers2` and `Iterators2`, introduced in [core-jvm PR #1656][pr] under | ||
| `io.spine.server`, live in this repo's `io.spine.util` package (module `base`) | ||
| with test coverage, so that all Spine SDK repos can share them and `core-jvm` | ||
| can later drop its copies. | ||
|
|
||
| ## Context | ||
|
|
||
| - Both classes are nullness-friendly wrappers over Guava's `Suppliers.memoize` | ||
| and `Iterators.filter`: Guava declares `<T extends @Nullable Object>`, which | ||
| reads as a nullness mismatch when the result is assigned to a non-null type | ||
| in `@NullMarked` code. Declaring plain `<T>` in a `@NullMarked` package binds | ||
| `T` to a non-null type. | ||
| - `io.spine.util` already hosts the Guava-companion naming pattern | ||
| (`Preconditions2`, `Predicates2`) and is `@NullMarked` via `package-info.java`. | ||
| - The classes keep `@Internal` (as in the source PR): they are framework | ||
| plumbing; widening to public API later is non-breaking, the reverse is not. | ||
| - New tests follow `kotlin-jvm-tester` conventions: Kotlin, `Spec` suffix, | ||
| `internal`, `UtilityClassTest` base (private ctor + final + | ||
| `NullPointerTester` on public statics), Kotest assertions. | ||
|
|
||
| [pr]: https://github.com/SpineEventEngine/core-jvm/pull/1656 | ||
|
|
||
| ## Plan | ||
|
|
||
| - [x] Add `base/src/main/java/io/spine/util/Suppliers2.java` — verbatim from | ||
| the PR, only the `package` line changes. | ||
| - [x] Add `base/src/main/java/io/spine/util/Iterators2.java` — same. | ||
| - [x] Add `base/src/test/kotlin/io/spine/util/Suppliers2Spec.kt` — delegate | ||
| value returned; laziness; delegate called once. | ||
| - [x] Add `base/src/test/kotlin/io/spine/util/Iterators2Spec.kt` — retains | ||
| matching elements in order; exhausted when none match; `remove()` | ||
| unsupported. | ||
| - [x] Verify: `./gradlew build dokkaGenerate` (JDK 17 via `JAVA_HOME`). | ||
| - [ ] PR: branch off `master`, bump `version.gradle.kts` | ||
| (`2.0.0-SNAPSHOT.424` → `.425`), run `pre-pr`, open the PR. | ||
|
|
||
| ## Log | ||
|
|
||
| - 2026-07-16 16:45 — drafted and validated (build commands, `UtilityClassTest` | ||
| contract, detekt impact); executing. | ||
| - 2026-07-16 16:55 — all files in place; scoped specs green (11 tests, | ||
| 0 failures); full `./gradlew build dokkaGenerate` BUILD SUCCESSFUL in 30s. | ||
| `spine-code-review` + `kotlin-engineer` review passes launched. | ||
| - 2026-07-16 17:14 — version bumped to `.425`, reports regenerated, | ||
| pre-PR gate PASS (3 reviewers approve); PR #953 opened. | ||
| - 2026-07-16 17:35 — `review-docs` nits addressed: `@return` tags on both | ||
| methods, labeled `Suppliers.memoize()` link, timed `Log` entries. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /* | ||
| * Copyright 2026, TeamDev. All rights reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Redistribution and use in source and/or binary forms, with or without | ||
| * modification, must retain the above copyright notice and the following | ||
| * disclaimer. | ||
| * | ||
| * 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 | ||
| * OWNER 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 io.spine.util; | ||
|
|
||
| import com.google.common.collect.Iterators; | ||
| import io.spine.annotation.Internal; | ||
|
|
||
| import java.util.Iterator; | ||
| import java.util.function.Predicate; | ||
|
|
||
| /** | ||
| * Utilities for working with {@link Iterator}s, complementing | ||
| * {@link Iterators com.google.common.collect.Iterators}. | ||
| */ | ||
| @Internal | ||
| public final class Iterators2 { | ||
|
|
||
| /** Prevents instantiation of this utility class. */ | ||
| private Iterators2() { | ||
| } | ||
|
|
||
| /** | ||
| * Returns a view of the passed iterator containing the elements matching the predicate. | ||
| * | ||
| * <p>The returned iterator does not support removal. | ||
| * | ||
| * <p>Prefer this method over | ||
| * {@link Iterators#filter(Iterator, com.google.common.base.Predicate) Iterators.filter()} | ||
| * when the elements are not {@code null} — which, in this | ||
| * {@link org.jspecify.annotations.NullMarked @NullMarked} code, is the usual case. | ||
| * Guava declares its method as {@code <T extends @Nullable Object>}, deliberately, so that | ||
| * an iterator over nullable elements can be filtered too. The cost is that the nullness of | ||
| * {@code T} is no longer inferred from the assignment, and assigning the result to an | ||
| * {@code Iterator<@NonNull T>} reads as a nullness mismatch. Declaring {@code <T>} here, | ||
| * in a {@code @NullMarked} package, binds {@code T} to a non-null type and settles it. | ||
| * | ||
| * <p>Takes a {@link Predicate java.util.function.Predicate}, rather than the Guava one | ||
| * the delegate expects, so that the callers do not adapt it themselves. | ||
| * | ||
| * @param unfiltered | ||
| * the iterator to filter | ||
| * @param retainIfTrue | ||
| * the predicate matching the elements to retain | ||
| * @param <T> | ||
| * the type of the iterated elements | ||
| * @return a filtered view of the given iterator | ||
| */ | ||
| @SuppressWarnings("NullableProblems") | ||
| public static <T> Iterator<T> filter(Iterator<T> unfiltered, | ||
| Predicate<? super T> retainIfTrue) { | ||
| return Iterators.filter(unfiltered, retainIfTrue::test); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /* | ||
| * Copyright 2026, TeamDev. All rights reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Redistribution and use in source and/or binary forms, with or without | ||
| * modification, must retain the above copyright notice and the following | ||
| * disclaimer. | ||
| * | ||
| * 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 | ||
| * OWNER 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 io.spine.util; | ||
|
|
||
| import com.google.common.base.Suppliers; | ||
| import io.spine.annotation.Internal; | ||
|
|
||
| import java.util.function.Supplier; | ||
|
|
||
| /** | ||
| * Utilities for working with {@link Supplier}s, complementing | ||
| * {@link Suppliers com.google.common.base.Suppliers}. | ||
| */ | ||
| @Internal | ||
| public final class Suppliers2 { | ||
|
|
||
| /** Prevents instantiation of this utility class. */ | ||
| private Suppliers2() { | ||
| } | ||
|
|
||
| /** | ||
| * Returns a supplier that obtains the value from the given delegate on the first call, | ||
| * and returns that same value on the calls that follow. | ||
| * | ||
| * <p>Prefer this method over | ||
| * {@link Suppliers#memoize(com.google.common.base.Supplier) Suppliers.memoize()} | ||
| * when the supplied value is not {@code null} — which, in this | ||
| * {@link org.jspecify.annotations.NullMarked @NullMarked} code, is the usual case. | ||
| * Guava declares its method as {@code <T extends @Nullable Object>}, deliberately, so that | ||
| * a {@code null} value can be memoized too. The cost is that the nullness of {@code T} is | ||
| * no longer inferred from the assignment, and assigning the result to a | ||
| * {@code Supplier<@NonNull T>} reads as a nullness mismatch. Declaring {@code <T>} here, | ||
| * in a {@code @NullMarked} package, binds {@code T} to a non-null type and settles it. | ||
| * | ||
| * @param delegate | ||
| * the supplier of the value to memoize | ||
| * @param <T> | ||
| * the type of the supplied value | ||
| * @return a supplier that memoizes the value of the delegate | ||
| */ | ||
| @SuppressWarnings("NullableProblems") | ||
| public static <T> Supplier<T> memoize(Supplier<T> delegate) { | ||
| return Suppliers.memoize(delegate::get); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| /* | ||
| * Copyright 2026, TeamDev. All rights reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Redistribution and use in source and/or binary forms, with or without | ||
| * modification, must retain the above copyright notice and the following | ||
| * disclaimer. | ||
| * | ||
| * 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 | ||
| * OWNER 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 io.spine.util | ||
|
|
||
| import io.kotest.assertions.throwables.shouldThrow | ||
| import io.kotest.matchers.collections.shouldContainExactly | ||
| import io.kotest.matchers.shouldBe | ||
| import io.spine.testing.UtilityClassTest | ||
| import org.junit.jupiter.api.DisplayName | ||
| import org.junit.jupiter.api.Test | ||
|
|
||
| @DisplayName("`Iterators2` utility class should") | ||
| internal class Iterators2Spec : UtilityClassTest<Iterators2>(Iterators2::class.java) { | ||
|
|
||
| @Test | ||
| fun `retain only the elements matching the predicate`() { | ||
| val source = listOf(1, 2, 3, 4, 5, 6).iterator() | ||
|
|
||
| val filtered = Iterators2.filter(source) { it % 2 == 0 } | ||
|
|
||
| filtered.asSequence().toList() shouldContainExactly listOf(2, 4, 6) | ||
| } | ||
|
|
||
| @Test | ||
| fun `return an exhausted iterator when no elements match`() { | ||
| val source = listOf(1, 3, 5).iterator() | ||
|
|
||
| val filtered = Iterators2.filter(source) { it % 2 == 0 } | ||
|
|
||
| filtered.hasNext() shouldBe false | ||
| } | ||
|
|
||
| @Test | ||
| fun `not support removal`() { | ||
| val source = mutableListOf(1, 2, 3) | ||
| val filtered: MutableIterator<Int> = Iterators2.filter(source.iterator()) { true } | ||
| filtered.next() | ||
|
|
||
| shouldThrow<UnsupportedOperationException> { | ||
| filtered.remove() | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.