diff --git a/pom.xml b/pom.xml
index 6e698f5b..3acca4ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -631,7 +631,7 @@
3.1.0provided
-
+
com.tngtech.archunitarchunit
@@ -729,6 +729,9 @@
org.jetbrains.kotlinkotlin-maven-plugin${kotlin.version}
+
+ ${source.level}
+ compile
diff --git a/src/main/java/org/springframework/hateoas/Affordance.java b/src/main/java/org/springframework/hateoas/Affordance.java
index c7a1edac..d5de9a2e 100644
--- a/src/main/java/org/springframework/hateoas/Affordance.java
+++ b/src/main/java/org/springframework/hateoas/Affordance.java
@@ -15,6 +15,8 @@
*/
package org.springframework.hateoas;
+import lombok.AccessLevel;
+import lombok.Getter;
import lombok.Value;
import java.util.HashMap;
@@ -42,8 +44,18 @@ public class Affordance {
/**
* Collection of {@link AffordanceModel}s related to this affordance.
*/
- private final Map affordanceModels = new HashMap<>();
+ private final @Getter(AccessLevel.PACKAGE) Map affordanceModels = new HashMap<>();
+ /**
+ * Creates a new {@link Affordance}.
+ *
+ * @param name
+ * @param link
+ * @param httpMethod
+ * @param inputType
+ * @param queryMethodParameters
+ * @param outputType
+ */
public Affordance(String name, Link link, HttpMethod httpMethod, ResolvableType inputType,
List queryMethodParameters, ResolvableType outputType) {
diff --git a/src/main/java/org/springframework/hateoas/AffordanceModel.java b/src/main/java/org/springframework/hateoas/AffordanceModel.java
index 76451c46..8d8df39a 100644
--- a/src/main/java/org/springframework/hateoas/AffordanceModel.java
+++ b/src/main/java/org/springframework/hateoas/AffordanceModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 the original author or authors.
+ * Copyright 2018-2019 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.
@@ -23,11 +23,13 @@ import java.util.List;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
+import org.springframework.util.Assert;
/**
* Collection of attributes needed to render any form of hypermedia.
- *
+ *
* @author Greg Turnquist
+ * @author Oliver Drotbohm
*/
@EqualsAndHashCode
@AllArgsConstructor
@@ -58,7 +60,7 @@ public abstract class AffordanceModel {
* Collection of {@link QueryParameter}s to interrogate a resource.
*/
private List queryMethodParameters;
-
+
/**
* Response body domain type.
*/
@@ -72,4 +74,30 @@ public abstract class AffordanceModel {
public String getURI() {
return this.link.expand().getHref();
}
+
+ /**
+ * Returns whether the {@link Affordance} has the given {@link HttpMethod}.
+ *
+ * @param method must not be {@literal null}.
+ * @return
+ */
+ public boolean hasHttpMethod(HttpMethod method) {
+
+ Assert.notNull(method, "HttpMethod must not be null!");
+
+ return this.httpMethod.equals(method);
+ }
+
+ /**
+ * Returns whether the {@link Affordance} points to the target of the given {@link Link}.
+ *
+ * @param link must not be {@literal null}.
+ * @return
+ */
+ public boolean pointsToTargetOf(Link link) {
+
+ Assert.notNull(link, "Link must not be null!");
+
+ return getURI().equals(link.expand().getHref());
+ }
}
diff --git a/src/main/java/org/springframework/hateoas/AffordanceModelFactory.java b/src/main/java/org/springframework/hateoas/AffordanceModelFactory.java
index a7434480..fca05ff9 100644
--- a/src/main/java/org/springframework/hateoas/AffordanceModelFactory.java
+++ b/src/main/java/org/springframework/hateoas/AffordanceModelFactory.java
@@ -16,27 +16,23 @@
package org.springframework.hateoas;
import java.util.List;
-import java.util.Optional;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
-import org.springframework.plugin.core.Plugin;
/**
* @author Greg Turnquist
* @author Oliver Gierke
*/
-public interface AffordanceModelFactory extends Plugin {
+public interface AffordanceModelFactory {
/**
* Declare the {@link MediaType} this factory supports.
*
* @return
*/
- default MediaType getMediaType() {
- return null;
- }
+ MediaType getMediaType();
/**
* Look up the {@link AffordanceModel} for this factory.
@@ -51,18 +47,4 @@ public interface AffordanceModelFactory extends Plugin {
*/
AffordanceModel getAffordanceModel(String name, Link link, HttpMethod httpMethod, ResolvableType inputType,
List queryMethodParameters, ResolvableType outputType);
-
- /**
- * Returns if a plugin should be invoked according to the given delimiter.
- *
- * @param delimiter
- * @return if the plugin should be invoked
- */
- @Override
- default boolean supports(MediaType delimiter) {
-
- return Optional.ofNullable(getMediaType()) //
- .map(mediaType -> mediaType.equals(delimiter)) //
- .orElse(false);
- }
}
diff --git a/src/main/java/org/springframework/hateoas/EntityLinks.java b/src/main/java/org/springframework/hateoas/EntityLinks.java
index 26d31363..d7c7bac2 100644
--- a/src/main/java/org/springframework/hateoas/EntityLinks.java
+++ b/src/main/java/org/springframework/hateoas/EntityLinks.java
@@ -71,7 +71,7 @@ public interface EntityLinks extends Plugin> {
/**
* Creates a {@link Link} pointing to the collection resource of the given type. The relation type of the link will be
- * determined by the implementation class and should be defaulted to {@link IanaLinkRelation#SELF}.
+ * determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
* @param type the entity type to point to, must not be {@literal null}.
* @return the {@link Link} pointing to the collection resource exposed for the given entity. Will never be
@@ -82,7 +82,7 @@ public interface EntityLinks extends Plugin> {
/**
* Creates a {@link Link} pointing to single resource backing the given entity type and id. The relation type of the
- * link will be determined by the implementation class and should be defaulted to {@link IanaLinkRelation#SELF}.
+ * link will be determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
* @param type the entity type to point to, must not be {@literal null}.
* @param id the identifier of the entity of the given type
@@ -94,7 +94,7 @@ public interface EntityLinks extends Plugin> {
/**
* Creates a {@link Link} pointing to single resource backing the given entity. The relation type of the link will be
- * determined by the implementation class and should be defaulted to {@link IanaLinkRelation#SELF}.
+ * determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
* @param entity the entity type to point to, must not be {@literal null}.
* @return the {@link Link} pointing to the resource exposed for the given entity. Will never be {@literal null}.
diff --git a/src/main/java/org/springframework/hateoas/IanaLinkRelation.java b/src/main/java/org/springframework/hateoas/IanaLinkRelations.java
similarity index 59%
rename from src/main/java/org/springframework/hateoas/IanaLinkRelation.java
rename to src/main/java/org/springframework/hateoas/IanaLinkRelations.java
index 5eefb4fe..3fac900e 100644
--- a/src/main/java/org/springframework/hateoas/IanaLinkRelation.java
+++ b/src/main/java/org/springframework/hateoas/IanaLinkRelations.java
@@ -15,167 +15,172 @@
*/
package org.springframework.hateoas;
+import lombok.experimental.UtilityClass;
+
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
+import org.springframework.util.ReflectionUtils;
+
/**
* Capture standard IANA-based link relations.
*
* @see https://www.iana.org/assignments/link-relations/link-relations.xhtml
* @see https://tools.ietf.org/html/rfc8288
* @see https://github.com/link-relations/registry
- *
* @author Greg Turnquist
* @author Roland Kulcsár
+ * @author Oliver Gierke
* @since 1.0
*/
-public enum IanaLinkRelation implements LinkRelation {
+@UtilityClass
+public class IanaLinkRelations {
/**
* Refers to a resource that is the subject of the link's context.
*
* @see https://tools.ietf.org/html/rfc6903
*/
- ABOUT("about"),
+ public static final LinkRelation ABOUT = LinkRelation.of("about");
/**
* Refers to a substitute for this context
*
* @see http://www.w3.org/TR/html5/links.html#link-type-alternate
*/
- ALTERNATE("alternate"),
+ public static final LinkRelation ALTERNATE = LinkRelation.of("alternate");
/**
* Refers to an appendix.
- *
+ *
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- APPENDIX("appendix"),
+ public static final LinkRelation APPENDIX = LinkRelation.of("appendix");
/**
* Refers to a collection of records, documents, or other materials of historical interest.
*
* @see http://www.w3.org/TR/2011/WD-html5-20110113/links.html#rel-archives
*/
- ARCHIVES("archives"),
+ public static final LinkRelation ARCHIVES = LinkRelation.of("archives");
/**
* Refers to the context's author.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-author
*/
- AUTHOR("author"),
+ public static final LinkRelation AUTHOR = LinkRelation.of("author");
/**
* Identifies the entity that blocks access to a resource following receipt of a legal demand.
- *
+ *
* @see https://tools.ietf.org/html/rfc7725
*/
- BLOCKED_BY("blocked-by"),
+ public static final LinkRelation BLOCKED_BY = LinkRelation.of("blocked-by");
/**
* Gives a permanent link to use for bookmarking purposes.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-bookmark
*/
- BOOKMARK("bookmark"),
+ public static final LinkRelation BOOKMARK = LinkRelation.of("bookmark");
/**
* Designates the preferred version of a resource (the IRI and its contents).
*
* @see https://tools.ietf.org/html/rfc6596
*/
- CANONICAL("canonical"),
+ public static final LinkRelation CANONICAL = LinkRelation.of("canonical");
/**
* Refers to a chapter in a collection of resources.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- CHAPTER("chapter"),
+ public static final LinkRelation CHAPTER = LinkRelation.of("chapter");
/**
* Indicates that the link target is preferred over the link context for the purpose of referencing.
*
* @see https://datatracker.ietf.org/doc/draft-vandesompel-citeas/
*/
- CITE_AS("cite-as"),
+ public static final LinkRelation CITE_AS = LinkRelation.of("cite-as");
/**
* The target IRI points to a resource which represents the collection resource for the context IRI.
*
* @see https://tools.ietf.org/html/rfc6573
*/
- COLLECTION("collection"),
+ public static final LinkRelation COLLECTION = LinkRelation.of("collection");
/**
* Refers to a table of contents.
+ *
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- CONTENTS("contents"),
+ public static final LinkRelation CONTENTS = LinkRelation.of("contents");
/**
- * The document linked to was later converted to the document that contains this link relation.
- * For example, an RFC can have a link to the Internet-Draft that became the RFC; in that case,
- * the link relation would be "convertedFrom".
- *
+ * The document linked to was later converted to the document that contains this link relation. For example, an RFC
+ * can have a link to the Internet-Draft that became the RFC; in that case, the link relation would be
+ * "convertedFrom".
+ *
* @see https://tools.ietf.org/html/rfc7991
*/
- CONVERTED_FROM("convertedFrom"),
+ public static final LinkRelation CONVERTED_FROM = LinkRelation.of("convertedFrom");
/**
* Refers to a copyright statement that applies to the link's context.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- COPYRIGHT("copyright"),
+ public static final LinkRelation COPYRIGHT = LinkRelation.of("copyright");
/**
* The target IRI points to a resource where a submission form can be obtained.
*
* @see https://tools.ietf.org/html/rfc6861
*/
- CREATE_FORM("create-form"),
+ public static final LinkRelation CREATE_FORM = LinkRelation.of("create-form");
/**
* Refers to a resource containing the most recent item(s) in a collection of resources.
*
* @see https://tools.ietf.org/html/rfc5005
*/
- CURRENT("current"),
+ public static final LinkRelation CURRENT = LinkRelation.of("current");
/**
* Refers to a resource providing information about the link's context.
*
* @see http://www.w3.org/TR/powder-dr/#assoc-linking
*/
- DESCRIBED_BY("describedBy"),
+ public static final LinkRelation DESCRIBED_BY = LinkRelation.of("describedBy");
/**
* The relationship A 'describes' B asserts that resource A provides a description of resource B. There are no
- * constraints on the format or representation of either A or B, neither are there any further constraints on
- * either resource.
+ * constraints on the format or representation of either A or B, neither are there any further constraints on either
+ * resource.
*
* @see https://tools.ietf.org/html/rfc6892
*/
- DESCRIBES("describes"),
+ public static final LinkRelation DESCRIBES = LinkRelation.of("describes");
/**
- * Refers to a list of patent disclosures made with respect to material for which 'disclosure' relation is
- * specified.
+ * Refers to a list of patent disclosures made with respect to material for which 'disclosure' relation is specified.
*
* @see https://tools.ietf.org/html/rfc6579
*/
- DISCLOSURE("disclosure"),
+ public static final LinkRelation DISCLOSURE = LinkRelation.of("disclosure");
/**
- * Used to indicate an origin that will be used to fetch required resources for the link context, and that the
- * user agent ought to resolve as early as possible.
+ * Used to indicate an origin that will be used to fetch required resources for the link context, and that the user
+ * agent ought to resolve as early as possible.
*
* @see https://www.w3.org/TR/resource-hints/
*/
- DNS_PREFETCH("dns-prefetch"),
+ public static final LinkRelation DNS_PREFETCH = LinkRelation.of("dns-prefetch");
/**
* Refers to a resource whose available representations are byte-for-byte identical with the corresponding
@@ -183,100 +188,100 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see https://tools.ietf.org/html/rfc6249
*/
- DUPLICATE("duplicate"),
+ public static final LinkRelation DUPLICATE = LinkRelation.of("duplicate");
/**
* Refers to a resource that can be used to edit the link's context.
*
* @see https://tools.ietf.org/html/rfc5023
*/
- EDIT("edit"),
+ public static final LinkRelation EDIT = LinkRelation.of("edit");
/**
* The target IRI points to a resource where a submission form for editing associated resource can be obtained.
*
* @see https://tools.ietf.org/html/rfc6861
*/
- EDIT_FORM("edit-form"),
+ public static final LinkRelation EDIT_FORM = LinkRelation.of("edit-form");
/**
* Refers to a resource that can be used to edit media associated with the link's context.
*
* @see https://tools.ietf.org/html/rfc5023
*/
- EDIT_MEDIA("edit-media"),
+ public static final LinkRelation EDIT_MEDIA = LinkRelation.of("edit-media");
/**
* Identifies a related resource that is potentially large and might require special handling.
*
* @see https://tools.ietf.org/html/rfc4287
*/
- ENCLOSURE("enclosure"),
+ public static final LinkRelation ENCLOSURE = LinkRelation.of("enclosure");
/**
* An IRI that refers to the furthest preceding resource in a series of resources.
*
* @see https://tools.ietf.org/html/rfc8288
*/
- FIRST("first"),
+ public static final LinkRelation FIRST = LinkRelation.of("first");
/**
* Refers to a glossary of terms.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- GLOSSARY("glossary"),
+ public static final LinkRelation GLOSSARY = LinkRelation.of("glossary");
/**
* Refers to context-sensitive help.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-help
*/
- HELP("help"),
+ public static final LinkRelation HELP = LinkRelation.of("help");
/**
* Refers to a resource hosted by the server indicated by the link context.
*
* @see https://tools.ietf.org/html/rfc6690
*/
- HOSTS("hosts"),
+ public static final LinkRelation HOSTS = LinkRelation.of("hosts");
/**
* Refers to a hub that enables registration for notification of updates to the context.
*
* @see http://pubsubhubbub.googlecode.com
*/
- HUB("hub"),
+ public static final LinkRelation HUB = LinkRelation.of("hub");
/**
* Refers to an icon representing the link's context.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-icon
*/
- ICON("icon"),
+ public static final LinkRelation ICON = LinkRelation.of("icon");
/**
* Refers to an index.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- INDEX("index"),
+ public static final LinkRelation INDEX = LinkRelation.of("index");
/**
- * refers to a resource associated with a time interval that ends before the beginning of the time interval
- * associated with the context resource
+ * refers to a resource associated with a time interval that ends before the beginning of the time interval associated
+ * with the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalAfter section 4.2.21
*/
- INTERVAL_AFTER("intervalAfter"),
+ public static final LinkRelation INTERVAL_AFTER = LinkRelation.of("intervalAfter");
/**
- * refers to a resource associated with a time interval that begins after the end of the time interval associated
- * with the context resource
+ * refers to a resource associated with a time interval that begins after the end of the time interval associated with
+ * the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalBefore section 4.2.22
*/
- INTERVAL_BEFORE("intervalBefore"),
+ public static final LinkRelation INTERVAL_BEFORE = LinkRelation.of("intervalBefore");
/**
* refers to a resource associated with a time interval that begins after the beginning of the time interval
@@ -285,15 +290,15 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see https://www.w3.org/TR/owl-time/#time:intervalContains section 4.2.23
*/
- INTERVAL_CONTAINS("intervalContains"),
+ public static final LinkRelation INTERVAL_CONTAINS = LinkRelation.of("intervalContains");
/**
- * refers to a resource associated with a time interval that begins after the end of the time interval associated
- * with the context resource, or ends before the beginning of the time interval associated with the context resource
+ * refers to a resource associated with a time interval that begins after the end of the time interval associated with
+ * the context resource, or ends before the beginning of the time interval associated with the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalDisjoint section 4.2.24
*/
- INTERVAL_DISJOINT("intervalDisjoint"),
+ public static final LinkRelation INTERVAL_DISJOINT = LinkRelation.of("intervalDisjoint");
/**
* refers to a resource associated with a time interval that begins before the beginning of the time interval
@@ -302,68 +307,68 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see https://www.w3.org/TR/owl-time/#time:intervalDuring section 4.2.25
*/
- INTERVAL_DURING("intervalDuring"),
+ public static final LinkRelation INTERVAL_DURING = LinkRelation.of("intervalDuring");
/**
* refers to a resource associated with a time interval whose beginning coincides with the beginning of the time
- * interval associated with the context resource, and whose end coincides with the end of the time interval
- * associated with the context resource
+ * interval associated with the context resource, and whose end coincides with the end of the time interval associated
+ * with the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalEquals section 4.2.26
*/
- INTERVAL_EQUALS("intervalEquals"),
+ public static final LinkRelation INTERVAL_EQUALS = LinkRelation.of("intervalEquals");
/**
* refers to a resource associated with a time interval that begins after the beginning of the time interval
- * associated with the context resource, and whose end coincides with the end of the time interval associated with
- * the context resource
+ * associated with the context resource, and whose end coincides with the end of the time interval associated with the
+ * context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalFinishedBy section 4.2.27
*/
- INTERVAL_FINISHED_BY("intervalFinishedBy"),
+ public static final LinkRelation INTERVAL_FINISHED_BY = LinkRelation.of("intervalFinishedBy");
/**
* refers to a resource associated with a time interval that begins before the beginning of the time interval
- * associated with the context resource, and whose end coincides with the end of the time interval associated with
- * the context resource
+ * associated with the context resource, and whose end coincides with the end of the time interval associated with the
+ * context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalFinishes section 4.2.28
*/
- INTERVAL_FINISHES("intervalFinishes"),
+ public static final LinkRelation INTERVAL_FINISHES = LinkRelation.of("intervalFinishes");
/**
- * refers to a resource associated with a time interval that begins before or is coincident with the beginning
- * of the time interval associated with the context resource, and ends after or is coincident with the end of the
- * time interval associated with the context resource
+ * refers to a resource associated with a time interval that begins before or is coincident with the beginning of the
+ * time interval associated with the context resource, and ends after or is coincident with the end of the time
+ * interval associated with the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalIn section 4.2.29
*/
- INTERVAL_IN("intervalIn"),
+ public static final LinkRelation INTERVAL_IN = LinkRelation.of("intervalIn");
/**
- * refers to a resource associated with a time interval whose beginning coincides with the end of the time
- * interval associated with the context resource
+ * refers to a resource associated with a time interval whose beginning coincides with the end of the time interval
+ * associated with the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalMeets section 4.2.30
*/
- INTERVAL_MEETS("intervalMeets"),
+ public static final LinkRelation INTERVAL_MEETS = LinkRelation.of("intervalMeets");
/**
- * refers to a resource associated with a time interval whose beginning coincides with the end of the time
- * interval associated with the context resource
+ * refers to a resource associated with a time interval whose beginning coincides with the end of the time interval
+ * associated with the context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalMetBy section 4.2.31
*/
- INTERVAL_MET_BY("intervalMetBy"),
+ public static final LinkRelation INTERVAL_MET_BY = LinkRelation.of("intervalMetBy");
/**
* refers to a resource associated with a time interval that begins before the beginning of the time interval
- * associated with the context resource, and ends after the beginning of the time interval associated with the
- * context resource
+ * associated with the context resource, and ends after the beginning of the time interval associated with the context
+ * resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalOverlappedBy section 4.2.32
*/
- INTERVAL_OVERLAPPED_BY("intervalOverlappedBy"),
+ public static final LinkRelation INTERVAL_OVERLAPPED_BY = LinkRelation.of("intervalOverlappedBy");
/**
* refers to a resource associated with a time interval that begins before the end of the time interval associated
@@ -371,16 +376,16 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see https://www.w3.org/TR/owl-time/#time:intervalOverlaps section 4.2.33
*/
- INTERVAL_OVERLAPS("intervalOverlaps"),
+ public static final LinkRelation INTERVAL_OVERLAPS = LinkRelation.of("intervalOverlaps");
/**
* refers to a resource associated with a time interval whose beginning coincides with the beginning of the time
- * interval associated with the context resource, and ends before the end of the time interval associated with
- * the context resource
+ * interval associated with the context resource, and ends before the end of the time interval associated with the
+ * context resource
*
* @see https://www.w3.org/TR/owl-time/#time:intervalStartedBy section 4.2.34
*/
- INTERVAL_STARTED_BY("intervalStartedBy"),
+ public static final LinkRelation INTERVAL_STARTED_BY = LinkRelation.of("intervalStartedBy");
/**
* refers to a resource associated with a time interval whose beginning coincides with the beginning of the time
@@ -389,140 +394,140 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see https://www.w3.org/TR/owl-time/#time:intervalStarts section 4.2.35
*/
- INTERVAL_STARTS("intervalStarts"),
+ public static final LinkRelation INTERVAL_STARTS = LinkRelation.of("intervalStarts");
/**
* The target IRI points to a resource that is a member of the collection represented by the context IRI.
*
* @see https://tools.ietf.org/html/rfc6573
*/
- ITEM("item"),
+ public static final LinkRelation ITEM = LinkRelation.of("item");
/**
* An IRI that refers to the furthest following resource in a series of resources.
*
* @see https://tools.ietf.org/html/rfc8288
*/
- LAST("last"),
+ public static final LinkRelation LAST = LinkRelation.of("last");
/**
* Points to a resource containing the latest (e.g., current) version of the context.
*
* @see https://tools.ietf.org/html/rfc5829
*/
- LATEST_VERSION("latest-version"),
+ public static final LinkRelation LATEST_VERSION = LinkRelation.of("latest-version");
/**
* Refers to a license associated with this context.
*
* @see https://tools.ietf.org/html/rfc4946
*/
- LICENSE("license"),
+ public static final LinkRelation LICENSE = LinkRelation.of("license");
/**
* Refers to further information about the link's context, expressed as a LRDD ("Link-based Resource Descriptor
- * Document") resource. See RFC6415 for information about processing this relation type in host-meta documents.
- * When used elsewhere, it refers to additional links and other metadata. Multiple instances indicate additional
- * LRDD resources. LRDD resources MUST have an "application/xrd+xml" representation, and MAY have others.
+ * Document") resource. See RFC6415 for information about processing this relation type in host-meta documents. When
+ * used elsewhere, it refers to additional links and other metadata. Multiple instances indicate additional LRDD
+ * resources. LRDD resources MUST have an "application/xrd+xml" representation, and MAY have others.
*
* @see https://tools.ietf.org/html/rfc6415
*/
- LRDD("lrdd"),
+ public static final LinkRelation LRDD = LinkRelation.of("lrdd");
/**
* The Target IRI points to a Memento, a fixed resource that will not change state anymore.
*
* @see https://tools.ietf.org/html/rfc7089
*/
- MEMENTO("memento"),
+ public static final LinkRelation MEMENTO = LinkRelation.of("memento");
/**
* Refers to a resource that can be used to monitor changes in an HTTP resource.
*
* @see https://tools.ietf.org/html/rfc5989
*/
- MONITOR("monitor"),
+ public static final LinkRelation MONITOR = LinkRelation.of("monitor");
/**
* Refers to a resource that can be used to monitor changes in a specified group of HTTP resources.
*
* @see https://tools.ietf.org/html/rfc5989
*/
- MONITOR_GROUP("monitor-group"),
+ public static final LinkRelation MONITOR_GROUP = LinkRelation.of("monitor-group");
/**
* Indicates that the link's context is a part of a series, and that the next in the series is the link target.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-next
*/
- NEXT("next"),
+ public static final LinkRelation NEXT = LinkRelation.of("next");
/**
* Refers to the immediately following archive resource.
*
* @see https://tools.ietf.org/html/rfc5005
*/
- NEXT_ARCHIVE("next-archive"),
+ public static final LinkRelation NEXT_ARCHIVE = LinkRelation.of("next-archive");
/**
* Indicates that the context’s original author or publisher does not endorse the link target.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-nofollow
*/
- NOFOLLOW("nofollow"),
+ public static final LinkRelation NOFOLLOW = LinkRelation.of("nofollow");
/**
* Indicates that no referrer information is to be leaked when following the link.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-noreferrer
*/
- NOREFERRER("noreferrer"),
+ public static final LinkRelation NOREFERRER = LinkRelation.of("noreferrer");
/**
* The Target IRI points to an Original Resource.
*
* @see https://tools.ietf.org/html/rfc7089
*/
- ORIGINAL("original"),
+ public static final LinkRelation ORIGINAL = LinkRelation.of("original");
/**
* Indicates a resource where payment is accepted.
*
* @see https://tools.ietf.org/html/rfc8288
*/
- PAYMENT("payment"),
+ public static final LinkRelation PAYMENT = LinkRelation.of("payment");
/**
* Gives the address of the pingback resource for the link context.
*
* @see http://www.hixie.ch/specs/pingback/pingback
*/
- PINGBACK("pingback"),
+ public static final LinkRelation PINGBACK = LinkRelation.of("pingback");
/**
- * Used to indicate an origin that will be used to fetch required resources for the link context. Initiating an
- * early connection, which includes the DNS lookup, TCP handshake, and optional TLS negotiation, allows the user
- * agent to mask the high latency costs of establishing a connection.
+ * Used to indicate an origin that will be used to fetch required resources for the link context. Initiating an early
+ * connection, which includes the DNS lookup, TCP handshake, and optional TLS negotiation, allows the user agent to
+ * mask the high latency costs of establishing a connection.
*
* @see https://www.w3.org/TR/resource-hints/
*/
- PRECONNECT("preconnect"),
+ public static final LinkRelation PRECONNECT = LinkRelation.of("preconnect");
/**
* Points to a resource containing the predecessor version in the version history.
*
* @see https://tools.ietf.org/html/rfc5829
*/
- PREDECESSOR_VERSION("predecessor-version"),
+ public static final LinkRelation PREDECESSOR_VERSION = LinkRelation.of("predecessor-version");
/**
- * The prefetch link relation type is used to identify a resource that might be required by the next navigation
- * from the link context, and that the user agent ought to fetch, such that the user agent can deliver a faster
- * response once the resource is requested in the future.
- *
+ * The prefetch link relation type is used to identify a resource that might be required by the next navigation from
+ * the link context, and that the user agent ought to fetch, such that the user agent can deliver a faster response
+ * once the resource is requested in the future.
+ *
* @see http://www.w3.org/TR/resource-hints/
*/
- PREFETCH("prefetch"),
+ public static final LinkRelation PREFETCH = LinkRelation.of("prefetch");
/**
* Refers to a resource that should be loaded early in the processing of the link's context, without blocking
@@ -530,51 +535,51 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see http://www.w3.org/TR/preload/
*/
- PRELOAD("preload"),
+ public static final LinkRelation PRELOAD = LinkRelation.of("preload");
/**
- * Used to identify a resource that might be required by the next navigation from the link context, and that the
- * user agent ought to fetch and execute, such that the user agent can deliver a faster response once the resource
- * is requested in the future.
+ * Used to identify a resource that might be required by the next navigation from the link context, and that the user
+ * agent ought to fetch and execute, such that the user agent can deliver a faster response once the resource is
+ * requested in the future.
*
* @see https://www.w3.org/TR/resource-hints/
*/
- PRERENDER("prerender"),
+ public static final LinkRelation PRERENDER = LinkRelation.of("prerender");
/**
* Indicates that the link's context is a part of a series, and that the previous in the series is the link target.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-prev
*/
- PREV("prev"),
+ public static final LinkRelation PREV = LinkRelation.of("prev");
/**
* Refers to a resource that provides a preview of the link's context.
*
* @see https://tools.ietf.org/html/rfc6903, section 3
*/
- PREVIEW("preview"),
+ public static final LinkRelation PREVIEW = LinkRelation.of("preview");
/**
- * Refers to the previous resource in an ordered series of resources. Synonym for "prev".
+ * Refers to the previous resource in an ordered series of resources. Synonym for "prev".
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- PREVIOUS("previous"),
+ public static final LinkRelation PREVIOUS = LinkRelation.of("previous");
/**
* Refers to the immediately preceding archive resource.
*
* @see https://tools.ietf.org/html/rfc5005
*/
- PREV_ARCHIVE("prev-archive"),
+ public static final LinkRelation PREV_ARCHIVE = LinkRelation.of("prev-archive");
/**
* Refers to a privacy policy associated with the link's context.
*
* @see https://tools.ietf.org/html/rfc6903, section 4
*/
- PRIVACY_POLICY("privacy-policy"),
+ public static final LinkRelation PRIVACY_POLICY = LinkRelation.of("privacy-policy");
/**
* Identifying that a resource representation conforms to a certain profile, without affecting the non-profile
@@ -582,253 +587,217 @@ public enum IanaLinkRelation implements LinkRelation {
*
* @see https://tools.ietf.org/html/rfc6906
*/
- PROFILE("profile"),
+ public static final LinkRelation PROFILE = LinkRelation.of("profile");
/**
* Identifies a related resource.
*
* @see https://tools.ietf.org/html/rfc4287
*/
- RELATED("related"),
+ public static final LinkRelation RELATED = LinkRelation.of("related");
/**
- * Identifies the root of RESTCONF API as configured on this HTTP server. The "restconf" relation defines the
- * root of the API defined in RFC8040. Subsequent revisions of RESTCONF will use alternate relation values to
- * support protocol versioning.
+ * Identifies the root of RESTCONF API as configured on this HTTP server. The "restconf" relation defines the root of
+ * the API defined in RFC8040. Subsequent revisions of RESTCONF will use alternate relation values to support protocol
+ * versioning.
*
* @see https://tools.ietf.org/html/rfc8040
*/
- RESTCONF("restconf"),
+ public static final LinkRelation RESTCONF = LinkRelation.of("restconf");
/**
* Identifies a resource that is a reply to the context of the link.
*
* @see https://tools.ietf.org/html/rfc4685
*/
- REPLIES("replies"),
+ public static final LinkRelation REPLIES = LinkRelation.of("replies");
/**
* Refers to a resource that can be used to search through the link's context and related resources.
*
* @see http://www.opensearch.org/Specifications/OpenSearch/1.1
*/
- SEARCH("search"),
+ public static final LinkRelation SEARCH = LinkRelation.of("search");
/**
* Refers to a section in a collection of resources.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- SECTION("section"),
+ public static final LinkRelation SECTION = LinkRelation.of("section");
/**
* Conveys an identifier for the link's context.
*
* @see https://tools.ietf.org/html/rfc4287
*/
- SELF("self"),
+ public static final LinkRelation SELF = LinkRelation.of("self");
/**
* Indicates a URI that can be used to retrieve a service document.
*
* @see https://tools.ietf.org/html/rfc5023
*/
- SERVICE("service"),
+ public static final LinkRelation SERVICE = LinkRelation.of("service");
/**
* Refers to the first resource in a collection of resources.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- START("start"),
+ public static final LinkRelation START = LinkRelation.of("start");
/**
* Refers to a stylesheet.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-stylesheet
*/
- STYLESHEET("stylesheet"),
+ public static final LinkRelation STYLESHEET = LinkRelation.of("stylesheet");
/**
* Refers to a resource serving as a subsection in a collection of resources.
*
* @see http://www.w3.org/TR/1999/REC-html401-19991224
*/
- SUBSECTION("subsection"),
+ public static final LinkRelation SUBSECTION = LinkRelation.of("subsection");
/**
* Points to a resource containing the successor version in the version history.
*
* @see https://tools.ietf.org/html/rfc5829
*/
- SUCCESSOR_VERSION("successor-versions"),
+ public static final LinkRelation SUCCESSOR_VERSION = LinkRelation.of("successor-versions");
/**
* Gives a tag (identified by the given address) that applies to the current document.
*
* @see http://www.w3.org/TR/html5/links.html#link-type-tag
*/
- TAG("tag"),
+ public static final LinkRelation TAG = LinkRelation.of("tag");
/**
* Refers to the terms of service associated with the link's context.
*
* @see https://tools.ietf.org/html/rfc6903, section 5
*/
- TERMS_OF_SERVICE("terms-of-service"),
+ public static final LinkRelation TERMS_OF_SERVICE = LinkRelation.of("terms-of-service");
/**
* The Target IRI points to a TimeGate for an Original Resource.
*
* @see https://tools.ietf.org/html/rfc7089
*/
- TIMEGATE("timegate"),
+ public static final LinkRelation TIMEGATE = LinkRelation.of("timegate");
/**
* The Target IRI points to a TimeMap for an Original Resource.
*
* @see https://tools.ietf.org/html/rfc7089
*/
- TIMEMAP("timemap"),
+ public static final LinkRelation TIMEMAP = LinkRelation.of("timemap");
/**
- * Refers to a resource identifying the abstract semantic type of which the link's context is considered to be
- * an instance.
+ * Refers to a resource identifying the abstract semantic type of which the link's context is considered to be an
+ * instance.
*
* @see https://tools.ietf.org/html/rfc6903, section 6
*/
- TYPE("type"),
+ public static final LinkRelation TYPE = LinkRelation.of("type");
/**
* Refers to a parent document in a hierarchy of documents.
*
* @see https://tools.ietf.org/html/rfc8288
*/
- UP("up"),
+ public static final LinkRelation UP = LinkRelation.of("up");
/**
* Points to a resource containing the version history for the context.
*
* @see https://tools.ietf.org/html/rfc5829
*/
- VERSION_HISTORY("version-history"),
+ public static final LinkRelation VERSION_HISTORY = LinkRelation.of("version-history");
/**
* Identifies a resource that is the source of the information in the link's context.
*
* @see https://tools.ietf.org/html/rfc4287
*/
- VIA("via"),
+ public static final LinkRelation VIA = LinkRelation.of("via");
/**
- * Identifies a target URI that supports the Webmention protcol. This allows clients that mention a resource in
- * some form of publishing process to contact that endpoint and inform it that this resource has been mentioned.
+ * Identifies a target URI that supports the Webmention protcol. This allows clients that mention a resource in some
+ * form of publishing process to contact that endpoint and inform it that this resource has been mentioned.
*
* @see http://www.w3.org/TR/webmention/
*/
- WEBMENTION("webmention"),
+ public static final LinkRelation WEBMENTION = LinkRelation.of("webmention");
/**
* Points to a working copy for this resource.
+ *
* @see https://tools.ietf.org/html/rfc5829
*/
- WORKING_COPY("working-copy"),
+ public static final LinkRelation WORKING_COPY = LinkRelation.of("working-copy");
/**
* Points to the versioned resource from which this working copy was obtained.
- *
+ *
* @see https://tools.ietf.org/html/rfc5829
*/
- WORKING_COPY_OF("working-copy-of");
-
- /**
- * Actual IANA value for the link relation.
- */
- private final String value;
+ public static final LinkRelation WORKING_COPY_OF = LinkRelation.of("working-copy-of");
/**
- * Initialize the enum value.
- *
- * @param value
+ * Consolidated collection of {@link IanaLinkRelations}s.
*/
- IanaLinkRelation(String value) {
- this.value = value;
- }
-
- /**
- * Return the IANA value.
- */
- @Override
- public String value() {
- return this.value;
- }
-
- /**
- * Consolidated collection of {@link IanaLinkRelation}s.
- */
- public static final Set LINK_RELATIONS;
-
- /**
- * Consolidated collection of {@link IanaLinkRelation} values.
- */
- public static final Set RELS;
+ private final Set LINK_RELATIONS;
static {
- LINK_RELATIONS = Arrays.stream(IanaLinkRelation.values())
- .collect(Collectors.toSet());
-
- RELS = LINK_RELATIONS.stream()
- .map(LinkRelation::value)
- .collect(Collectors.toSet());
+ LINK_RELATIONS = Arrays.stream(IanaLinkRelations.class.getDeclaredFields()) //
+ .filter(ReflectionUtils::isPublicStaticFinal) //
+ .map(it -> ReflectionUtils.getField(it, null)) //
+ .map(LinkRelation.class::cast) //
+ .collect(Collectors.toSet());
}
/**
- * Is this relation an IANA standard?
+ * Is this relation an IANA standard? Per RFC8288, parsing of link relations is case insensitive.
*
- * Per RFC8288, parsing of link relations is case insensitive.
- *
* @param rel
* @return boolean
*/
public static boolean isIanaRel(String rel) {
- return
- rel != null
- &&
- LINK_RELATIONS.stream().anyMatch(linkRelation -> linkRelation.value().equalsIgnoreCase(rel));
+ return rel != null && LINK_RELATIONS.stream() //
+ .anyMatch(it -> it.value().equalsIgnoreCase(rel));
}
/**
- * Is this relation an IANA standard?
- *
- * Per RFC8288, parsing of link relations is case insensitive.
+ * Is this relation an IANA standard? Per RFC8288, parsing of link relations is case insensitive.
*
* @param rel
* @return
*/
public static boolean isIanaRel(LinkRelation rel) {
-
- return
- rel != null
- &&
- LINK_RELATIONS.stream().anyMatch(linkRelation -> linkRelation.value.equalsIgnoreCase(rel.value()));
+
+ return rel != null && LINK_RELATIONS.stream() //
+ .anyMatch(it -> it.value().equalsIgnoreCase(rel.value()));
}
/**
- * Convert a string-based link relation to a {@link IanaLinkRelation}.
- *
- * Per RFC8288, parsing of link relations is case insensitive.
+ * Convert a string-based link relation to a {@link IanaLinkRelations}. Per RFC8288, parsing of link relations is case
+ * insensitive.
*
* @param rel as a string
- * @return rel as a {@link IanaLinkRelation}
+ * @return rel as a {@link IanaLinkRelations}
*/
- public static IanaLinkRelation parse(String rel) {
+ public static LinkRelation parse(String rel) {
- return LINK_RELATIONS.stream()
- .filter(linkRelation -> linkRelation.value().equalsIgnoreCase(rel))
- .findFirst()
- .orElseThrow(() -> new IllegalArgumentException(rel + " is not a valid IANA link relation!"));
+ return LINK_RELATIONS.stream() //
+ .filter(it -> it.value().equalsIgnoreCase(rel)) //
+ .findFirst() //
+ .orElseThrow(() -> new IllegalArgumentException(rel + " is not a valid IANA link relation!"));
}
}
diff --git a/src/main/java/org/springframework/hateoas/IanaRels.java b/src/main/java/org/springframework/hateoas/IanaRels.java
index 7f73179a..5599b929 100644
--- a/src/main/java/org/springframework/hateoas/IanaRels.java
+++ b/src/main/java/org/springframework/hateoas/IanaRels.java
@@ -33,10 +33,10 @@ public class IanaRels {
*
* @param rel the relation type to check
* @return
- * @deprecated Migrate to {@link IanaLinkRelation#isIanaRel(String)}.
+ * @deprecated Migrate to {@link IanaLinkRelations#isIanaRel(String)}.
*/
@Deprecated
public static boolean isIanaRel(String rel) {
- return IanaLinkRelation.isIanaRel(rel);
+ return IanaLinkRelations.isIanaRel(rel);
}
}
diff --git a/src/main/java/org/springframework/hateoas/Link.java b/src/main/java/org/springframework/hateoas/Link.java
index c3bdadad..cbac46e1 100755
--- a/src/main/java/org/springframework/hateoas/Link.java
+++ b/src/main/java/org/springframework/hateoas/Link.java
@@ -41,7 +41,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Value object for links.
- *
+ *
* @author Oliver Gierke
* @author Greg Turnquist
* @author Jens Schauder
@@ -50,7 +50,8 @@ import com.fasterxml.jackson.annotation.JsonInclude;
@JsonIgnoreProperties(value = "templated", ignoreUnknown = true)
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
-@EqualsAndHashCode(of = { "rel", "href", "hreflang", "media", "title", "type", "deprecation", "profile", "name", "affordances" })
+@EqualsAndHashCode(
+ of = { "rel", "href", "hreflang", "media", "title", "type", "deprecation", "profile", "name", "affordances" })
public class Link implements Serializable {
private static final long serialVersionUID = -9037755944661782121L;
@@ -63,36 +64,31 @@ public class Link implements Serializable {
public static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
/**
- * @deprecated Use {@link IanaLinkRelation#SELF} instead.
+ * @deprecated Use {@link IanaLinkRelations#SELF} instead.
*/
- @Deprecated
- public static final String REL_SELF = IanaLinkRelation.SELF.value();
+ public static final @Deprecated LinkRelation REL_SELF = IanaLinkRelations.SELF;
/**
- * @deprecated Use {@link IanaLinkRelation#FIRST} instead.
+ * @deprecated Use {@link IanaLinkRelations#FIRST} instead.
*/
- @Deprecated
- public static final String REL_FIRST = IanaLinkRelation.FIRST.value();
+ public static final @Deprecated LinkRelation REL_FIRST = IanaLinkRelations.FIRST;
/**
- * @deprecated Use {@link IanaLinkRelation#PREV} instead.
+ * @deprecated Use {@link IanaLinkRelations#PREV} instead.
*/
- @Deprecated
- public static final String REL_PREVIOUS = IanaLinkRelation.PREV.value();
+ public static final @Deprecated LinkRelation REL_PREVIOUS = IanaLinkRelations.PREV;
/**
- * @deprecated Use {@link IanaLinkRelation#NEXT} instead.
+ * @deprecated Use {@link IanaLinkRelations#NEXT} instead.
*/
- @Deprecated
- public static final String REL_NEXT = IanaLinkRelation.NEXT.value();
+ public static final @Deprecated LinkRelation REL_NEXT = IanaLinkRelations.NEXT;
/**
- * @deprecated Use {@link IanaLinkRelation#LAST} instead.
+ * @deprecated Use {@link IanaLinkRelations#LAST} instead.
*/
- @Deprecated
- public static final String REL_LAST = IanaLinkRelation.LAST.value();
+ public static final @Deprecated LinkRelation REL_LAST = IanaLinkRelations.LAST;
- private @Wither String rel;
+ private LinkRelation rel;
private @Wither String href;
private @Wither String hreflang;
private @Wither String media;
@@ -106,52 +102,74 @@ public class Link implements Serializable {
/**
* Creates a new link to the given URI with the self rel.
- *
- * @see IanaLinkRelation#SELF
+ *
+ * @see IanaLinkRelations#SELF
* @param href must not be {@literal null} or empty.
*/
public Link(String href) {
- this(href, IanaLinkRelation.SELF.value());
+ this(href, IanaLinkRelations.SELF);
}
/**
* Creates a new {@link Link} to the given URI with the given rel.
- *
+ *
* @param href must not be {@literal null} or empty.
* @param rel must not be {@literal null} or empty.
*/
public Link(String href, String rel) {
+ this(new UriTemplate(href), LinkRelation.of(rel));
+ }
+
+ /**
+ * Creates a new {@link Link} to the given URI with the given rel.
+ *
+ * @param href must not be {@literal null} or empty.
+ * @param rel must not be {@literal null} or empty.
+ */
+ public Link(String href, LinkRelation rel) {
this(new UriTemplate(href), rel);
}
/**
* Creates a new Link from the given {@link UriTemplate} and rel.
- *
+ *
* @param template must not be {@literal null}.
* @param rel must not be {@literal null} or empty.
*/
public Link(UriTemplate template, String rel) {
-
- Assert.notNull(template, "UriTemplate must not be null!");
- Assert.hasText(rel, "Rel must not be null or empty!");
-
- this.template = template;
- this.href = template.toString();
- this.rel = rel;
- this.affordances = new ArrayList<>();
+ this(template, LinkRelation.of(rel));
}
- public Link(String href, String rel, List affordances) {
+ /**
+ * Creates a new Link from the given {@link UriTemplate} and rel.
+ *
+ * @param template must not be {@literal null}.
+ * @param rel must not be {@literal null} or empty.
+ */
+ public Link(UriTemplate template, LinkRelation rel) {
+ this(template, rel, Collections.emptyList());
+ }
- this(href, rel);
+ /**
+ * Creates a new Link from the given {@link UriTemplate}, link relation and affordances.
+ *
+ * @param template must not be {@literal null}.
+ * @param rel must not be {@literal null} or empty.
+ */
+ private Link(UriTemplate template, LinkRelation rel, List affordances) {
- Assert.notNull(affordances, "affordances must not be null!");
+ Assert.notNull(template, "UriTemplate must not be null!");
+ Assert.notNull(rel, "LinkRelation must not be null!");
+ Assert.notNull(affordances, "Affordances must not be null!");
+ this.template = template;
+ this.rel = rel;
+ this.href = template.toString();
this.affordances = affordances;
}
/**
- * Empty constructor required by the marshalling framework.
+ * Empty constructor required by the marshaling framework.
*/
protected Link() {
this.affordances = new ArrayList<>();
@@ -159,7 +177,7 @@ public class Link implements Serializable {
/**
* Returns safe copy of {@link Affordance}s.
- *
+ *
* @return
*/
public List getAffordances() {
@@ -168,11 +186,11 @@ public class Link implements Serializable {
/**
* Returns a {@link Link} pointing to the same URI but with the {@code self} relation.
- *
+ *
* @return
*/
public Link withSelfRel() {
- return withRel(IanaLinkRelation.SELF.value());
+ return withRel(IanaLinkRelations.SELF);
}
/**
@@ -194,7 +212,7 @@ public class Link implements Serializable {
/**
* Convenience method when chaining an existing {@link Link}.
- *
+ *
* @param name
* @param httpMethod
* @param inputType
@@ -202,13 +220,14 @@ public class Link implements Serializable {
* @param outputType
* @return
*/
- public Link andAffordance(String name, HttpMethod httpMethod, ResolvableType inputType, List queryMethodParameters, ResolvableType outputType) {
+ public Link andAffordance(String name, HttpMethod httpMethod, ResolvableType inputType,
+ List queryMethodParameters, ResolvableType outputType) {
return andAffordance(new Affordance(name, this, httpMethod, inputType, queryMethodParameters, outputType));
}
/**
- * Convenience method when chaining an existing {@link Link}. Defaults the name of the affordance to verb + classname, e.g.
- * {@literal } produces {@literal }.
+ * Convenience method when chaining an existing {@link Link}. Defaults the name of the affordance to verb + classname,
+ * e.g. {@literal } produces {@literal }.
*
* @param httpMethod
* @param inputType
@@ -216,13 +235,15 @@ public class Link implements Serializable {
* @param outputType
* @return
*/
- public Link andAffordance(HttpMethod httpMethod, ResolvableType inputType, List queryMethodParameters, ResolvableType outputType) {
- return andAffordance(httpMethod.toString().toLowerCase() + inputType.resolve().getSimpleName(), httpMethod, inputType, queryMethodParameters, outputType);
+ public Link andAffordance(HttpMethod httpMethod, ResolvableType inputType, List queryMethodParameters,
+ ResolvableType outputType) {
+ return andAffordance(httpMethod.toString().toLowerCase() + inputType.resolve().getSimpleName(), httpMethod,
+ inputType, queryMethodParameters, outputType);
}
/**
- * Convenience method when chaining an existing {@link Link}. Defaults the name of the affordance to verb + classname, e.g.
- * {@literal } produces {@literal }.
+ * Convenience method when chaining an existing {@link Link}. Defaults the name of the affordance to verb + classname,
+ * e.g. {@literal } produces {@literal }.
*
* @param httpMethod
* @param inputType
@@ -230,13 +251,15 @@ public class Link implements Serializable {
* @param outputType
* @return
*/
- public Link andAffordance(HttpMethod httpMethod, Class> inputType, List queryMethodParameters, Class> outputType) {
- return andAffordance(httpMethod, ResolvableType.forClass(inputType), queryMethodParameters, ResolvableType.forClass(outputType));
+ public Link andAffordance(HttpMethod httpMethod, Class> inputType, List queryMethodParameters,
+ Class> outputType) {
+ return andAffordance(httpMethod, ResolvableType.forClass(inputType), queryMethodParameters,
+ ResolvableType.forClass(outputType));
}
/**
* Create new {@link Link} with additional {@link Affordance}s.
- *
+ *
* @param affordances must not be {@literal null}.
* @return
*/
@@ -251,7 +274,7 @@ public class Link implements Serializable {
/**
* Creats a new {@link Link} with the given {@link Affordance}s.
- *
+ *
* @param affordances must not be {@literal null}.
* @return
*/
@@ -263,7 +286,7 @@ public class Link implements Serializable {
/**
* Returns the variable names contained in the template.
- *
+ *
* @return
*/
@JsonIgnore
@@ -273,7 +296,7 @@ public class Link implements Serializable {
/**
* Returns all {@link TemplateVariables} contained in the {@link Link}.
- *
+ *
* @return
*/
@JsonIgnore
@@ -283,7 +306,7 @@ public class Link implements Serializable {
/**
* Returns whether or not the link is templated.
- *
+ *
* @return
*/
public boolean isTemplated() {
@@ -292,7 +315,7 @@ public class Link implements Serializable {
/**
* Turns the current template into a {@link Link} by expanding it using the given parameters.
- *
+ *
* @param arguments
* @return
*/
@@ -302,7 +325,7 @@ public class Link implements Serializable {
/**
* Turns the current template into a {@link Link} by expanding it using the given parameters.
- *
+ *
* @param arguments must not be {@literal null}.
* @return
*/
@@ -310,9 +333,32 @@ public class Link implements Serializable {
return new Link(getUriTemplate().expand(arguments).toString(), getRel());
}
+ /**
+ * Creates a new {@link Link} with the same href but given {@link LinkRelation}.
+ *
+ * @param relation must not be {@literal null}.
+ * @return
+ */
+ public Link withRel(LinkRelation relation) {
+
+ Assert.notNull(relation, "LinkRelation must not be null!");
+
+ return new Link(relation, href, hreflang, media, title, type, deprecation, profile, name, template, affordances);
+ }
+
+ /**
+ * Creates a new {@link Link} with the same href but given {@link LinkRelation}.
+ *
+ * @param relation must not be {@literal null} or empty.
+ * @return
+ */
+ public Link withRel(String relation) {
+ return withRel(LinkRelation.of(relation));
+ }
+
/**
* Returns whether the current {@link Link} has the given link relation.
- *
+ *
* @param rel must not be {@literal null} or empty.
* @return
*/
@@ -320,7 +366,20 @@ public class Link implements Serializable {
Assert.hasText(rel, "Link relation must not be null or empty!");
- return this.rel.equals(rel);
+ return hasRel(LinkRelation.of(rel));
+ }
+
+ /**
+ * Returns whether the {@link Link} has the given {@link LinkRelation}.
+ *
+ * @param rel must not be {@literal null}.
+ * @return
+ */
+ public boolean hasRel(LinkRelation rel) {
+
+ Assert.notNull(rel, "Link relation must not be null!");
+
+ return this.rel.isSameAs(rel);
}
private UriTemplate getUriTemplate() {
@@ -339,7 +398,7 @@ public class Link implements Serializable {
@Override
public String toString() {
- String linkString = String.format("<%s>;rel=\"%s\"", href, rel);
+ String linkString = String.format("<%s>;rel=\"%s\"", href, rel.value());
if (hreflang != null) {
linkString += ";hreflang=\"" + hreflang + "\"";
@@ -375,7 +434,7 @@ public class Link implements Serializable {
/**
* Factory method to easily create {@link Link} instances from RFC-5988 compatible {@link String} representations of a
* link. Will return {@literal null} if input {@link String} is either empty or {@literal null}.
- *
+ *
* @param element an RFC-5899 compatible representation of a link.
* @throws IllegalArgumentException if a non-empty {@link String} was given that does not adhere to RFC-5899.
* @throws IllegalArgumentException if no {@code rel} attribute could be found.
@@ -436,7 +495,7 @@ public class Link implements Serializable {
/**
* Parses the links attributes from the given source {@link String}.
- *
+ *
* @param source
* @return
*/
diff --git a/src/main/java/org/springframework/hateoas/LinkBuilder.java b/src/main/java/org/springframework/hateoas/LinkBuilder.java
index f1431ce2..94f86548 100644
--- a/src/main/java/org/springframework/hateoas/LinkBuilder.java
+++ b/src/main/java/org/springframework/hateoas/LinkBuilder.java
@@ -19,7 +19,7 @@ import java.net.URI;
/**
* Builder to ease building {@link Link} instances.
- *
+ *
* @author Ricardo Gladwell
*/
public interface LinkBuilder {
@@ -27,7 +27,7 @@ public interface LinkBuilder {
/**
* Adds the given object's {@link String} representation as sub-resource to the current URI. Will unwrap
* {@link Identifiable}s to their id value (see {@link Identifiable#getId()}).
- *
+ *
* @param object
* @return
*/
@@ -36,7 +36,7 @@ public interface LinkBuilder {
/**
* Adds the given {@link Identifiable}'s id as sub-resource. Will simply return the {@link LinkBuilder} as is if the
* given entity is {@literal null}.
- *
+ *
* @param identifiable
* @return
*/
@@ -44,23 +44,33 @@ public interface LinkBuilder {
/**
* Creates a URI of the link built by the current builder instance.
- *
+ *
* @return
*/
URI toUri();
/**
- * Creates the {@link Link} built by the current builder instance with the given rel.
- *
+ * Creates the {@link Link} built by the current builder instance with the given link relation.
+ *
+ * @param rel must not be {@literal null}.
+ * @return
+ */
+ default Link withRel(String rel) {
+ return withRel(LinkRelation.of(rel));
+ }
+
+ /**
+ * Creates the {@link Link} built by the current builder instance with the given {@link LinkRelation}.
+ *
* @param rel must not be {@literal null} or empty.
* @return
*/
- Link withRel(String rel);
+ Link withRel(LinkRelation rel);
/**
- * Creates the {@link Link} built by the current builder instance with the default self rel.
- *
- * @see IanaLinkRelation#SELF
+ * Creates the {@link Link} built by the current builder instance with the default self link relation.
+ *
+ * @see IanaLinkRelations#SELF
* @return
*/
Link withSelfRel();
diff --git a/src/main/java/org/springframework/hateoas/LinkDiscoverer.java b/src/main/java/org/springframework/hateoas/LinkDiscoverer.java
index 81f9c612..d552cd52 100644
--- a/src/main/java/org/springframework/hateoas/LinkDiscoverer.java
+++ b/src/main/java/org/springframework/hateoas/LinkDiscoverer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 the original author or authors.
+ * Copyright 2012-2019 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.
@@ -16,51 +16,88 @@
package org.springframework.hateoas;
import java.io.InputStream;
-import java.util.List;
+import java.util.Optional;
import org.springframework.http.MediaType;
import org.springframework.plugin.core.Plugin;
/**
* Interface to allow discovering links by relation type from some source.
- *
+ *
* @author Oliver Gierke
*/
public interface LinkDiscoverer extends Plugin {
/**
* Finds a single link with the given relation type in the given {@link String} representation.
- *
+ *
* @param rel must not be {@literal null} or empty.
- * @param representation must not be {@literal null} or empty.
+ * @param representation must not be {@literal null}.
* @return the first link with the given relation type found, or {@literal null} if none was found.
*/
- Link findLinkWithRel(String rel, String representation);
+ default Optional findLinkWithRel(String rel, String representation) {
+ return findLinkWithRel(LinkRelation.of(rel), representation);
+ }
+
+ Optional findLinkWithRel(LinkRelation rel, String representation);
/**
* Finds a single link with the given relation type in the given {@link InputStream} representation.
- *
+ *
* @param rel must not be {@literal null} or empty.
- * @param representation must not be {@literal null} or empty.
+ * @param representation must not be {@literal null}.
* @return the first link with the given relation type found, or {@literal null} if none was found.
*/
- Link findLinkWithRel(String rel, InputStream representation);
+ default Optional findLinkWithRel(String rel, InputStream representation) {
+ return findLinkWithRel(LinkRelation.of(rel), representation);
+ }
/**
- * Returns all links with the given relation type found in the given {@link String} representation.
- *
- * @param rel must not be {@literal null} or empty.
- * @param representation must not be {@literal null} or empty.
- * @return
+ * Finds a single link with the given {@link LinkRelation} in the given {@link InputStream} representation.
+ *
+ * @param rel must not be {@literal null}.
+ * @param representation must not be {@literal null}.
+ * @return the first link with the given relation type found, or {@literal null} if none was found.
*/
- List findLinksWithRel(String rel, String representation);
+ Optional findLinkWithRel(LinkRelation rel, InputStream representation);
/**
- * Returns all links with the given relation type found in the given {@link InputStream} representation.
- *
+ * Returns all links with the given link relation found in the given {@link String} representation.
+ *
* @param rel must not be {@literal null} or empty.
- * @param representation must not be {@literal null} or empty.
- * @return
+ * @param representation must not be {@literal null}.
+ * @return will never be {@literal null}.
*/
- List findLinksWithRel(String rel, InputStream representation);
+ default Links findLinksWithRel(String rel, String representation) {
+ return findLinksWithRel(LinkRelation.of(rel), representation);
+ }
+
+ /**
+ * Returns all links with the given {@link LinkRelation} found in the given {@link String} representation.
+ *
+ * @param rel must not be {@literal null}.
+ * @param representation must not be {@literal null}.
+ * @return will never be {@literal null}.
+ */
+ Links findLinksWithRel(LinkRelation rel, String representation);
+
+ /**
+ * Returns all links with the given link relation found in the given {@link InputStream} representation.
+ *
+ * @param rel must not be {@literal null} or empty.
+ * @param representation must not be {@literal null}.
+ * @return will never be {@literal null}.
+ */
+ default Links findLinksWithRel(String rel, InputStream representation) {
+ return findLinksWithRel(LinkRelation.of(rel), representation);
+ }
+
+ /**
+ * Returns all links with the given {@link LinkRelation} found in the given {@link InputStream} representation.
+ *
+ * @param rel must not be {@literal null}.
+ * @param representation must not be {@literal null}.
+ * @return will never be {@literal null}.
+ */
+ Links findLinksWithRel(LinkRelation rel, InputStream representation);
}
diff --git a/src/main/java/org/springframework/hateoas/LinkDiscoverers.java b/src/main/java/org/springframework/hateoas/LinkDiscoverers.java
index 810e1de9..548fd040 100644
--- a/src/main/java/org/springframework/hateoas/LinkDiscoverers.java
+++ b/src/main/java/org/springframework/hateoas/LinkDiscoverers.java
@@ -15,6 +15,8 @@
*/
package org.springframework.hateoas;
+import java.util.Optional;
+
import org.springframework.http.MediaType;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.util.Assert;
@@ -22,7 +24,7 @@ import org.springframework.util.Assert;
/**
* Value object to wrap a {@link PluginRegistry} for {@link LinkDiscoverer} so that it's easier to inject them into
* clients wanting to lookup a {@link LinkDiscoverer} for a given {@link MediaTypes}.
- *
+ *
* @author Oliver Gierke
*/
public class LinkDiscoverers {
@@ -31,7 +33,7 @@ public class LinkDiscoverers {
/**
* Creates a new {@link LinkDiscoverers} instance with the given {@link PluginRegistry}.
- *
+ *
* @param discoverers must not be {@literal null}.
*/
public LinkDiscoverers(PluginRegistry discoverers) {
@@ -42,21 +44,41 @@ public class LinkDiscoverers {
/**
* Returns the {@link LinkDiscoverer} suitable for the given {@link MediaType}.
- *
+ *
+ * @param mediaType
+ * @return will never be {@literal null}.
+ */
+ public Optional getLinkDiscovererFor(MediaType mediaType) {
+ return discoverers.getPluginFor(mediaType);
+ }
+
+ /**
+ * Returns the {@link LinkDiscoverer} suitable for the given media type.
+ *
* @param mediaType
* @return
*/
- public LinkDiscoverer getLinkDiscovererFor(MediaType mediaType) {
+ public Optional getLinkDiscovererFor(String mediaType) {
+ return getLinkDiscovererFor(MediaType.valueOf(mediaType));
+ }
+
+ /**
+ * Returns the {@link LinkDiscoverer} suitable for the given {@link MediaType}.
+ *
+ * @param mediaType
+ * @return will never be {@literal null}.
+ */
+ public LinkDiscoverer getRequiredLinkDiscovererFor(MediaType mediaType) {
return discoverers.getRequiredPluginFor(mediaType);
}
/**
* Returns the {@link LinkDiscoverer} suitable for the given media type.
- *
+ *
* @param mediaType
* @return
*/
- public LinkDiscoverer getLinkDiscovererFor(String mediaType) {
- return getLinkDiscovererFor(MediaType.valueOf(mediaType));
+ public LinkDiscoverer getRequiredLinkDiscovererFor(String mediaType) {
+ return getRequiredLinkDiscovererFor(MediaType.valueOf(mediaType));
}
}
diff --git a/src/main/java/org/springframework/hateoas/LinkRelation.java b/src/main/java/org/springframework/hateoas/LinkRelation.java
index bd794671..d2d0df49 100644
--- a/src/main/java/org/springframework/hateoas/LinkRelation.java
+++ b/src/main/java/org/springframework/hateoas/LinkRelation.java
@@ -15,10 +15,19 @@
*/
package org.springframework.hateoas;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import org.springframework.util.Assert;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
/**
* Interface for defining link relations. Can be used for implementing spec-based link relations as well as custom ones.
*
* @author Greg Turnquist
+ * @author Oliver Drotbohm
* @since 1.0
*/
public interface LinkRelation {
@@ -26,5 +35,44 @@ public interface LinkRelation {
/**
* Return the link relation's value.
*/
+ @JsonValue
String value();
+
+ /**
+ * Creates a new {@link LinkRelation}.
+ *
+ * @param relation must not be {@literal null} or empty.
+ * @return
+ */
+ @JsonCreator
+ static LinkRelation of(String relation) {
+ return StringLinkRelation.of(relation);
+ }
+
+ /**
+ * Creates a new {@link Iterable} of {@link LinkRelation} for each of the given {@link String}s.
+ *
+ * @param others must not be {@literal null}.
+ * @return
+ */
+ static Iterable manyOf(String... others) {
+
+ return Arrays.stream(others) //
+ .map(LinkRelation::of) //
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns whether the given {@link LinkRelation} is logically the same as the current one, independent of
+ * implementation, i.e. whether the plain {@link String} values match.
+ *
+ * @param relation must not be {@literal null}.
+ * @return
+ */
+ default boolean isSameAs(LinkRelation relation) {
+
+ Assert.notNull(relation, "LinkRelation must not be null!");
+
+ return this.value().equals(relation.value());
+ }
}
diff --git a/src/main/java/org/springframework/hateoas/Links.java b/src/main/java/org/springframework/hateoas/Links.java
index fc2874ff..4b05d326 100644
--- a/src/main/java/org/springframework/hateoas/Links.java
+++ b/src/main/java/org/springframework/hateoas/Links.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2017 the original author or authors.
+ * Copyright 2013-2019 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.
@@ -21,12 +21,19 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collector;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
+import com.fasterxml.jackson.annotation.JsonValue;
+
/**
* Value object to represent a list of {@link Link}s.
*
@@ -35,75 +42,51 @@ import org.springframework.util.StringUtils;
*/
public class Links implements Iterable {
+ public static final Links NONE = new Links(Collections.emptyList());
private static final Pattern LINK_HEADER_PATTERN = Pattern.compile("(<[^>]*>(;\\s*\\w+=\"[^\"]*\")+)");
- static final Links NO_LINKS = new Links(Collections.emptyList());
-
private final List links;
- /**
- * Creates a new {@link Links} instance from the given {@link Link}s.
- *
- * @param links
- */
- public Links(List links) {
- this.links = links == null ? Collections.emptyList() : Collections.unmodifiableList(links);
+ private Links(Iterable links) {
+
+ Assert.notNull(links, "Links must not be null!");
+
+ this.links = StreamSupport.stream(links.spliterator(), false) //
+ .collect(Collectors.toList());
}
- /**
- * Creates a new {@link Links} instance from the given {@link Link}s.
- *
- * @param links
- */
- public Links(Link... links) {
+ private Links(Link... links) {
this(Arrays.asList(links));
}
/**
- * Returns the {@link Link} with the given rel.
+ * Creates a new {@link Links} instance from the given {@link Link}s.
*
- * @param rel the relation type to lookup a link for.
- * @return the link with the given rel or {@literal Optional#empty()} if none found.
+ * @param links
*/
- public Optional getLink(String rel) {
-
- return links.stream() //
- .filter(link -> link.getRel().equals(rel)).findFirst();
+ public static Links of(Link... links) {
+ return new Links(links);
}
/**
- * Returns the {@link Link} with the given relation.
+ * Creates a new {@link Links} instance from the given {@link Link}s.
*
- * @param rel the relation type to lookup a link for.
- * @return
- * @throws IllegalArgumentException if no link with the given relation was present.
- * @since 1.0
+ * @param links
*/
- public Link getRequiredLink(String rel) {
-
- return getLink(rel) //
- .orElseThrow(() -> new IllegalArgumentException(String.format("Couldn't find link with rel '%s'!", rel)));
+ public static Links of(Iterable links) {
+ return new Links(links);
}
/**
- * Returns all {@link Links} with the given relation type.
+ * Creates a {@link Links} instance from the given RFC5988-compatible link format.
*
- * @return the links
+ * @param source a comma separated list of {@link Link} representations.
+ * @return the {@link Links} represented by the given {@link String}.
+ * @deprecated use {@link #parse(String)} instead
*/
- public List getLinks(String rel) {
-
- return links.stream() //
- .filter(link -> link.getRel().endsWith(rel)).collect(Collectors.toList());
- }
-
- /**
- * Returns whether the {@link Links} container contains a {@link Link} with the given rel.
- *
- * @param rel
- * @return
- */
- public boolean hasLink(String rel) {
- return getLink(rel).isPresent();
+ @Deprecated
+ public static Links valueOf(String source) {
+ return parse(source);
}
/**
@@ -112,10 +95,10 @@ public class Links implements Iterable {
* @param source a comma separated list of {@link Link} representations.
* @return the {@link Links} represented by the given {@link String}.
*/
- public static Links valueOf(String source) {
+ public static Links parse(String source) {
if (!StringUtils.hasText(source)) {
- return NO_LINKS;
+ return NONE;
}
Matcher matcher = LINK_HEADER_PATTERN.matcher(source);
@@ -133,6 +116,194 @@ public class Links implements Iterable {
return new Links(links);
}
+ /**
+ * Creates a new {@link Links} instance with all given {@link Link}s added. For conditional adding see
+ * {@link #merge(Link...)}.
+ *
+ * @param links must not be {@literal null}.
+ * @return
+ * @see #merge(Link...)
+ * @see #merge(MergeMode, Link...)
+ */
+ public Links and(Link... links) {
+
+ Assert.notNull(links, "Links must not be null!");
+
+ return and(Arrays.asList(links));
+ }
+
+ /**
+ * 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(Iterable links) {
+
+ List newLinks = new ArrayList<>(this.links);
+ links.forEach(newLinks::add);
+
+ return Links.of(newLinks);
+ }
+
+ /**
+ * 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...)}.
+ *
+ * @param links the {@link Link}s to be merged, must not be {@literal null}.
+ * @return
+ * @see MergeMode#SKIP_BY_EQUALITY
+ * @see #and(Link...)
+ */
+ public Links merge(Link... links) {
+ return merge(Arrays.asList(links));
+ }
+
+ /**
+ * 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...)}.
+ *
+ * @param links the {@link Link}s to be merged, must not be {@literal null}.
+ * @return
+ * @see MergeMode#SKIP_BY_EQUALITY
+ * @see #and(Link...)
+ */
+ public Links merge(Iterable links) {
+ return merge(MergeMode.SKIP_BY_EQUALITY, 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, Link... links) {
+ 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, Iterable links) {
+
+ Assert.notNull(mode, "MergeMode must not be null!");
+ Assert.notNull(links, "Links must not be null!");
+
+ List newLinks = MergeMode.REPLACE_BY_REL.equals(mode) //
+ ? allWithoutRels(links)
+ : new ArrayList<>(this.links);
+
+ links.forEach(it -> {
+
+ if (MergeMode.REPLACE_BY_REL.equals(mode)) {
+ newLinks.add(it);
+ }
+
+ if (MergeMode.SKIP_BY_EQUALITY.equals(mode) && !this.links.contains(it)) {
+ newLinks.add(it);
+ }
+
+ if (MergeMode.SKIP_BY_REL.equals(mode) && !this.hasLink(it.getRel())) {
+ newLinks.add(it);
+ }
+ });
+
+ return new Links(newLinks);
+ }
+
+ /**
+ * Returns a {@link Links} with all {@link Link}s with the given {@link LinkRelation} removed.
+ *
+ * @param relation must not be {@literal null}.
+ * @return
+ */
+ public Links without(LinkRelation relation) {
+
+ Assert.notNull(relation, "LinkRelation must not be null!");
+
+ return this.links.stream() //
+ .filter(it -> !it.hasRel(relation)) //
+ .collect(Links.collector());
+ }
+
+ /**
+ * Returns a {@link Link} with the given relation if contained in the current {@link Links} instance,
+ * {@link Optional#empty()} otherwise.
+ *
+ * @param relation must not be {@literal null} or empty.
+ * @return
+ */
+ public Optional getLink(String relation) {
+ return getLink(LinkRelation.of(relation));
+ }
+
+ /**
+ * Returns the {@link Link} with the given rel.
+ *
+ * @param rel the relation type to lookup a link for.
+ * @return the link with the given rel or {@literal Optional#empty()} if none found.
+ */
+ public Optional getLink(LinkRelation rel) {
+
+ return links.stream() //
+ .filter(it -> it.hasRel(rel)) //
+ .findFirst();
+ }
+
+ /**
+ * Returns the {@link Link} with the given relation.
+ *
+ * @param rel the relation type to lookup a link for.
+ * @return
+ * @throws IllegalArgumentException if no link with the given relation was present.
+ * @since 1.0
+ */
+ public Link getRequiredLink(String rel) {
+ return getRequiredLink(LinkRelation.of(rel));
+ }
+
+ /**
+ * Returns the {@link Link} with the given relation.
+ *
+ * @param relation the relation type to lookup a link for.
+ * @return
+ * @throws IllegalArgumentException if no link with the given relation was present.
+ */
+ public Link getRequiredLink(LinkRelation relation) {
+
+ return getLink(relation) //
+ .orElseThrow(() -> new IllegalArgumentException(String.format("Couldn't find link with rel '%s'!", relation)));
+ }
+
+ /**
+ * Returns whether the {@link Links} container contains a {@link Link} with the given relation.
+ *
+ * @param relation must not be {@literal null} or empty.
+ * @return
+ */
+ public boolean hasLink(String relation) {
+ return getLink(relation).isPresent();
+ }
+
+ /**
+ * Returns whether the current {@link Links} contains a {@link Link} with the given relation.
+ *
+ * @param relation must not be {@literal null}.
+ * @return
+ */
+ public boolean hasLink(LinkRelation relation) {
+ return getLink(relation).isPresent();
+ }
+
/**
* Returns whether the {@link Links} container is empty.
*
@@ -142,6 +313,86 @@ public class Links implements Iterable {
return links.isEmpty();
}
+ /**
+ * Returns whether the current {@link Links} has the given size.
+ *
+ * @param size
+ * @return
+ */
+ public boolean hasSize(long size) {
+ return links.size() == size;
+ }
+
+ /**
+ * Returns whether the {@link Links} contain a single {@link Link}.
+ *
+ * @return
+ */
+ public boolean hasSingleLink() {
+ return hasSize(1);
+ }
+
+ /**
+ * Creates a {@link Stream} of the current {@link Links}.
+ *
+ * @return
+ */
+ public Stream stream() {
+ return this.links.stream();
+ }
+
+ /**
+ * Returns the current {@link Links} as {@link List}.
+ *
+ * @return
+ */
+ @JsonValue
+ public List toList() {
+ return this.links;
+ }
+
+ /**
+ * Returns whether the current {@link Links} contain all given {@link Link}s (but potentially others).
+ *
+ * @param links must not be {@literal null}.
+ * @return
+ */
+ public boolean contains(Link... links) {
+ return this.links.containsAll(Links.of(links).toList());
+ }
+
+ /**
+ * Returns whether the current {@link Links} contain all given {@link Link}s (but potentially others).
+ *
+ * @param links must not be {@literal null}.
+ * @return
+ */
+ public boolean contains(Iterable links) {
+ return this.links.containsAll(Links.of(links).toList());
+ }
+
+ /**
+ * Returns whether the current {@link Links} instance contains exactly the same {@link Link} as the given one.
+ *
+ * @param links must not be {@literal null}.
+ * @return
+ */
+ public boolean containsSameLinksAs(Iterable links) {
+
+ Links other = Links.of(links);
+
+ return this.links.size() != other.links.size() ? false : this.links.containsAll(other.links);
+ }
+
+ /**
+ * Creates a new {@link Collector} to collect a {@link Stream} of {@link Link}s into a {@link Links} instance.
+ *
+ * @return will never be {@literal null}.
+ */
+ public static Collector collector() {
+ return Collectors.collectingAndThen(Collectors.toList(), Links::of);
+ }
+
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
@@ -188,4 +439,38 @@ public class Links implements Iterable {
return result;
}
+
+ private List allWithoutRels(Iterable links) {
+
+ Set toFilter = StreamSupport.stream(links.spliterator(), false) //
+ .map(Link::getRel) //
+ .collect(Collectors.toSet());
+
+ return this.links.stream() //
+ .filter(it -> !toFilter.contains(it.getRel())) //
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * The mode how to merge two {@link Links} instances.
+ *
+ * @author Oliver Drotbohm
+ */
+ public static enum MergeMode {
+
+ /**
+ * Skips to add the same links on merge. Multiple links with the same link relation might appear.
+ */
+ SKIP_BY_EQUALITY,
+
+ /**
+ * Skips to add links with the same link relation, i.e. existing ones with the same relation are preferred.
+ */
+ SKIP_BY_REL,
+
+ /**
+ * Replaces existing links with the same link relation.
+ */
+ REPLACE_BY_REL;
+ }
}
diff --git a/src/main/java/org/springframework/hateoas/PagedResources.java b/src/main/java/org/springframework/hateoas/PagedResources.java
index 7062f11f..7f8553ea 100644
--- a/src/main/java/org/springframework/hateoas/PagedResources.java
+++ b/src/main/java/org/springframework/hateoas/PagedResources.java
@@ -27,7 +27,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/**
* DTO to implement binding response representations of pageable collections.
- *
+ *
* @author Oliver Gierke
* @author Greg Turnquist
*/
@@ -46,7 +46,7 @@ public class PagedResources extends Resources {
/**
* Creates a new {@link PagedResources} from the given content, {@link PageMetadata} and {@link Link}s (optional).
- *
+ *
* @param content must not be {@literal null}.
* @param metadata
* @param links
@@ -57,7 +57,7 @@ public class PagedResources extends Resources {
/**
* Creates a new {@link PagedResources} from the given content {@link PageMetadata} and {@link Link}s.
- *
+ *
* @param content must not be {@literal null}.
* @param metadata
* @param links
@@ -69,7 +69,7 @@ public class PagedResources extends Resources {
/**
* Returns the pagination metadata.
- *
+ *
* @return the metadata
*/
@JsonProperty("page")
@@ -79,7 +79,7 @@ public class PagedResources extends Resources {
/**
* Factory method to easily create a {@link PagedResources} instance from a set of entities and pagination metadata.
- *
+ *
* @param content must not be {@literal null}.
* @param metadata
* @return
@@ -99,25 +99,25 @@ public class PagedResources extends Resources {
/**
* Returns the Link pointing to the next page (if set).
- *
+ *
* @return
*/
@JsonIgnore
public Optional getNextLink() {
- return getLink(IanaLinkRelation.NEXT.value());
+ return getLink(IanaLinkRelations.NEXT);
}
/**
* Returns the Link pointing to the previous page (if set).
- *
+ *
* @return
*/
@JsonIgnore
public Optional getPreviousLink() {
- return getLink(IanaLinkRelation.PREV.value());
+ return getLink(IanaLinkRelations.PREV);
}
- /*
+ /*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceSupport#toString()
*/
@@ -126,7 +126,7 @@ public class PagedResources extends Resources {
return String.format("PagedResource { content: %s, metadata: %s, links: %s }", getContent(), metadata, getLinks());
}
- /*
+ /*
* (non-Javadoc)
* @see org.springframework.hateoas.Resources#equals(java.lang.Object)
*/
@@ -147,7 +147,7 @@ public class PagedResources extends Resources {
return metadataEquals && super.equals(obj);
}
- /*
+ /*
* (non-Javadoc)
* @see org.springframework.hateoas.Resources#hashCode()
*/
@@ -161,7 +161,7 @@ public class PagedResources extends Resources {
/**
* Value object for pagination metadata.
- *
+ *
* @author Oliver Gierke
*/
public static class PageMetadata {
@@ -177,7 +177,7 @@ public class PagedResources extends Resources {
/**
* Creates a new {@link PageMetadata} from the given size, number, total elements and total pages.
- *
+ *
* @param size
* @param number zero-indexed, must be less than totalPages
* @param totalElements
@@ -198,7 +198,7 @@ public class PagedResources extends Resources {
/**
* Creates a new {@link PageMetadata} from the given size, number and total elements.
- *
+ *
* @param size the size of the page
* @param number the number of the page
* @param totalElements the total number of elements available
@@ -209,7 +209,7 @@ public class PagedResources extends Resources {
/**
* Returns the requested size of the page.
- *
+ *
* @return the size a positive long.
*/
public long getSize() {
@@ -218,7 +218,7 @@ public class PagedResources extends Resources {
/**
* Returns the total number of elements available.
- *
+ *
* @return the totalElements a positive long.
*/
public long getTotalElements() {
@@ -227,7 +227,7 @@ public class PagedResources extends Resources {
/**
* Returns how many pages are available in total.
- *
+ *
* @return the totalPages a positive long.
*/
public long getTotalPages() {
@@ -236,14 +236,14 @@ public class PagedResources extends Resources {
/**
* Returns the number of the current page.
- *
+ *
* @return the number a positive long.
*/
public long getNumber() {
return number;
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -253,7 +253,7 @@ public class PagedResources extends Resources {
totalElements, size);
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@@ -274,7 +274,7 @@ public class PagedResources extends Resources {
&& this.totalPages == that.totalPages;
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
diff --git a/src/main/java/org/springframework/hateoas/RelProvider.java b/src/main/java/org/springframework/hateoas/RelProvider.java
index 56734a7a..523cfde9 100644
--- a/src/main/java/org/springframework/hateoas/RelProvider.java
+++ b/src/main/java/org/springframework/hateoas/RelProvider.java
@@ -19,24 +19,24 @@ import org.springframework.plugin.core.Plugin;
/**
* API to provide relation types for collections and items of the given type.
- *
+ *
* @author Oliver Gierke
*/
public interface RelProvider extends Plugin> {
/**
* Returns the relation type to be used to point to an item resource of the given type.
- *
+ *
* @param type must not be {@literal null}.
* @return
*/
- String getItemResourceRelFor(Class> type);
+ LinkRelation getItemResourceRelFor(Class> type);
/**
* Returns the relation type to be used to point to a collection resource of the given type.
- *
+ *
* @param type must not be {@literal null}.
* @return
*/
- String getCollectionResourceRelFor(Class> type);
+ LinkRelation getCollectionResourceRelFor(Class> type);
}
diff --git a/src/main/java/org/springframework/hateoas/ResourceSupport.java b/src/main/java/org/springframework/hateoas/ResourceSupport.java
index c11659b1..7f5a1631 100755
--- a/src/main/java/org/springframework/hateoas/ResourceSupport.java
+++ b/src/main/java/org/springframework/hateoas/ResourceSupport.java
@@ -28,7 +28,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Base class for DTOs to collect links.
- *
+ *
* @author Oliver Gierke
* @author Johhny Lim
* @author Greg Turnquist
@@ -42,16 +42,16 @@ public class ResourceSupport implements Identifiable {
}
/**
- * Returns the {@link Link} with a rel of {@link IanaLinkRelation#SELF}.
+ * Returns the {@link Link} with a rel of {@link IanaLinkRelations#SELF}.
*/
@JsonIgnore
public Optional getId() {
- return getLink(IanaLinkRelation.SELF.value());
+ return getLink(IanaLinkRelations.SELF);
}
/**
* Adds the given link to the resource.
- *
+ *
* @param link
*/
public void add(Link link) {
@@ -63,7 +63,7 @@ public class ResourceSupport implements Identifiable {
/**
* Adds all given {@link Link}s to the resource.
- *
+ *
* @param links
*/
public void add(Iterable links) {
@@ -87,7 +87,7 @@ public class ResourceSupport implements Identifiable {
/**
* Returns whether the resource contains {@link Link}s at all.
- *
+ *
* @return
*/
public boolean hasLinks() {
@@ -96,7 +96,7 @@ public class ResourceSupport implements Identifiable {
/**
* Returns whether the resource contains a {@link Link} with the given rel.
- *
+ *
* @param rel
* @return
*/
@@ -104,14 +104,18 @@ public class ResourceSupport implements Identifiable {
return getLink(rel).isPresent();
}
+ public boolean hasLink(LinkRelation rel) {
+ return hasLink(rel.value());
+ }
+
/**
* Returns all {@link Link}s contained in this resource.
- *
+ *
* @return
*/
@JsonProperty("links")
- public List getLinks() {
- return links;
+ public Links getLinks() {
+ return Links.of(links);
}
/**
@@ -123,20 +127,24 @@ public class ResourceSupport implements Identifiable {
/**
* Returns the link with the given rel.
- *
+ *
* @param rel
* @return the link with the given rel or {@link Optional#empty()} if none found.
*/
public Optional getLink(String rel) {
+ return getLink(LinkRelation.of(rel));
+ }
+
+ public Optional getLink(LinkRelation rel) {
return links.stream() //
- .filter(link -> link.hasRel(rel)) //
+ .filter(it -> it.hasRel(rel)) //
.findFirst();
}
/**
* 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.
@@ -147,6 +155,10 @@ public class ResourceSupport implements Identifiable {
.orElseThrow(() -> new IllegalArgumentException(String.format("No link with rel %s found!", rel)));
}
+ public Link getRequiredLink(LinkRelation rel) {
+ return getRequiredLink(rel.value());
+ }
+
/**
* Returns all {@link Link}s with the given relation type.
*
@@ -159,7 +171,11 @@ public class ResourceSupport implements Identifiable {
.collect(Collectors.toList());
}
- /*
+ public List getLinks(LinkRelation rel) {
+ return getLinks(rel.value());
+ }
+
+ /*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -168,7 +184,7 @@ public class ResourceSupport implements Identifiable {
return String.format("links: %s", links.toString());
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@@ -188,7 +204,7 @@ public class ResourceSupport implements Identifiable {
return this.links.equals(that.links);
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
diff --git a/src/main/java/org/springframework/hateoas/Resources.java b/src/main/java/org/springframework/hateoas/Resources.java
index 9b9882d6..61474c5d 100644
--- a/src/main/java/org/springframework/hateoas/Resources.java
+++ b/src/main/java/org/springframework/hateoas/Resources.java
@@ -27,7 +27,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/**
* General helper to easily create a wrapper for a collection of entities.
- *
+ *
* @author Oliver Gierke
* @author Greg Turnquist
*/
@@ -44,7 +44,7 @@ public class Resources extends ResourceSupport implements Iterable {
/**
* Creates a {@link Resources} instance with the given content and {@link Link}s (optional).
- *
+ *
* @param content must not be {@literal null}.
* @param links the links to be added to the {@link Resources}.
*/
@@ -54,7 +54,7 @@ public class Resources extends ResourceSupport implements Iterable {
/**
* Creates a {@link Resources} instance with the given content and {@link Link}s.
- *
+ *
* @param content must not be {@literal null}.
* @param links the links to be added to the {@link Resources}.
*/
@@ -72,7 +72,7 @@ public class Resources extends ResourceSupport implements Iterable {
/**
* Creates a new {@link Resources} instance by wrapping the given domain class instances into a {@link Resource}.
- *
+ *
* @param content must not be {@literal null}.
* @return
*/
@@ -80,6 +80,7 @@ public class Resources extends ResourceSupport implements Iterable {
public static , S> Resources wrap(Iterable content) {
Assert.notNull(content, "Content must not be null!");
+
ArrayList resources = new ArrayList<>();
for (S element : content) {
@@ -91,7 +92,7 @@ public class Resources extends ResourceSupport implements Iterable {
/**
* Returns the underlying elements.
- *
+ *
* @return the content will never be {@literal null}.
*/
@JsonProperty("content")
@@ -99,7 +100,7 @@ public class Resources extends ResourceSupport implements Iterable {
return Collections.unmodifiableCollection(content);
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@@ -117,7 +118,7 @@ public class Resources extends ResourceSupport implements Iterable {
return String.format("Resources { content: %s, %s }", getContent(), super.toString());
}
- /*
+ /*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceSupport#equals(java.lang.Object)
*/
@@ -138,7 +139,7 @@ public class Resources extends ResourceSupport implements Iterable {
return contentEqual && super.equals(obj);
}
- /*
+ /*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceSupport#hashCode()
*/
diff --git a/src/main/java/org/springframework/hateoas/StringLinkRelation.java b/src/main/java/org/springframework/hateoas/StringLinkRelation.java
new file mode 100644
index 00000000..34c16f4f
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/StringLinkRelation.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.hateoas;
+
+import lombok.AccessLevel;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Value;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.util.Assert;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Simple value type for a {@link String} based {@link LinkRelation}.
+ *
+ * @author Oliver Drotbohm
+ */
+@Value
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+class StringLinkRelation implements LinkRelation, Serializable {
+
+ private static final long serialVersionUID = -3904935345545567957L;
+ private static final Map CACHE = new ConcurrentHashMap(256);
+
+ @NonNull String relation;
+
+ /**
+ * Returns a (potentially cached) {@link LinkRelation} for the given value.
+ *
+ * @param relation must not be {@literal null} or empty.
+ * @return
+ */
+ @JsonCreator
+ public static StringLinkRelation of(String relation) {
+
+ Assert.hasText(relation, "Relation must not be null or empty!");
+
+ return CACHE.computeIfAbsent(relation, it -> new StringLinkRelation(it));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.LinkRelation#value()
+ */
+ @JsonValue
+ @Override
+ public String value() {
+ return relation;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return value();
+ }
+}
diff --git a/src/main/java/org/springframework/hateoas/UriTemplate.java b/src/main/java/org/springframework/hateoas/UriTemplate.java
index b7b02052..93298199 100644
--- a/src/main/java/org/springframework/hateoas/UriTemplate.java
+++ b/src/main/java/org/springframework/hateoas/UriTemplate.java
@@ -22,8 +22,6 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -36,7 +34,7 @@ import org.springframework.web.util.UriComponentsBuilder;
/**
* Custom URI template to support qualified URI template variables.
- *
+ *
* @author Oliver Gierke
* @author JamesE Richardson
* @see http://tools.ietf.org/html/rfc6570
@@ -52,7 +50,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Creates a new {@link UriTemplate} using the given template string.
- *
+ *
* @param template must not be {@literal null} or empty.
*/
public UriTemplate(String template) {
@@ -71,7 +69,7 @@ public class UriTemplate implements Iterable, Serializable {
String[] names = matcher.group(2).split(",");
for (String name : names) {
-
+
TemplateVariable variable;
if (name.endsWith(VariableType.COMPOSITE_PARAM.toString())) {
@@ -94,7 +92,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Creates a new {@link UriTemplate} from the given base URI and {@link TemplateVariables}.
- *
+ *
* @param baseUri must not be {@literal null} or empty.
* @param variables defaults to {@link TemplateVariables#NONE}.
*/
@@ -108,7 +106,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Creates a new {@link UriTemplate} with the current {@link TemplateVariable}s augmented with the given ones.
- *
+ *
* @param variables can be {@literal null}.
* @return will never be {@literal null}.
*/
@@ -142,7 +140,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Creates a new {@link UriTemplate} with a {@link TemplateVariable} with the given name and type added.
- *
+ *
* @param variableName must not be {@literal null} or empty.
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
@@ -153,7 +151,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Returns whether the given candidate is a URI template.
- *
+ *
* @param candidate
* @return
*/
@@ -168,7 +166,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Returns the {@link TemplateVariable}s discovered.
- *
+ *
* @return
*/
public List getVariables() {
@@ -177,7 +175,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Returns the names of the variables discovered.
- *
+ *
* @return
*/
public List getVariableNames() {
@@ -189,7 +187,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Expands the {@link UriTemplate} using the given parameters. The values will be applied in the order of the
* variables discovered.
- *
+ *
* @param parameters
* @return
* @see #expand(Map)
@@ -215,7 +213,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Expands the {@link UriTemplate} using the given parameters.
- *
+ *
* @param parameters must not be {@literal null}.
* @return
*/
@@ -237,7 +235,7 @@ public class UriTemplate implements Iterable, Serializable {
return builder.build().toUri();
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@@ -246,7 +244,7 @@ public class UriTemplate implements Iterable, Serializable {
return this.variables.iterator();
}
- /*
+ /*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -268,7 +266,7 @@ public class UriTemplate implements Iterable, Serializable {
/**
* Appends the value for the given {@link TemplateVariable} to the given {@link UriComponentsBuilder}.
- *
+ *
* @param builder must not be {@literal null}.
* @param variable must not be {@literal null}.
* @param value can be {@literal null}.
@@ -311,17 +309,20 @@ public class UriTemplate implements Iterable, Serializable {
* @param value
* @see https://tools.ietf.org/html/rfc6570#section-2.4.2
*/
+ @SuppressWarnings("unchecked")
private static void appendComposite(UriComponentsBuilder builder, String name, Object value) {
if (value instanceof Iterable) {
- for (Object valuePart : (Iterable>) value) {
- builder.queryParam(name, valuePart);
- }
+
+ ((Iterable>) value).forEach(it -> builder.queryParam(name, it));
+
} else if (value instanceof Map) {
- for (Entry