diff --git a/src/main/java/org/springframework/hateoas/Identifiable.java b/src/main/java/org/springframework/hateoas/Identifiable.java index e8f8488d..88688ff5 100644 --- a/src/main/java/org/springframework/hateoas/Identifiable.java +++ b/src/main/java/org/springframework/hateoas/Identifiable.java @@ -16,6 +16,7 @@ package org.springframework.hateoas; import java.io.Serializable; +import java.util.Optional; /** * Interface to mark objects that are identifiable by an ID of any type. @@ -27,7 +28,7 @@ public interface Identifiable { /** * Returns the id identifying the object. * - * @return the identifier or {@literal null} if not available. + * @return the identifier or {@link Optional#empty()} if not available. */ - ID getId(); + Optional getId(); } diff --git a/src/main/java/org/springframework/hateoas/PagedResources.java b/src/main/java/org/springframework/hateoas/PagedResources.java index 3dc59c29..d0fede10 100644 --- a/src/main/java/org/springframework/hateoas/PagedResources.java +++ b/src/main/java/org/springframework/hateoas/PagedResources.java @@ -18,6 +18,7 @@ package org.springframework.hateoas; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Optional; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @@ -105,7 +106,7 @@ public class PagedResources extends Resources { * @return */ @JsonIgnore - public Link getNextLink() { + public Optional getNextLink() { return getLink(Link.REL_NEXT); } @@ -115,7 +116,7 @@ public class PagedResources extends Resources { * @return */ @JsonIgnore - public Link getPreviousLink() { + public Optional getPreviousLink() { return getLink(Link.REL_PREVIOUS); } @@ -251,8 +252,8 @@ public class PagedResources extends Resources { */ @Override public String toString() { - return String.format("Metadata { number: %d, total pages: %d, total elements: %d, size: %d }", number, - totalPages, totalElements, size); + return String.format("Metadata { number: %d, total pages: %d, total elements: %d, size: %d }", number, totalPages, + totalElements, size); } /* diff --git a/src/main/java/org/springframework/hateoas/ResourceSupport.java b/src/main/java/org/springframework/hateoas/ResourceSupport.java index ea5f477d..08a6425b 100755 --- a/src/main/java/org/springframework/hateoas/ResourceSupport.java +++ b/src/main/java/org/springframework/hateoas/ResourceSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,8 @@ package org.springframework.hateoas; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; +import java.util.stream.Collectors; import javax.xml.bind.annotation.XmlElement; @@ -37,14 +38,14 @@ public class ResourceSupport implements Identifiable { private final List links; public ResourceSupport() { - this.links = new ArrayList<>(); + this.links = new ArrayList<>(); } /** * Returns the {@link Link} with a rel of {@link Link#REL_SELF}. */ @JsonIgnore - public Link getId() { + public Optional getId() { return getLink(Link.REL_SELF); } @@ -65,7 +66,7 @@ public class ResourceSupport implements Identifiable { */ public void add(Iterable links) { Assert.notNull(links, "Given links must not be null!"); - links.forEach(this::add); + links.forEach(this::add); } /** @@ -94,7 +95,7 @@ public class ResourceSupport implements Identifiable { * @return */ public boolean hasLink(String rel) { - return getLink(rel) != null; + return getLink(rel).isPresent(); } /** @@ -119,13 +120,23 @@ public class ResourceSupport implements Identifiable { * Returns the link with the given rel. * * @param rel - * @return the link with the given rel or {@literal null} if none found. + * @return the link with the given rel or {@link Optional#empty()} if none found. */ - public Link getLink(String rel) { + public Optional getLink(String rel) { + return getLinks(rel).stream().findFirst(); + } - return getLinks(rel).stream() - .findFirst() - .orElse(null); + /** + * Returns the link with the given rel. + * + * @param rel + * @return the link with the given rel. + * @throws IllegalArgumentException in case no link with the given rel can be found. + */ + public Link getRequiredLink(String rel) { + + return getLink(rel) // + .orElseThrow(() -> new IllegalArgumentException(String.format("No link with rel %s found!", rel))); } /** @@ -135,9 +146,8 @@ public class ResourceSupport implements Identifiable { */ public List getLinks(String rel) { - return links.stream() - .filter(link -> link.getRel().equals(rel)) - .collect(Collectors.toList()); + return links.stream() // + .filter(link -> link.getRel().equals(rel)).collect(Collectors.toList()); } /* diff --git a/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java b/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java index 9a1c4e0f..1d8444fd 100644 --- a/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java +++ b/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java @@ -19,6 +19,7 @@ import static org.springframework.hateoas.core.EncodingUtils.*; import static org.springframework.web.util.UriComponentsBuilder.*; import java.net.URI; +import java.util.Optional; import org.springframework.hateoas.Identifiable; import org.springframework.hateoas.Link; @@ -68,6 +69,8 @@ public abstract class LinkBuilderSupport implements LinkB */ public T slash(Object object) { + object = Optional.class.isInstance(object) ? ((Optional) object).orElse(null) : object; + if (object == null) { return getThis(); } diff --git a/src/test/java/org/springframework/hateoas/LinksUnitTest.java b/src/test/java/org/springframework/hateoas/LinksUnitTest.java index f82281d7..813a4965 100755 --- a/src/test/java/org/springframework/hateoas/LinksUnitTest.java +++ b/src/test/java/org/springframework/hateoas/LinksUnitTest.java @@ -74,8 +74,8 @@ public class LinksUnitTest { */ @Test public void getSingleLinkByRel() { - assertThat(reference.getLink("bar")).isEqualTo(Optional.of(new Link("/somethingElse", "bar"))); - assertThat(reference2.getLink("bar")).isEqualTo(Optional.of(new Link("/somethingElse", "bar").withHreflang("de"))); + assertThat(reference.getLink("bar")).hasValue(new Link("/somethingElse", "bar")); + assertThat(reference2.getLink("bar")).hasValue(new Link("/somethingElse", "bar").withHreflang("de")); } /** @@ -90,7 +90,7 @@ public class LinksUnitTest { Links twoWithCommaInFirst = Links.valueOf(WITH_COMMA.concat(",").concat(SECOND)); - assertThat(twoWithCommaInFirst.getLink("foo")).isEqualTo(Optional.of(withComma)); - assertThat(twoWithCommaInFirst.getLink("bar")).isEqualTo(Optional.of(new Link("/somethingElse", "bar"))); + assertThat(twoWithCommaInFirst.getLink("foo")).hasValue(withComma); + assertThat(twoWithCommaInFirst.getLink("bar")).hasValue(new Link("/somethingElse", "bar")); } } diff --git a/src/test/java/org/springframework/hateoas/ResourceSupportUnitTest.java b/src/test/java/org/springframework/hateoas/ResourceSupportUnitTest.java index 2d305365..a29cc8e8 100755 --- a/src/test/java/org/springframework/hateoas/ResourceSupportUnitTest.java +++ b/src/test/java/org/springframework/hateoas/ResourceSupportUnitTest.java @@ -45,10 +45,10 @@ public class ResourceSupportUnitTest { ResourceSupport support = new ResourceSupport(); support.add(link); - assertThat(support.getId()).isNull(); + assertThat(support.getId()).isEmpty(); assertThat(support.hasLinks()).isTrue(); assertThat(support.hasLink(link.getRel())).isTrue(); - assertThat(support.getLink(link.getRel())).isEqualTo(link); + assertThat(support.getLink(link.getRel())).hasValue(link); assertThat(support.getLinks(Link.REL_NEXT)).contains(link); } @@ -75,7 +75,7 @@ public class ResourceSupportUnitTest { ResourceSupport support = new ResourceSupport(); support.add(Arrays.asList(first, second)); - assertThat(support.getId()).isNull(); + assertThat(support.getId()).isEmpty(); assertThat(support.hasLinks()).isTrue(); assertThat(support.getLinks()).contains(first, second); assertThat(support.getLinks()).hasSize(2); @@ -90,7 +90,7 @@ public class ResourceSupportUnitTest { ResourceSupport support = new ResourceSupport(); support.add(link); - assertThat(support.getId()).isEqualTo(link); + assertThat(support.getId()).hasValue(link); } @Test(expected = IllegalArgumentException.class) diff --git a/src/test/java/org/springframework/hateoas/client/TraversonTest.java b/src/test/java/org/springframework/hateoas/client/TraversonTest.java index 5291580e..281b22ab 100755 --- a/src/test/java/org/springframework/hateoas/client/TraversonTest.java +++ b/src/test/java/org/springframework/hateoas/client/TraversonTest.java @@ -302,7 +302,7 @@ public class TraversonTest { // end::hop-with-param[] assertThat(itemResource.hasLink("self")).isTrue(); - assertThat(itemResource.getLink("self").expand().getHref()) + assertThat(itemResource.getRequiredLink("self").expand().getHref()) .isEqualTo(server.rootResource() + "/springagram/items/1"); final Item item = itemResource.getContent(); @@ -330,7 +330,7 @@ public class TraversonTest { // end::hop-put[] assertThat(itemResource.hasLink("self")).isTrue(); - assertThat(itemResource.getLink("self").expand().getHref()) + assertThat(itemResource.getRequiredLink("self").expand().getHref()) .isEqualTo(server.rootResource() + "/springagram/items/1"); final Item item = itemResource.getContent(); @@ -355,7 +355,7 @@ public class TraversonTest { .withTemplateParameters(params).toObject(resourceParameterizedTypeReference); assertThat(itemResource.hasLink("self")).isTrue(); - assertThat(itemResource.getLink("self").expand().getHref()) + assertThat(itemResource.getRequiredLink("self").expand().getHref()) .isEqualTo(server.rootResource() + "/springagram/items/1"); final Item item = itemResource.getContent(); @@ -376,7 +376,8 @@ public class TraversonTest { toObject(Resource.class); assertThat(itemResource.hasLink("self")).isTrue(); - assertThat(itemResource.getLink("self").expand().getHref()).isEqualTo(server.rootResource() + "/springagram/items"); + assertThat(itemResource.getRequiredLink("self").expand().getHref()) + .isEqualTo(server.rootResource() + "/springagram/items"); } private void setUpActors() { diff --git a/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java b/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java index f4b2308f..3ce3c1fc 100755 --- a/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java @@ -122,7 +122,7 @@ public class ControllerLinkBuilderUnitTest extends TestUtils { public void usesIdOfIdentifyableForPathSegment() { Identifiable identifyable = Mockito.mock(Identifiable.class); - Mockito.when(identifyable.getId()).thenReturn(10L); + Mockito.when(identifyable.getId()).thenReturn(Optional.of(10L)); Link link = linkTo(PersonControllerImpl.class).slash(identifyable).withSelfRel(); assertThat(link.getHref()).endsWith("/people/10"); @@ -599,8 +599,8 @@ public class ControllerLinkBuilderUnitTest extends TestUtils { Long id; @Override - public Long getId() { - return id; + public Optional getId() { + return Optional.ofNullable(id); } } diff --git a/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java b/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java index ef490d75..dc1b1bb4 100755 --- a/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java @@ -20,6 +20,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; import java.util.Arrays; import java.util.List; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -53,7 +54,7 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { public void createsInstanceWithSelfLinkToController() { PersonResource resource = assembler.createResource(person); - Link link = resource.getLink(Link.REL_SELF); + Link link = resource.getRequiredLink(Link.REL_SELF); assertThat(link).isNotNull(); assertThat(resource.getLinks()).hasSize(1); @@ -63,8 +64,10 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { public void usesAlternateIdIfGivenExplicitly() { PersonResource resource = assembler.createResourceWithId(person.alternateId, person); - Link selfLink = resource.getId(); - assertThat(selfLink.getHref()).endsWith("/people/id"); + Optional selfLink = resource.getId(); + + assertThat(selfLink.map(Link::getHref)) // + .hasValueSatisfying(it -> assertThat(it.endsWith("/people/id"))); } @Test @@ -72,8 +75,10 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { PersonResource resource = new PersonResourceAssembler(ParameterizedController.class).createResource(person, person, "bar"); - Link selfLink = resource.getId(); - assertThat(selfLink.getHref()).endsWith("/people/10/bar/addresses/10"); + Optional selfLink = resource.getId(); + + assertThat(selfLink.map(Link::getHref)) // + .hasValueSatisfying(it -> assertThat(it.endsWith("/people/id"))); } @Test @@ -114,8 +119,8 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { String alternateId; @Override - public Long getId() { - return id; + public Optional getId() { + return Optional.ofNullable(id); } }