diff --git a/src/main/java/org/springframework/hateoas/Links.java b/src/main/java/org/springframework/hateoas/Links.java index 63caeaec..9f2bf82a 100644 --- a/src/main/java/org/springframework/hateoas/Links.java +++ b/src/main/java/org/springframework/hateoas/Links.java @@ -76,7 +76,7 @@ public class Links implements Iterable { * @param links */ public static Links of(Iterable links) { - return new Links(links); + return Links.class.isInstance(links) ? Links.class.cast(links) : new Links(links); } /** @@ -134,6 +134,26 @@ public class Links implements Iterable { return and(Arrays.asList(links)); } + /** + * Adds the given links if the given condition is {@literal true}. The given {@link Link}s 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 + */ + @SafeVarargs + public final Links andIf(boolean condition, Link... links) { + return condition ? and(links) : this; + } + /** * 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 {@literal true}. Essentially syntactic sugar to write:
diff --git a/src/test/java/org/springframework/hateoas/LinksUnitTest.java b/src/test/java/org/springframework/hateoas/LinksUnitTest.java index 43f4f7ec..b45b424a 100755 --- a/src/test/java/org/springframework/hateoas/LinksUnitTest.java +++ b/src/test/java/org/springframework/hateoas/LinksUnitTest.java @@ -143,12 +143,31 @@ class LinksUnitTest { assertThat(Links.of(first, second).containsSameLinksAs(Links.of(first, second))).isTrue(); } - @Test // #1322 - void conditionallyAddsLink() { + @TestFactory // #1322, #1341 + Stream conditionallyAddsLink() { - Links links = Links.NONE.andIf(true, () -> Link.of("/foo")); + Links links = Links.NONE; + Link link = Link.of("/foo"); - assertThat(links.getRequiredLink(IanaLinkRelations.SELF).getHref()).isEqualTo("/foo"); + List adders = Arrays.asList(// + NamedLinks.of("adds via varargs", links.andIf(true, link)), // + NamedLinks.of("adds via Supplier", links.andIf(true, () -> link)), // + NamedLinks.of("adds via Stream", links.andIf(true, Stream.of(link)))); + + Stream adderTests = DynamicTest.stream(adders.iterator(), NamedLinks::getName, it -> { + assertThat(it.links.getRequiredLink(IanaLinkRelations.SELF).getHref()).isEqualTo("/foo"); + }); + + List nonAdders = Arrays.asList(// + NamedLinks.of("does not add via varargs", links.andIf(false, link)), // + NamedLinks.of("does not add via Supplier", links.andIf(false, () -> link)), // + NamedLinks.of("does not add via Stream", links.andIf(false, Stream.of(link)))); + + Stream nonAdderTests = DynamicTest.stream(nonAdders.iterator(), NamedLinks::getName, it -> { + assertThat(it.links).isEmpty(); + }); + + return Stream.concat(adderTests, nonAdderTests); } @Test // #1322 @@ -192,6 +211,52 @@ class LinksUnitTest { .element(0).extracting(Link::getHref).isEqualTo("/foo")); } + @TestFactory // #1340 + Stream replacesLinksViaMerge() { + + Links links = Links.of(Link.of("/foo")); + Link sameRel = Link.of("/bar"); + + List sources = Arrays.asList(// + NamedLinks.of("replace same rel via varargs", links.merge(MergeMode.REPLACE_BY_REL, sameRel)), + NamedLinks.of("replace same rel via Stream", links.merge(MergeMode.REPLACE_BY_REL, Stream.of(sameRel)))); + + return DynamicTest.stream(sources.iterator(), NamedLinks::getName, + it -> assertThat(it.links).hasSize(1) // + .element(0).extracting(Link::getHref).isEqualTo("/bar")); + } + + @Test + void removesLinkByRel() { + assertThat(Links.of(Link.of("/foo")).without(IanaLinkRelations.SELF)).isEmpty(); + } + + @Test + void basics() { + + Links none = Links.NONE; + + assertThat(none.isEmpty()).isTrue(); + assertThat(none.stream()).isEmpty(); + assertThat(none.hasSingleLink()).isFalse(); + assertThat(none.hasSize(0)).isTrue(); + assertThat(none.hasLink("self")).isFalse(); + + Links one = none.and(Link.of("/foo")); + + assertThat(one.isEmpty()).isFalse(); + assertThat(one.stream()).isNotEmpty(); + assertThat(one.hasSingleLink()).isTrue(); + assertThat(one.hasSize(1)).isTrue(); + assertThat(one.hasLink("self")).isTrue(); + + Links anotherOne = none.and(Link.of("/foo")); + + assertThat(anotherOne).isEqualTo(one); + assertThat(one).isEqualTo(anotherOne); + assertThat(one.hashCode()).isEqualTo(anotherOne.hashCode()); + } + @Value(staticConstructor = "of") static class NamedLinks { String name;