diff --git a/src/main/java/org/springframework/hateoas/Links.java b/src/main/java/org/springframework/hateoas/Links.java
index 8930d617..63caeaec 100644
--- a/src/main/java/org/springframework/hateoas/Links.java
+++ b/src/main/java/org/springframework/hateoas/Links.java
@@ -136,7 +136,7 @@ public class Links implements Iterable {
/**
* Adds the given links if the given condition is {@literal true}. The given {@link Supplier}s will only be resolved
- * if the given condition is true. Essentially syntactic sugar to write:
+ * if the given condition is {@literal true}. Essentially syntactic sugar to write:
*
* if (a > 3) {
* links = links.and(…);
@@ -154,9 +154,29 @@ public class Links implements Iterable {
Assert.notNull(links, "Links must not be null!");
- return condition //
- ? and(Arrays.stream(links).map(Supplier::get).collect(Collectors.toList())) //
- : this;
+ return andIf(condition, Stream.of(links).map(Supplier::get));
+ }
+
+ /**
+ * Adds the given links if the given condition is {@literal true}. The given {@link Stream} will only be resolved if
+ * the given condition is {@literal true}. Essentially syntactic sugar to write:
+ *
+ * if (a > 3) {
+ * links = links.and(…);
+ * }
+ * as
+ * links = link.andIf(a > 3, …);
+ *
+ *
+ * @param condition
+ * @param links must not be {@literal null}.
+ * @return
+ */
+ public final Links andIf(boolean condition, Stream links) {
+
+ Assert.notNull(links, "Links must not be null!");
+
+ return condition ? and(links.collect(Collectors.toList())) : this;
}
/**
@@ -176,6 +196,19 @@ public class Links implements Iterable {
return Links.of(newLinks);
}
+ /**
+ * Creates a new {@link Links} instance with all given {@link Link}s added. For conditional adding see
+ * {@link #merge(Iterable)}.
+ *
+ * @param links must not be {@literal null}.
+ * @return
+ * @see #merge(Iterable)
+ * @see #merge(MergeMode, Iterable)
+ */
+ public Links and(Stream links) {
+ return and(links.collect(Collectors.toList()));
+ }
+
/**
* Merges the current {@link Links} with the given ones, skipping {@link Link}s already contained in the current
* instance. For unconditional combination see {@link #and(Link...)}.
@@ -191,12 +224,25 @@ public class Links implements Iterable {
/**
* Merges the current {@link Links} with the given ones, skipping {@link Link}s already contained in the current
- * instance. For unconditional combination see {@link #and(Link...)}.
+ * instance. For unconditional combination see {@link #and(Stream)}.
*
* @param links the {@link Link}s to be merged, must not be {@literal null}.
* @return
* @see MergeMode#SKIP_BY_EQUALITY
- * @see #and(Link...)
+ * @see #and(Stream)
+ */
+ public Links merge(Stream links) {
+ return merge(links.collect(Collectors.toList()));
+ }
+
+ /**
+ * Merges the current {@link Links} with the given ones, skipping {@link Link}s already contained in the current
+ * instance. For unconditional combination see {@link #and(Iterable)}.
+ *
+ * @param links the {@link Link}s to be merged, must not be {@literal null}.
+ * @return
+ * @see MergeMode#SKIP_BY_EQUALITY
+ * @see #and(Iterable)
*/
public Links merge(Iterable links) {
return merge(MergeMode.SKIP_BY_EQUALITY, links);
@@ -213,6 +259,17 @@ public class Links implements Iterable {
return merge(mode, Arrays.asList(links));
}
+ /**
+ * Merges the current {@link Links} with the given ones applying the given {@link MergeMode}.
+ *
+ * @param mode must not be {@literal null}.
+ * @param links must not be {@literal null}.
+ * @return
+ */
+ public Links merge(MergeMode mode, Stream links) {
+ return merge(mode, links.collect(Collectors.toList()));
+ }
+
/**
* Merges the current {@link Links} with the given ones applying the given {@link MergeMode}.
*
diff --git a/src/test/java/org/springframework/hateoas/LinksUnitTest.java b/src/test/java/org/springframework/hateoas/LinksUnitTest.java
index 313965e9..43f4f7ec 100755
--- a/src/test/java/org/springframework/hateoas/LinksUnitTest.java
+++ b/src/test/java/org/springframework/hateoas/LinksUnitTest.java
@@ -17,10 +17,17 @@ package org.springframework.hateoas;
import static org.assertj.core.api.Assertions.*;
-import java.util.Arrays;
-import java.util.Optional;
+import lombok.Value;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestFactory;
+import org.springframework.hateoas.Links.MergeMode;
import org.springframework.util.StringUtils;
/**
@@ -151,4 +158,43 @@ class LinksUnitTest {
throw new IllegalStateException();
})).doesNotThrowAnyException();
}
+
+ @TestFactory // #1340
+ Stream addsStreamOfLinks() {
+
+ Links links = Links.NONE;
+ Link link = Link.of("/foo");
+
+ List sources = Arrays.asList(//
+ NamedLinks.of("via varargs", links.and(link)), //
+ NamedLinks.of("via Iterable", links.and(Arrays.asList(link))), //
+ NamedLinks.of("via Stream", links.and(Stream.of(link))));
+
+ return DynamicTest.stream(sources.iterator(), NamedLinks::getName,
+ it -> assertThat(it.links.getRequiredLink(IanaLinkRelations.SELF)).isNotNull());
+ }
+
+ @TestFactory // #1340
+ Stream mergesStreamOfLinks() {
+
+ Links links = Links.NONE.and(Link.of("/foo"));
+ Link same = Link.of("/foo");
+ Link sameRel = Link.of("/bar");
+
+ List sources = Arrays.asList(//
+ NamedLinks.of("merge same via varargs", links.merge(same)),
+ NamedLinks.of("merge same rel via varargs", links.merge(MergeMode.SKIP_BY_REL, sameRel)),
+ NamedLinks.of("merge same via Stream", links.merge(Stream.of(same))),
+ NamedLinks.of("merge same rel via Stream", links.merge(MergeMode.SKIP_BY_REL, Stream.of(sameRel))));
+
+ return DynamicTest.stream(sources.iterator(), NamedLinks::getName,
+ it -> assertThat(it.links).hasSize(1) //
+ .element(0).extracting(Link::getHref).isEqualTo("/foo"));
+ }
+
+ @Value(staticConstructor = "of")
+ static class NamedLinks {
+ String name;
+ Links links;
+ }
}