#1294 - Delombok production code.
Production code will no longer use Lombok. It's confined to test scope.
This commit is contained in:
@@ -15,12 +15,9 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -31,15 +28,17 @@ import org.springframework.lang.Nullable;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class Affordance implements Iterable<AffordanceModel> {
|
||||
public final class Affordance implements Iterable<AffordanceModel> {
|
||||
|
||||
/**
|
||||
* Collection of {@link AffordanceModel}s related to this affordance.
|
||||
*/
|
||||
@Getter(AccessLevel.PACKAGE) //
|
||||
private final Map<MediaType, AffordanceModel> models;
|
||||
|
||||
public Affordance(Map<MediaType, AffordanceModel> models) {
|
||||
this.models = models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the {@link AffordanceModel} for the requested {@link MediaType}.
|
||||
*
|
||||
@@ -58,6 +57,30 @@ public class Affordance implements Iterable<AffordanceModel> {
|
||||
*/
|
||||
@Override
|
||||
public Iterator<AffordanceModel> iterator() {
|
||||
return models.values().iterator();
|
||||
return this.models.values().iterator();
|
||||
}
|
||||
|
||||
Map<MediaType, AffordanceModel> getModels() {
|
||||
return this.models;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Affordance that = (Affordance) o;
|
||||
return Objects.equals(this.models, that.models);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.models);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Affordance(models=" + this.models + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,9 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
@@ -37,9 +32,6 @@ import org.springframework.util.Assert;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public abstract class AffordanceModel {
|
||||
|
||||
/**
|
||||
@@ -72,6 +64,17 @@ public abstract class AffordanceModel {
|
||||
*/
|
||||
private PayloadMetadata output;
|
||||
|
||||
public AffordanceModel(String name, Link link, HttpMethod httpMethod, InputPayloadMetadata input,
|
||||
List<QueryParameter> queryMethodParameters, PayloadMetadata output) {
|
||||
|
||||
this.name = name;
|
||||
this.link = link;
|
||||
this.httpMethod = httpMethod;
|
||||
this.input = input;
|
||||
this.queryMethodParameters = queryMethodParameters;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the {@link Link} into an {@literal href} with no parameters.
|
||||
*
|
||||
@@ -107,6 +110,49 @@ public abstract class AffordanceModel {
|
||||
return getURI().equals(link.expand().getHref());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Link getLink() {
|
||||
return this.link;
|
||||
}
|
||||
|
||||
public HttpMethod getHttpMethod() {
|
||||
return this.httpMethod;
|
||||
}
|
||||
|
||||
public InputPayloadMetadata getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
public List<QueryParameter> getQueryMethodParameters() {
|
||||
return this.queryMethodParameters;
|
||||
}
|
||||
|
||||
public PayloadMetadata getOutput() {
|
||||
return this.output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
AffordanceModel that = (AffordanceModel) o;
|
||||
return Objects.equals(this.name, that.name) && Objects.equals(this.link, that.link)
|
||||
&& this.httpMethod == that.httpMethod && Objects.equals(this.input, that.input)
|
||||
&& Objects.equals(this.queryMethodParameters, that.queryMethodParameters)
|
||||
&& Objects.equals(this.output, that.output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.name, this.link, this.httpMethod, this.input, this.queryMethodParameters, this.output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata about payloads.
|
||||
*
|
||||
@@ -168,13 +214,18 @@ public abstract class AffordanceModel {
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
private static class DelegatingInputPayloadMetadata implements InputPayloadMetadata {
|
||||
|
||||
private final PayloadMetadata metadata;
|
||||
|
||||
public static DelegatingInputPayloadMetadata of(PayloadMetadata metadata) {
|
||||
return new DelegatingInputPayloadMetadata(metadata);
|
||||
}
|
||||
|
||||
private DelegatingInputPayloadMetadata(PayloadMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.AffordanceModel.PayloadMetadata#stream()
|
||||
@@ -210,6 +261,26 @@ public abstract class AffordanceModel {
|
||||
public List<String> getI18nCodes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
DelegatingInputPayloadMetadata that = (DelegatingInputPayloadMetadata) o;
|
||||
return Objects.equals(this.metadata, that.metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.metadata);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "AffordanceModel.DelegatingInputPayloadMetadata(metadata=" + this.metadata + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -36,8 +34,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
*/
|
||||
@UtilityClass
|
||||
public class IanaLinkRelations {
|
||||
public final class IanaLinkRelations {
|
||||
|
||||
/**
|
||||
* A String equivalent of {@link IanaLinkRelations#ABOUT}.
|
||||
@@ -1227,7 +1224,7 @@ public class IanaLinkRelations {
|
||||
/**
|
||||
* Consolidated collection of {@link IanaLinkRelations}s.
|
||||
*/
|
||||
private final Set<LinkRelation> LINK_RELATIONS;
|
||||
private static final Set<LinkRelation> LINK_RELATIONS;
|
||||
|
||||
static {
|
||||
|
||||
@@ -1239,6 +1236,10 @@ public class IanaLinkRelations {
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private IanaLinkRelations() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this relation an IANA standard? Per RFC 8288, parsing of link relations is case insensitive.
|
||||
*
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* Static class to find out whether a relation type is defined by the IANA.
|
||||
*
|
||||
@@ -25,9 +23,12 @@ import lombok.experimental.UtilityClass;
|
||||
* @author Roland Kulcsár
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@UtilityClass
|
||||
@Deprecated
|
||||
public class IanaRels {
|
||||
public final class IanaRels {
|
||||
|
||||
private IanaRels() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given relation type is defined by the IANA.
|
||||
|
||||
@@ -15,12 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.With;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -28,6 +22,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -48,10 +43,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(value = { "templated", "template" }, ignoreUnknown = true)
|
||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@EqualsAndHashCode(
|
||||
of = { "rel", "href", "hreflang", "media", "title", "type", "deprecation", "profile", "name", "affordances" })
|
||||
public class Link implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -9037755944661782121L;
|
||||
@@ -89,14 +80,14 @@ public class Link implements Serializable {
|
||||
public static final @Deprecated LinkRelation REL_LAST = IanaLinkRelations.LAST;
|
||||
|
||||
private LinkRelation rel;
|
||||
private @With String href;
|
||||
private @With String hreflang;
|
||||
private @With String media;
|
||||
private @With String title;
|
||||
private @With String type;
|
||||
private @With String deprecation;
|
||||
private @With String profile;
|
||||
private @With String name;
|
||||
private String href;
|
||||
private String hreflang;
|
||||
private String media;
|
||||
private String title;
|
||||
private String type;
|
||||
private String deprecation;
|
||||
private String profile;
|
||||
private String name;
|
||||
private @JsonIgnore UriTemplate template;
|
||||
private @JsonIgnore List<Affordance> affordances;
|
||||
|
||||
@@ -178,6 +169,22 @@ public class Link implements Serializable {
|
||||
this.affordances = affordances;
|
||||
}
|
||||
|
||||
private Link(LinkRelation rel, String href, String hreflang, String media, String title, String type,
|
||||
String deprecation, String profile, String name, UriTemplate template, List<Affordance> affordances) {
|
||||
|
||||
this.rel = rel;
|
||||
this.href = href;
|
||||
this.hreflang = hreflang;
|
||||
this.media = media;
|
||||
this.title = title;
|
||||
this.type = type;
|
||||
this.deprecation = deprecation;
|
||||
this.profile = profile;
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.affordances = affordances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new link to the given URI with the self relation.
|
||||
*
|
||||
@@ -421,46 +428,6 @@ public class Link implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String linkString = String.format("<%s>;rel=\"%s\"", href, rel.value());
|
||||
|
||||
if (hreflang != null) {
|
||||
linkString += ";hreflang=\"" + hreflang + "\"";
|
||||
}
|
||||
|
||||
if (media != null) {
|
||||
linkString += ";media=\"" + media + "\"";
|
||||
}
|
||||
|
||||
if (title != null) {
|
||||
linkString += ";title=\"" + title + "\"";
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
linkString += ";type=\"" + type + "\"";
|
||||
}
|
||||
|
||||
if (deprecation != null) {
|
||||
linkString += ";deprecation=\"" + deprecation + "\"";
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
linkString += ";profile=\"" + profile + "\"";
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
linkString += ";name=\"" + name + "\"";
|
||||
}
|
||||
|
||||
return linkString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to easily create {@link Link} instances from RFC-8288 compatible {@link String} representations of a
|
||||
* link.
|
||||
@@ -544,4 +511,220 @@ public class Link implements Serializable {
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal href}.
|
||||
*
|
||||
* @param href
|
||||
* @return
|
||||
*/
|
||||
public Link withHref(String href) {
|
||||
|
||||
return this.href == href ? this
|
||||
: new Link(this.rel, href, this.hreflang, this.media, this.title, this.type, this.deprecation, this.profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal hrefleng}.
|
||||
*
|
||||
* @param hreflang
|
||||
* @return
|
||||
*/
|
||||
public Link withHreflang(String hreflang) {
|
||||
|
||||
return this.hreflang == hreflang ? this
|
||||
: new Link(this.rel, this.href, hreflang, this.media, this.title, this.type, this.deprecation, this.profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal media}.
|
||||
*
|
||||
* @param media
|
||||
* @return
|
||||
*/
|
||||
public Link withMedia(String media) {
|
||||
|
||||
return this.media == media ? this
|
||||
: new Link(this.rel, this.href, this.hreflang, media, this.title, this.type, this.deprecation, this.profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal title}.
|
||||
*
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
public Link withTitle(String title) {
|
||||
|
||||
return this.title == title ? this
|
||||
: new Link(this.rel, this.href, this.hreflang, this.media, title, this.type, this.deprecation, this.profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal type}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public Link withType(String type) {
|
||||
|
||||
return this.type == type ? this
|
||||
: new Link(this.rel, this.href, this.hreflang, this.media, this.title, type, this.deprecation, this.profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal deprecation}.
|
||||
*
|
||||
* @param deprecation
|
||||
* @return
|
||||
*/
|
||||
public Link withDeprecation(String deprecation) {
|
||||
|
||||
return this.deprecation == deprecation ? this
|
||||
: new Link(this.rel, this.href, this.hreflang, this.media, this.title, this.type, deprecation, this.profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal profile}.
|
||||
*
|
||||
* @param profile
|
||||
* @return
|
||||
*/
|
||||
public Link withProfile(String profile) {
|
||||
|
||||
return this.profile == profile ? this
|
||||
: new Link(this.rel, this.href, this.hreflang, this.media, this.title, this.type, this.deprecation, profile,
|
||||
this.name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Link} by copying all attributes and applying the new {@literal name}.
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public Link withName(String name) {
|
||||
|
||||
return this.name == name ? this
|
||||
: new Link(this.rel, this.href, this.hreflang, this.media, this.title, this.type, this.deprecation,
|
||||
this.profile, name, this.template, this.affordances);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public LinkRelation getRel() {
|
||||
return this.rel;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getHreflang() {
|
||||
return this.hreflang;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getMedia() {
|
||||
return this.media;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getDeprecation() {
|
||||
return this.deprecation;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getProfile() {
|
||||
return this.profile;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public UriTemplate getTemplate() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Link link = (Link) o;
|
||||
return Objects.equals(this.rel, link.rel) && Objects.equals(this.href, link.href)
|
||||
&& Objects.equals(this.hreflang, link.hreflang) && Objects.equals(this.media, link.media)
|
||||
&& Objects.equals(this.title, link.title) && Objects.equals(this.type, link.type)
|
||||
&& Objects.equals(this.deprecation, link.deprecation) && Objects.equals(this.profile, link.profile)
|
||||
&& Objects.equals(this.name, link.name) && Objects.equals(this.affordances, link.affordances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return Objects.hash(this.rel, this.href, this.hreflang, this.media, this.title, this.type, this.deprecation,
|
||||
this.profile, this.name, this.affordances);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String linkString = String.format("<%s>;rel=\"%s\"", href, rel.value());
|
||||
|
||||
if (hreflang != null) {
|
||||
linkString += ";hreflang=\"" + hreflang + "\"";
|
||||
}
|
||||
|
||||
if (media != null) {
|
||||
linkString += ";media=\"" + media + "\"";
|
||||
}
|
||||
|
||||
if (title != null) {
|
||||
linkString += ";title=\"" + title + "\"";
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
linkString += ";type=\"" + type + "\"";
|
||||
}
|
||||
|
||||
if (deprecation != null) {
|
||||
linkString += ";deprecation=\"" + deprecation + "\"";
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
linkString += ";profile=\"" + profile + "\"";
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
linkString += ";name=\"" + name + "\"";
|
||||
}
|
||||
|
||||
return linkString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -36,14 +32,19 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class QueryParameter {
|
||||
public final class QueryParameter {
|
||||
|
||||
private final String name;
|
||||
private final @Nullable @With String value;
|
||||
private final @Nullable String value;
|
||||
private final boolean required;
|
||||
|
||||
private QueryParameter(String name, String value, boolean required) {
|
||||
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryParameter} from the given {@link MethodParameter}.
|
||||
*
|
||||
@@ -93,4 +94,48 @@ public class QueryParameter {
|
||||
public static QueryParameter optional(String name) {
|
||||
return new QueryParameter(name, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link QueryParameter} by copying all attributes and applying the new {@literal value}.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryParameter withValue(@Nullable String value) {
|
||||
return this.value == value ? this : new QueryParameter(this.name, value, this.required);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return this.required;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
QueryParameter that = (QueryParameter) o;
|
||||
return this.required == that.required && Objects.equals(this.name, that.name)
|
||||
&& Objects.equals(this.value, that.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.name, this.value, this.required);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "QueryParameter(name=" + this.name + ", value=" + this.value + ", required=" + this.required + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -36,14 +31,12 @@ import com.fasterxml.jackson.annotation.JsonValue;
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
class StringLinkRelation implements LinkRelation, Serializable {
|
||||
final class StringLinkRelation implements LinkRelation, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3904935345545567957L;
|
||||
private static final Map<String, StringLinkRelation> CACHE = new ConcurrentHashMap<>(256);
|
||||
|
||||
@NonNull String relation;
|
||||
private final String relation;
|
||||
|
||||
/**
|
||||
* Returns a (potentially cached) {@link LinkRelation} for the given value.
|
||||
@@ -59,6 +52,13 @@ class StringLinkRelation implements LinkRelation, Serializable {
|
||||
return CACHE.computeIfAbsent(relation, StringLinkRelation::new);
|
||||
}
|
||||
|
||||
private StringLinkRelation(String relation) {
|
||||
|
||||
Assert.notNull(relation, "relation cannot be null!");
|
||||
|
||||
this.relation = relation;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.LinkRelation#value()
|
||||
@@ -106,4 +106,8 @@ class StringLinkRelation implements LinkRelation, Serializable {
|
||||
|
||||
return this.relation.equalsIgnoreCase(that.relation);
|
||||
}
|
||||
|
||||
public String getRelation() {
|
||||
return this.relation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,10 @@ package org.springframework.hateoas;
|
||||
|
||||
import static org.springframework.hateoas.TemplateVariable.VariableType.*;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -33,15 +31,13 @@ import org.springframework.util.StringUtils;
|
||||
* @author Oliver Gierke
|
||||
* @author JamesE Richardson
|
||||
*/
|
||||
@Value
|
||||
@EqualsAndHashCode
|
||||
public final class TemplateVariable implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2731446749851863774L;
|
||||
|
||||
String name;
|
||||
TemplateVariable.VariableType type;
|
||||
String description;
|
||||
private final String name;
|
||||
private final TemplateVariable.VariableType type;
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* Creates a new {@link TemplateVariable} with the given name and type.
|
||||
@@ -206,6 +202,35 @@ public final class TemplateVariable implements Serializable {
|
||||
return StringUtils.hasText(description) ? String.format("%s - %s", base, description) : base;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public VariableType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
TemplateVariable that = (TemplateVariable) o;
|
||||
return Objects.equals(this.name, that.name) && this.type == that.type
|
||||
&& Objects.equals(this.description, that.description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.name, this.type, this.description);
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration for all supported variable types.
|
||||
*
|
||||
|
||||
@@ -17,8 +17,6 @@ package org.springframework.hateoas;
|
||||
|
||||
import static org.springframework.hateoas.TemplateVariable.VariableType.*;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -26,6 +24,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.hateoas.TemplateVariable.VariableType;
|
||||
@@ -36,7 +35,6 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
public final class TemplateVariables implements Iterable<TemplateVariable>, Serializable {
|
||||
|
||||
public static final TemplateVariables NONE = new TemplateVariables();
|
||||
@@ -172,4 +170,20 @@ public final class TemplateVariables implements Iterable<TemplateVariable>, Seri
|
||||
|
||||
return builder.append("}").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
TemplateVariables that = (TemplateVariables) o;
|
||||
return Objects.equals(this.variables, that.variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.variables);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,10 @@
|
||||
*/
|
||||
package org.springframework.hateoas.client;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -36,10 +31,7 @@ import org.springframework.util.Assert;
|
||||
* @author Manish Misra
|
||||
* @since 0.18
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
public class Hop {
|
||||
public final class Hop {
|
||||
|
||||
/**
|
||||
* Name of this hop.
|
||||
@@ -49,12 +41,19 @@ public class Hop {
|
||||
/**
|
||||
* Collection of URI Template parameters.
|
||||
*/
|
||||
private final @With Map<String, Object> parameters;
|
||||
private final Map<String, Object> parameters;
|
||||
|
||||
/**
|
||||
* Extra {@link HttpHeaders} to apply to this hop.
|
||||
*/
|
||||
private final @With HttpHeaders headers;
|
||||
private final HttpHeaders headers;
|
||||
|
||||
private Hop(String rel, Map<String, Object> parameters, HttpHeaders headers) {
|
||||
|
||||
this.rel = rel;
|
||||
this.parameters = parameters;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Hop} for the given relation name.
|
||||
@@ -86,6 +85,26 @@ public class Hop {
|
||||
return new Hop(this.rel, parameters, this.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Hop} by copying all the attributes and replacing the {@literal parameters}.
|
||||
*
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
public Hop withParameters(Map<String, Object> parameters) {
|
||||
return this.parameters == parameters ? this : new Hop(this.rel, parameters, this.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Hop} by copying all the attributes and replacing the {@literal headers}.
|
||||
*
|
||||
* @param headers
|
||||
* @return
|
||||
*/
|
||||
public Hop withHeaders(HttpHeaders headers) {
|
||||
return this.headers == headers ? this : new Hop(this.rel, this.parameters, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one header to the HttpHeaders collection.
|
||||
*
|
||||
@@ -136,4 +155,37 @@ public class Hop {
|
||||
|
||||
return mergedParameters;
|
||||
}
|
||||
|
||||
String getRel() {
|
||||
return this.rel;
|
||||
}
|
||||
|
||||
Map<String, Object> getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
HttpHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Hop hop = (Hop) o;
|
||||
return Objects.equals(this.rel, hop.rel) && Objects.equals(this.parameters, hop.parameters)
|
||||
&& Objects.equals(this.headers, hop.headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.rel, this.parameters, this.headers);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Hop(rel=" + this.rel + ", parameters=" + this.parameters + ", headers=" + this.headers + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,6 @@ package org.springframework.hateoas.client;
|
||||
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -27,6 +24,7 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
@@ -448,22 +446,86 @@ public class Traverson {
|
||||
/**
|
||||
* Temporary container for a string-base {@literal URI} and {@link HttpHeaders}.
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
private static class UriStringAndHeaders {
|
||||
private static final class UriStringAndHeaders {
|
||||
|
||||
private final String uri;
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
UriStringAndHeaders(String uri, HttpHeaders httpHeaders) {
|
||||
|
||||
this.uri = uri;
|
||||
this.httpHeaders = httpHeaders;
|
||||
}
|
||||
|
||||
String getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
HttpHeaders getHttpHeaders() {
|
||||
return this.httpHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
UriStringAndHeaders that = (UriStringAndHeaders) o;
|
||||
return Objects.equals(this.uri, that.uri) && Objects.equals(this.httpHeaders, that.httpHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.uri, this.httpHeaders);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Traverson.UriStringAndHeaders(uri=" + this.uri + ", httpHeaders=" + this.httpHeaders + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary container for a {@link URI}-based {@literal URI} and {@link HttpHeaders}.
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
private static class URIAndHeaders {
|
||||
private static final class URIAndHeaders {
|
||||
|
||||
private final URI uri;
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
URIAndHeaders(URI uri, HttpHeaders httpHeaders) {
|
||||
|
||||
this.uri = uri;
|
||||
this.httpHeaders = httpHeaders;
|
||||
}
|
||||
|
||||
URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
HttpHeaders getHttpHeaders() {
|
||||
return this.httpHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
URIAndHeaders that = (URIAndHeaders) o;
|
||||
return Objects.equals(this.uri, that.uri) && Objects.equals(this.httpHeaders, that.httpHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.uri, this.httpHeaders);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Traverson.URIAndHeaders(uri=" + this.uri + ", httpHeaders=" + this.httpHeaders + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
@@ -54,11 +52,14 @@ class RestTemplateHateoasConfiguration {
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class HypermediaRestTemplateBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectFactory<HypermediaRestTemplateConfigurer> configurer;
|
||||
|
||||
public HypermediaRestTemplateBeanPostProcessor(ObjectFactory<HypermediaRestTemplateConfigurer> configurer) {
|
||||
this.configurer = configurer;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -60,11 +58,14 @@ class WebClientHateoasConfiguration {
|
||||
* @author Greg Turnquist
|
||||
* @since 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class HypermediaWebClientBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectFactory<HypermediaWebClientConfigurer> configurer;
|
||||
|
||||
public HypermediaWebClientBeanPostProcessor(ObjectFactory<HypermediaWebClientConfigurer> configurer) {
|
||||
this.configurer = configurer;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -75,11 +73,14 @@ class WebFluxHateoasConfiguration {
|
||||
* @author Greg Turnquist
|
||||
* @since 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class HypermediaWebFluxConfigurer implements WebFluxConfigurer {
|
||||
|
||||
private final WebFluxCodecs codecs;
|
||||
|
||||
public HypermediaWebFluxConfigurer(WebFluxCodecs codecs) {
|
||||
this.codecs = codecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure custom HTTP message readers and writers or override built-in ones.
|
||||
* <p>
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -79,11 +77,14 @@ class WebMvcHateoasConfiguration {
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class HypermediaWebMvcConfigurer implements WebMvcConfigurer {
|
||||
|
||||
private final @NonNull WebConverters hypermediaConverters;
|
||||
|
||||
public HypermediaWebMvcConfigurer(WebConverters hypermediaConverters) {
|
||||
this.hypermediaConverters = hypermediaConverters;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#extendMessageConverters(java.util.List)
|
||||
@@ -98,11 +99,15 @@ class WebMvcHateoasConfiguration {
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class HypermediaRepresentationModelBeanProcessorPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectProvider<RepresentationModelProcessorInvoker> invoker;
|
||||
|
||||
public HypermediaRepresentationModelBeanProcessorPostProcessor(
|
||||
ObjectProvider<RepresentationModelProcessorInvoker> invoker) {
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
|
||||
@@ -17,11 +17,6 @@ package org.springframework.hateoas.mediatype;
|
||||
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -46,7 +41,6 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Drotbohm
|
||||
* @see #afford(HttpMethod)
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
public class Affordances implements AffordanceOperations {
|
||||
|
||||
private static List<AffordanceModelFactory> factories = SpringFactoriesLoader
|
||||
@@ -54,6 +48,14 @@ public class Affordances implements AffordanceOperations {
|
||||
|
||||
private final Link link;
|
||||
|
||||
public static Affordances of(Link link) {
|
||||
return new Affordances(link);
|
||||
}
|
||||
|
||||
private Affordances(Link link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link Affordance}s created.
|
||||
*
|
||||
@@ -91,19 +93,39 @@ public class Affordances implements AffordanceOperations {
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static class AffordanceBuilder implements AffordanceOperations {
|
||||
|
||||
private final Affordances context;
|
||||
|
||||
private final HttpMethod method;
|
||||
private final @With Link target;
|
||||
private final Link target;
|
||||
private final InputPayloadMetadata inputMetdata;
|
||||
private final PayloadMetadata outputMetadata;
|
||||
|
||||
private List<QueryParameter> parameters = Collections.emptyList();
|
||||
private @Nullable @With String name;
|
||||
private @Nullable String name;
|
||||
|
||||
private AffordanceBuilder(Affordances context, HttpMethod method, Link target, InputPayloadMetadata inputMetdata,
|
||||
PayloadMetadata outputMetadata) {
|
||||
|
||||
this.context = context;
|
||||
this.method = method;
|
||||
this.target = target;
|
||||
this.inputMetdata = inputMetdata;
|
||||
this.outputMetadata = outputMetadata;
|
||||
}
|
||||
|
||||
private AffordanceBuilder(Affordances context, HttpMethod method, Link target, InputPayloadMetadata inputMetdata,
|
||||
PayloadMetadata outputMetadata, List<QueryParameter> parameters, String name) {
|
||||
|
||||
this.context = context;
|
||||
this.method = method;
|
||||
this.target = target;
|
||||
this.inputMetdata = inputMetdata;
|
||||
this.outputMetadata = outputMetadata;
|
||||
this.parameters = parameters;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given type as input and output model for the affordance.
|
||||
@@ -205,7 +227,7 @@ public class Affordances implements AffordanceOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current {@link QueryParameters} with the given ones.
|
||||
* Replaces the current {@link QueryParameter} list with the given ones.
|
||||
*
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
@@ -215,7 +237,7 @@ public class Affordances implements AffordanceOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current {@link QueryParameters} with the given ones.
|
||||
* Replaces the current {@link QueryParameter} list with the given ones.
|
||||
*
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
@@ -320,5 +342,31 @@ public class Affordances implements AffordanceOperations {
|
||||
|
||||
return resolvedType == null ? name : name.concat(resolvedType.getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AffordanceBuilder} by copying all attributes and replacing the {@literal target}.
|
||||
*
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public AffordanceBuilder withTarget(Link target) {
|
||||
|
||||
return this.target == target ? this
|
||||
: new AffordanceBuilder(this.context, this.method, target, this.inputMetdata, this.outputMetadata,
|
||||
this.parameters, this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AffordanceBuilder} by copying all attributes and replacing the {@literal name}.
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public AffordanceBuilder withName(@Nullable String name) {
|
||||
|
||||
return this.name == name ? this
|
||||
: new AffordanceBuilder(this.context, this.method, this.target, this.inputMetdata, this.outputMetadata,
|
||||
this.parameters, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,22 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -140,6 +147,7 @@ public class PropertyUtils {
|
||||
}
|
||||
|
||||
private static Map<String, Object> unwrapPropertyIfNeeded(String propertyName, BeanWrapper wrapper) {
|
||||
|
||||
Field descriptorField = ReflectionUtils.findField(wrapper.getWrappedClass(), propertyName);
|
||||
Method readMethod = wrapper.getPropertyDescriptor(propertyName).getReadMethod();
|
||||
|
||||
@@ -422,13 +430,16 @@ public class PropertyUtils {
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
private static class DefaultPropertyMetadata implements PropertyMetadata, Comparable<DefaultPropertyMetadata> {
|
||||
|
||||
private static Comparator<PropertyMetadata> BY_NAME = Comparator.comparing(PropertyMetadata::getName);
|
||||
|
||||
private final AnnotatedProperty property;
|
||||
|
||||
private DefaultPropertyMetadata(AnnotatedProperty property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.mediatype.PropertyMetadata#getName()
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
@@ -39,7 +36,7 @@ import org.springframework.hateoas.AffordanceModel.PropertyMetadataConfigured;
|
||||
*/
|
||||
class TypeBasedPayloadMetadata implements InputPayloadMetadata {
|
||||
|
||||
private final @Getter(AccessLevel.PACKAGE) ResolvableType type;
|
||||
private final ResolvableType type;
|
||||
private final SortedMap<String, PropertyMetadata> properties;
|
||||
|
||||
TypeBasedPayloadMetadata(ResolvableType type, Stream<PropertyMetadata> properties) {
|
||||
@@ -93,4 +90,8 @@ class TypeBasedPayloadMetadata implements InputPayloadMetadata {
|
||||
|
||||
return Arrays.asList(type.getName(), type.getSimpleName());
|
||||
}
|
||||
|
||||
ResolvableType getType() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.alps;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.mediatype.alps.Descriptor.DescriptorBuilder;
|
||||
import org.springframework.hateoas.mediatype.alps.Doc.DocBuilder;
|
||||
@@ -38,11 +36,9 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
* @see http://alps.io
|
||||
* @see http://alps.io/spec/#prop-alps
|
||||
*/
|
||||
@Value
|
||||
@Builder(builderMethodName = "alps")
|
||||
@JsonPropertyOrder({ "version", "doc", "descriptor" })
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Alps {
|
||||
public final class Alps {
|
||||
|
||||
private final String version;
|
||||
private final Doc doc;
|
||||
@@ -57,6 +53,15 @@ public class Alps {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link AlpsBuilder}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static AlpsBuilder alps() {
|
||||
return new AlpsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link DescriptorBuilder}.
|
||||
*
|
||||
@@ -83,4 +88,72 @@ public class Alps {
|
||||
public static ExtBuilder ext() {
|
||||
return Ext.builder();
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public Doc getDoc() {
|
||||
return this.doc;
|
||||
}
|
||||
|
||||
public List<Descriptor> getDescriptor() {
|
||||
return this.descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Alps alps = (Alps) o;
|
||||
return Objects.equals(this.version, alps.version) && Objects.equals(this.doc, alps.doc)
|
||||
&& Objects.equals(this.descriptor, alps.descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.version, this.doc, this.descriptor);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Alps(version=" + this.version + ", doc=" + this.doc + ", descriptor=" + this.descriptor + ")";
|
||||
}
|
||||
|
||||
public static class AlpsBuilder {
|
||||
|
||||
private String version;
|
||||
private Doc doc;
|
||||
private List<Descriptor> descriptor;
|
||||
|
||||
AlpsBuilder() {}
|
||||
|
||||
public Alps.AlpsBuilder version(String version) {
|
||||
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Alps.AlpsBuilder doc(Doc doc) {
|
||||
|
||||
this.doc = doc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Alps.AlpsBuilder descriptor(List<Descriptor> descriptor) {
|
||||
|
||||
this.descriptor = descriptor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Alps build() {
|
||||
return new Alps(this.version, this.doc, this.descriptor);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Alps.AlpsBuilder(version=" + this.version + ", doc=" + this.doc + ", descriptor=" + this.descriptor + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.alps;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
@@ -33,11 +31,9 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
* @since 0.15
|
||||
* @see http://alps.io/spec/#prop-descriptor
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
@JsonPropertyOrder({ "id", "href", "name", "type", "doc", "descriptor", "ext" })
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Descriptor {
|
||||
public final class Descriptor {
|
||||
|
||||
private final String id;
|
||||
private final String href;
|
||||
@@ -63,4 +59,138 @@ public class Descriptor {
|
||||
this.rt = rt;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public static DescriptorBuilder builder() {
|
||||
return new DescriptorBuilder();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Doc getDoc() {
|
||||
return this.doc;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Ext getExt() {
|
||||
return this.ext;
|
||||
}
|
||||
|
||||
public String getRt() {
|
||||
return this.rt;
|
||||
}
|
||||
|
||||
public List<Descriptor> getDescriptor() {
|
||||
return this.descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Descriptor that = (Descriptor) o;
|
||||
return Objects.equals(this.id, that.id) && Objects.equals(this.href, that.href)
|
||||
&& Objects.equals(this.name, that.name) && Objects.equals(this.doc, that.doc) && this.type == that.type
|
||||
&& Objects.equals(this.ext, that.ext) && Objects.equals(this.rt, that.rt)
|
||||
&& Objects.equals(this.descriptor, that.descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.id, this.href, this.name, this.doc, this.type, this.ext, this.rt, this.descriptor);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "Descriptor(id=" + this.id + ", href=" + this.href + ", name=" + this.name + ", doc=" + this.doc + ", type="
|
||||
+ this.type + ", ext=" + this.ext + ", rt=" + this.rt + ", descriptor=" + this.descriptor + ")";
|
||||
}
|
||||
|
||||
public static class DescriptorBuilder {
|
||||
|
||||
private String id;
|
||||
private String href;
|
||||
private String name;
|
||||
private Doc doc;
|
||||
private Type type;
|
||||
private Ext ext;
|
||||
private String rt;
|
||||
private List<Descriptor> descriptor;
|
||||
|
||||
DescriptorBuilder() {}
|
||||
|
||||
public Descriptor.DescriptorBuilder id(String id) {
|
||||
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder href(String href) {
|
||||
|
||||
this.href = href;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder name(String name) {
|
||||
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder doc(Doc doc) {
|
||||
|
||||
this.doc = doc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder type(Type type) {
|
||||
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder ext(Ext ext) {
|
||||
|
||||
this.ext = ext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder rt(String rt) {
|
||||
|
||||
this.rt = rt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor.DescriptorBuilder descriptor(List<Descriptor> descriptor) {
|
||||
|
||||
this.descriptor = descriptor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor build() {
|
||||
return new Descriptor(this.id, this.href, this.name, this.doc, this.type, this.ext, this.rt, this.descriptor);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "Descriptor.DescriptorBuilder(id=" + this.id + ", href=" + this.href + ", name=" + this.name + ", doc="
|
||||
+ this.doc + ", type=" + this.type + ", ext=" + this.ext + ", rt=" + this.rt + ", descriptor="
|
||||
+ this.descriptor + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.alps;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -33,11 +32,9 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
* @since 0.15
|
||||
* @see http://alps.io/spec/#prop-doc
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
@JsonPropertyOrder({ "format", "href", "value" })
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Doc {
|
||||
public final class Doc {
|
||||
|
||||
private final String href;
|
||||
private final String value;
|
||||
@@ -67,4 +64,75 @@ public class Doc {
|
||||
this.value = value;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public static DocBuilder builder() {
|
||||
return new DocBuilder();
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return this.format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Doc doc = (Doc) o;
|
||||
return Objects.equals(this.href, doc.href) && Objects.equals(this.value, doc.value) && this.format == doc.format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.href, this.value, this.format);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Doc(href=" + this.href + ", value=" + this.value + ", format=" + this.format + ")";
|
||||
}
|
||||
|
||||
public static class DocBuilder {
|
||||
|
||||
private String href;
|
||||
private String value;
|
||||
private Format format;
|
||||
|
||||
DocBuilder() {}
|
||||
|
||||
public Doc.DocBuilder href(String href) {
|
||||
|
||||
this.href = href;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Doc.DocBuilder value(String value) {
|
||||
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Doc.DocBuilder format(Format format) {
|
||||
|
||||
this.format = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Doc build() {
|
||||
return new Doc(this.href, this.value, this.format);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Doc.DocBuilder(href=" + this.href + ", value=" + this.value + ", format=" + this.format + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.alps;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@@ -30,10 +29,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
* @since 0.15
|
||||
* @see http://alps.io/spec/#prop-ext
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
@JsonPropertyOrder({ "id", "href", "value" })
|
||||
public class Ext {
|
||||
public final class Ext {
|
||||
|
||||
private final String id;
|
||||
private final String href;
|
||||
@@ -46,4 +43,76 @@ public class Ext {
|
||||
this.href = href;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ExtBuilder builder() {
|
||||
return new ExtBuilder();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Ext ext = (Ext) o;
|
||||
return Objects.equals(this.id, ext.id) && Objects.equals(this.href, ext.href)
|
||||
&& Objects.equals(this.value, ext.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.id, this.href, this.value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Ext(id=" + this.id + ", href=" + this.href + ", value=" + this.value + ")";
|
||||
}
|
||||
|
||||
public static class ExtBuilder {
|
||||
|
||||
private String id;
|
||||
private String href;
|
||||
private String value;
|
||||
|
||||
ExtBuilder() {}
|
||||
|
||||
public Ext.ExtBuilder id(String id) {
|
||||
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ext.ExtBuilder href(String href) {
|
||||
|
||||
this.href = href;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ext.ExtBuilder value(String value) {
|
||||
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ext build() {
|
||||
return new Ext(this.id, this.href, this.value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Ext.ExtBuilder(id=" + this.id + ", href=" + this.href + ", value=" + this.value + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
@@ -39,19 +35,16 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With(AccessLevel.PACKAGE)
|
||||
class CollectionJson<T> {
|
||||
final class CollectionJson<T> {
|
||||
|
||||
private String version;
|
||||
private @Nullable String href;
|
||||
private final String version;
|
||||
private @Nullable final String href;
|
||||
|
||||
private @JsonInclude(Include.NON_EMPTY) Links links;
|
||||
private @JsonInclude(Include.NON_EMPTY) List<CollectionJsonItem<T>> items;
|
||||
private @JsonInclude(Include.NON_EMPTY) List<CollectionJsonQuery> queries;
|
||||
private @JsonInclude(Include.NON_NULL) @Nullable CollectionJsonTemplate template;
|
||||
private @JsonInclude(Include.NON_NULL) @Nullable CollectionJsonError error;
|
||||
private @JsonInclude(Include.NON_EMPTY) final Links links;
|
||||
private @JsonInclude(Include.NON_EMPTY) final List<CollectionJsonItem<T>> items;
|
||||
private @JsonInclude(Include.NON_EMPTY) final List<CollectionJsonQuery> queries;
|
||||
private @JsonInclude(Include.NON_NULL) @Nullable final CollectionJsonTemplate template;
|
||||
private @JsonInclude(Include.NON_NULL) @Nullable final CollectionJsonError error;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJson(@JsonProperty("version") String version, //
|
||||
@@ -75,23 +68,58 @@ class CollectionJson<T> {
|
||||
this("1.0", null, Links.NONE, Collections.emptyList(), null, null, null);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
final CollectionJson<T> withItems(CollectionJsonItem<T>... items) {
|
||||
return withItems(Arrays.asList(items));
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal version}.
|
||||
*
|
||||
* @param version
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withVersion(String version) {
|
||||
|
||||
return this.version == version ? this
|
||||
: new CollectionJson<T>(version, this.href, this.links, this.items, this.queries, this.template, this.error);
|
||||
}
|
||||
|
||||
CollectionJson<T> withItems(List<CollectionJsonItem<T>> items) {
|
||||
return new CollectionJson<>(version, href, links, items, queries, template, error);
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal href}.
|
||||
*
|
||||
* @param href
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withHref(@Nullable String href) {
|
||||
|
||||
return this.href == href ? this
|
||||
: new CollectionJson<T>(this.version, href, this.links, this.items, this.queries, this.template, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@link Link}s .
|
||||
*
|
||||
* @param links
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withLinks(Link... links) {
|
||||
return withLinks(Links.of(links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@link Links}.
|
||||
*
|
||||
* @param links
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withLinks(Links links) {
|
||||
return new CollectionJson<>(version, href, links, items, queries, template, error);
|
||||
|
||||
return this.links == links ? this
|
||||
: new CollectionJson<T>(this.version, this.href, links, this.items, this.queries, this.template, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@link Links} with a {@literal self}
|
||||
* {@link Link}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withOwnSelfLink() {
|
||||
|
||||
String href = this.href;
|
||||
@@ -103,7 +131,136 @@ class CollectionJson<T> {
|
||||
return withLinks(Links.of(Link.of(href)).merge(MergeMode.SKIP_BY_REL, links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal items}.
|
||||
*
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
@SafeVarargs
|
||||
final CollectionJson<T> withItems(CollectionJsonItem<T>... items) {
|
||||
return withItems(Arrays.asList(items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal items}.
|
||||
*
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withItems(List<CollectionJsonItem<T>> items) {
|
||||
|
||||
return this.items == items ? this
|
||||
: new CollectionJson<T>(this.version, this.href, this.links, items, this.queries, this.template, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal queries}.
|
||||
*
|
||||
* @param queries
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withQueries(List<CollectionJsonQuery> queries) {
|
||||
|
||||
return this.queries == queries ? this
|
||||
: new CollectionJson<T>(this.version, this.href, this.links, this.items, queries, this.template, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal template}.
|
||||
*
|
||||
* @param template
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withTemplate(@Nullable CollectionJsonTemplate template) {
|
||||
|
||||
return this.template == template ? this
|
||||
: new CollectionJson<T>(this.version, this.href, this.links, this.items, this.queries, template, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJson} by copying attributes and replacing the {@literal error}.
|
||||
*
|
||||
* @param error
|
||||
* @return
|
||||
*/
|
||||
CollectionJson<T> withError(@Nullable CollectionJsonError error) {
|
||||
|
||||
return this.error == error ? this
|
||||
: new CollectionJson<T>(this.version, this.href, this.links, this.items, this.queries, this.template, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any {@literal items}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasItems() {
|
||||
return !items.isEmpty();
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public Links getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public List<CollectionJsonItem<T>> getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public List<CollectionJsonQuery> getQueries() {
|
||||
return this.queries;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public CollectionJsonTemplate getTemplate() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public CollectionJsonError getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJson<?> that = (CollectionJson<?>) o;
|
||||
return Objects.equals(this.version, that.version) && Objects.equals(this.href, that.href)
|
||||
&& Objects.equals(this.links, that.links) && Objects.equals(this.items, that.items)
|
||||
&& Objects.equals(this.queries, that.queries) && Objects.equals(this.template, that.template)
|
||||
&& Objects.equals(this.error, that.error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.version, this.href, this.links, this.items, this.queries, this.template, this.error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "CollectionJson{" + "version='" + this.version + '\'' + ", href='" + this.href + '\'' + ", links="
|
||||
+ this.links + ", items=" + this.items + ", queries=" + this.queries + ", template=" + this.template
|
||||
+ ", error=" + this.error + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -36,14 +34,13 @@ import org.springframework.http.HttpMethod;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
class CollectionJsonAffordanceModel extends AffordanceModel {
|
||||
|
||||
private static final Set<HttpMethod> ENTITY_ALTERING_METHODS = EnumSet.of(HttpMethod.POST, HttpMethod.PUT,
|
||||
HttpMethod.PATCH);
|
||||
|
||||
private final @Getter List<CollectionJsonData> inputProperties;
|
||||
private final @Getter List<CollectionJsonData> queryProperties;
|
||||
private final List<CollectionJsonData> inputProperties;
|
||||
private final List<CollectionJsonData> queryProperties;
|
||||
|
||||
public CollectionJsonAffordanceModel(String name, Link link, HttpMethod httpMethod, InputPayloadMetadata inputType,
|
||||
List<QueryParameter> queryMethodParameters, PayloadMetadata outputType) {
|
||||
@@ -88,4 +85,31 @@ class CollectionJsonAffordanceModel extends AffordanceModel {
|
||||
.withValue("")) //
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<CollectionJsonData> getInputProperties() {
|
||||
return this.inputProperties;
|
||||
}
|
||||
|
||||
public List<CollectionJsonData> getQueryProperties() {
|
||||
return this.queryProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof CollectionJsonAffordanceModel))
|
||||
return false;
|
||||
if (!super.equals(o))
|
||||
return false;
|
||||
CollectionJsonAffordanceModel that = (CollectionJsonAffordanceModel) o;
|
||||
return Objects.equals(this.inputProperties, that.inputProperties)
|
||||
&& Objects.equals(this.queryProperties, that.queryProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), this.inputProperties, this.queryProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.hateoas.AffordanceModel;
|
||||
@@ -36,11 +34,15 @@ import org.springframework.http.MediaType;
|
||||
*/
|
||||
class CollectionJsonAffordanceModelFactory implements AffordanceModelFactory {
|
||||
|
||||
private final @Getter MediaType mediaType = MediaTypes.COLLECTION_JSON;
|
||||
private final MediaType mediaType = MediaTypes.COLLECTION_JSON;
|
||||
|
||||
@Override
|
||||
public AffordanceModel getAffordanceModel(String name, Link link, HttpMethod httpMethod,
|
||||
InputPayloadMetadata inputType, List<QueryParameter> queryMethodParameters, PayloadMetadata outputType) {
|
||||
return new CollectionJsonAffordanceModel(name, link, httpMethod, inputType, queryMethodParameters, outputType);
|
||||
}
|
||||
|
||||
public MediaType getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -29,15 +27,12 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@With
|
||||
class CollectionJsonData {
|
||||
final class CollectionJsonData {
|
||||
|
||||
private @Nullable String name;
|
||||
private @Nullable Object value;
|
||||
private @Nullable String prompt;
|
||||
private @Nullable final String name;
|
||||
private @Nullable final Object value;
|
||||
private @Nullable final String prompt;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonData(@JsonProperty("name") @Nullable String name, //
|
||||
@@ -52,4 +47,56 @@ class CollectionJsonData {
|
||||
CollectionJsonData() {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
public CollectionJsonData withName(@Nullable String name) {
|
||||
return this.name == name ? this : new CollectionJsonData(name, this.value, this.prompt);
|
||||
}
|
||||
|
||||
public CollectionJsonData withValue(@Nullable Object value) {
|
||||
return this.value == value ? this : new CollectionJsonData(this.name, value, this.prompt);
|
||||
}
|
||||
|
||||
public CollectionJsonData withPrompt(@Nullable String prompt) {
|
||||
return this.prompt == prompt ? this : new CollectionJsonData(this.name, this.value, prompt);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getPrompt() {
|
||||
return this.prompt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJsonData that = (CollectionJsonData) o;
|
||||
return Objects.equals(this.name, that.name) && Objects.equals(this.value, that.value)
|
||||
&& Objects.equals(this.prompt, that.prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.name, this.value, this.prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CollectionJsonData(name=" + this.name + ", value=" + this.value + ", prompt=" + this.prompt + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.Links;
|
||||
|
||||
@@ -33,13 +28,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With(AccessLevel.PACKAGE)
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
class CollectionJsonDocument<T> {
|
||||
final class CollectionJsonDocument<T> {
|
||||
|
||||
private CollectionJson<T> collection;
|
||||
private final CollectionJson<T> collection;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonDocument(@JsonProperty("version") String version, //
|
||||
@@ -49,6 +40,47 @@ class CollectionJsonDocument<T> {
|
||||
@JsonProperty("queries") List<CollectionJsonQuery> queries, //
|
||||
@JsonProperty("template") CollectionJsonTemplate template, //
|
||||
@JsonProperty("error") CollectionJsonError error) {
|
||||
|
||||
this.collection = new CollectionJson<>(version, href, links, items, queries, template, error);
|
||||
}
|
||||
|
||||
CollectionJsonDocument(CollectionJson<T> collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonDocument} by copying the attributes and replacing the {@literal collection}.
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonDocument<T> withCollection(CollectionJson<T> collection) {
|
||||
return this.collection == collection ? this : new CollectionJsonDocument<T>(this.collection);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public CollectionJson<T> getCollection() {
|
||||
return this.collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJsonDocument<?> that = (CollectionJsonDocument<?>) o;
|
||||
return Objects.equals(this.collection, that.collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.collection);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "CollectionJsonDocument(collection=" + this.collection + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -27,17 +25,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@With(AccessLevel.PACKAGE)
|
||||
class CollectionJsonError {
|
||||
final class CollectionJsonError {
|
||||
|
||||
private String title;
|
||||
private String code;
|
||||
private String message;
|
||||
private final String title;
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonError(@JsonProperty("title") @Nullable String title, @JsonProperty("code") @Nullable String code,
|
||||
@JsonProperty("message") @Nullable String message) {
|
||||
@JsonProperty("message") @Nullable String message) {
|
||||
|
||||
this.title = title;
|
||||
this.code = code;
|
||||
@@ -48,4 +44,66 @@ class CollectionJsonError {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonError} by copying the attributes and replacing the {@literal title}.
|
||||
*
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonError withTitle(String title) {
|
||||
return this.title == title ? this : new CollectionJsonError(title, this.code, this.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonError} by copying the attributes and replacing the {@literal code}.
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonError withCode(String code) {
|
||||
return this.code == code ? this : new CollectionJsonError(this.title, code, this.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonError} by copying the attributes and replacing the {@literal message}.
|
||||
*
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonError withMessage(String message) {
|
||||
return this.message == message ? this : new CollectionJsonError(this.title, this.code, message);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJsonError that = (CollectionJsonError) o;
|
||||
return Objects.equals(this.title, that.title) && Objects.equals(this.code, that.code)
|
||||
&& Objects.equals(this.message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.title, this.code, this.message);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "CollectionJsonError(title=" + this.title + ", code=" + this.code + ", message=" + this.message + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,9 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -44,16 +39,17 @@ import com.fasterxml.jackson.databind.JavaType;
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With(AccessLevel.PACKAGE)
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
class CollectionJsonItem<T> {
|
||||
final class CollectionJsonItem<T> {
|
||||
|
||||
private @Nullable String href;
|
||||
private List<CollectionJsonData> data;
|
||||
private @JsonInclude(Include.NON_EMPTY) Links links;
|
||||
private @Nullable @Getter(onMethod = @__(@JsonIgnore)) T rawData;
|
||||
private @Nullable final String href;
|
||||
private final List<CollectionJsonData> data;
|
||||
private @JsonInclude(Include.NON_EMPTY) final Links links;
|
||||
private @Nullable final T rawData;
|
||||
|
||||
/**
|
||||
* Simple scalar types that can be encoded by value, not type.
|
||||
*/
|
||||
private final static Set<Class<?>> PRIMITIVE_TYPES = Collections.singleton(String.class);
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonItem(@JsonProperty("href") @Nullable String href, //
|
||||
@@ -70,10 +66,86 @@ class CollectionJsonItem<T> {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
CollectionJsonItem(String href, List<CollectionJsonData> data, Links links, T rawData) {
|
||||
|
||||
this.href = href;
|
||||
this.data = data;
|
||||
this.links = links;
|
||||
this.rawData = rawData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple scalar types that can be encoded by value, not type.
|
||||
* Create new {@link CollectionJsonItem} by copying attributes and replacing the {@link Link}s.
|
||||
*
|
||||
* @param links
|
||||
* @return
|
||||
*/
|
||||
private final static Set<Class<?>> PRIMITIVE_TYPES = Collections.singleton(String.class);
|
||||
CollectionJsonItem<T> withLinks(Link... links) {
|
||||
return new CollectionJsonItem<>(this.href, this.data, Links.of(links), this.rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionJsonItem} by copying attributes and replacing the {@link Links}.
|
||||
*
|
||||
* @param links
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonItem<T> withLinks(Links links) {
|
||||
return this.links == links ? this : new CollectionJsonItem<T>(this.href, this.data, links, this.rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionJsonItem} by copying attributes and replacing the {@literal links} with a
|
||||
* {@literal self} link.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonItem<T> withOwnSelfLink() {
|
||||
|
||||
String href = this.href;
|
||||
|
||||
if (href == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return withLinks(Links.of(Link.of(href)).merge(MergeMode.SKIP_BY_REL, links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionJsonItem} by copying attributes and replacing the {@literal href}.
|
||||
*
|
||||
* @param href
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonItem<T> withHref(@Nullable String href) {
|
||||
return this.href == href ? this : new CollectionJsonItem<T>(href, this.data, this.links, this.rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionJsonItem} by copying attributes and replacing the {@literal data}.
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonItem<T> withData(List<CollectionJsonData> data) {
|
||||
return this.data == data ? this : new CollectionJsonItem<T>(this.href, data, this.links, this.rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionJsonItem} by copying attributes and replacing the {@literal rawData}.
|
||||
*
|
||||
* @param rawData
|
||||
* @return
|
||||
*/
|
||||
CollectionJsonItem<T> withRawData(@Nullable T rawData) {
|
||||
return this.rawData == rawData ? this : new CollectionJsonItem<T>(this.href, this.data, this.links, rawData);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a domain object into a collection of {@link CollectionJsonData} objects to serialize properly.
|
||||
@@ -81,7 +153,7 @@ class CollectionJsonItem<T> {
|
||||
* @return
|
||||
*/
|
||||
@JsonProperty
|
||||
public List<CollectionJsonData> getData() {
|
||||
List<CollectionJsonData> getData() {
|
||||
|
||||
if (!this.data.isEmpty()) {
|
||||
return this.data;
|
||||
@@ -91,7 +163,7 @@ class CollectionJsonItem<T> {
|
||||
return Collections.singletonList(new CollectionJsonData().withValue(this.rawData));
|
||||
}
|
||||
|
||||
if (rawData == null) {
|
||||
if (this.rawData == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -102,14 +174,25 @@ class CollectionJsonItem<T> {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
Links getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@JsonIgnore
|
||||
T getRawData() {
|
||||
return this.rawData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an object used the deserialized properties and the provided type from the deserializer.
|
||||
* Generate an object using the deserialized properties and the provided type from the deserializer.
|
||||
*
|
||||
* @param javaType - type of the object to create
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public Object toRawData(JavaType javaType) {
|
||||
Object toRawData(JavaType javaType) {
|
||||
|
||||
if (this.data.isEmpty()) {
|
||||
return null;
|
||||
@@ -120,25 +203,31 @@ class CollectionJsonItem<T> {
|
||||
}
|
||||
|
||||
return PropertyUtils.createObjectFromProperties(javaType.getRawClass(), //
|
||||
this.data.stream().collect(Collectors.toMap(CollectionJsonData::getName, CollectionJsonData::getValue)));
|
||||
this.data.stream() //
|
||||
.collect(Collectors.toMap(CollectionJsonData::getName, CollectionJsonData::getValue)));
|
||||
}
|
||||
|
||||
public CollectionJsonItem<T> withLinks(Link... links) {
|
||||
return new CollectionJsonItem<>(href, data, Links.of(links), rawData);
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJsonItem<?> that = (CollectionJsonItem<?>) o;
|
||||
return Objects.equals(this.href, that.href) && Objects.equals(this.data, that.data)
|
||||
&& Objects.equals(this.links, that.links) && Objects.equals(this.rawData, that.rawData);
|
||||
}
|
||||
|
||||
public CollectionJsonItem<T> withLinks(Links links) {
|
||||
return new CollectionJsonItem<>(href, data, links, rawData);
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.href, this.data, this.links, this.rawData);
|
||||
}
|
||||
|
||||
public CollectionJsonItem<T> withOwnSelfLink() {
|
||||
public String toString() {
|
||||
|
||||
String href = this.href;
|
||||
|
||||
if (href == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return withLinks(Links.of(Link.of(href)).merge(MergeMode.SKIP_BY_REL, links));
|
||||
return "CollectionJsonItem(href=" + this.href + ", data=" + this.data + ", links=" + this.links + ", rawData="
|
||||
+ this.rawData + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -30,17 +28,12 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@With
|
||||
class CollectionJsonQuery {
|
||||
final class CollectionJsonQuery {
|
||||
|
||||
@JsonInclude(Include.NON_NULL) private String rel;
|
||||
|
||||
@JsonInclude(Include.NON_NULL) private String href;
|
||||
|
||||
@JsonInclude(Include.NON_NULL) private String prompt;
|
||||
|
||||
@JsonInclude(Include.NON_EMPTY) private List<CollectionJsonData> data;
|
||||
private @JsonInclude(Include.NON_NULL) final String rel;
|
||||
private @JsonInclude(Include.NON_NULL) final String href;
|
||||
private @JsonInclude(Include.NON_NULL) final String prompt;
|
||||
private @JsonInclude(Include.NON_EMPTY) final List<CollectionJsonData> data;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonQuery(@JsonProperty("rel") @Nullable String rel, @JsonProperty("href") @Nullable String href,
|
||||
@@ -55,4 +48,84 @@ class CollectionJsonQuery {
|
||||
CollectionJsonQuery() {
|
||||
this(null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonQuery} by copying attributes and replacing the {@literal rel}.
|
||||
*
|
||||
* @param rel
|
||||
* @return
|
||||
*/
|
||||
public CollectionJsonQuery withRel(String rel) {
|
||||
return this.rel == rel ? this : new CollectionJsonQuery(rel, this.href, this.prompt, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonQuery} by copying attributes and replacing the {@literal href}.
|
||||
*
|
||||
* @param href
|
||||
* @return
|
||||
*/
|
||||
public CollectionJsonQuery withHref(String href) {
|
||||
return this.href == href ? this : new CollectionJsonQuery(this.rel, href, this.prompt, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonQuery} by copying attributes and replacing the {@literal prompt}.
|
||||
*
|
||||
* @param prompt
|
||||
* @return
|
||||
*/
|
||||
public CollectionJsonQuery withPrompt(String prompt) {
|
||||
return this.prompt == prompt ? this : new CollectionJsonQuery(this.rel, this.href, prompt, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CollectionJsonQuery} by copying attributes and replacing the {@literal data}.
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public CollectionJsonQuery withData(List<CollectionJsonData> data) {
|
||||
return this.data == data ? this : new CollectionJsonQuery(this.rel, this.href, this.prompt, data);
|
||||
}
|
||||
|
||||
public String getRel() {
|
||||
return this.rel;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
public String getPrompt() {
|
||||
return this.prompt;
|
||||
}
|
||||
|
||||
public List<CollectionJsonData> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJsonQuery that = (CollectionJsonQuery) o;
|
||||
return Objects.equals(this.rel, that.rel) && Objects.equals(this.href, that.href)
|
||||
&& Objects.equals(this.prompt, that.prompt) && Objects.equals(this.data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.rel, this.href, this.prompt, this.data);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "CollectionJsonQuery(rel=" + this.rel + ", href=" + this.href + ", prompt=" + this.prompt + ", data="
|
||||
+ this.data + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -28,11 +26,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@With
|
||||
class CollectionJsonTemplate {
|
||||
final class CollectionJsonTemplate {
|
||||
|
||||
private List<CollectionJsonData> data;
|
||||
private final List<CollectionJsonData> data;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonTemplate(@JsonProperty("data") @Nullable List<CollectionJsonData> data) {
|
||||
@@ -42,4 +38,32 @@ class CollectionJsonTemplate {
|
||||
CollectionJsonTemplate() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public CollectionJsonTemplate withData(List<CollectionJsonData> data) {
|
||||
return this.data == data ? this : new CollectionJsonTemplate(data);
|
||||
}
|
||||
|
||||
public List<CollectionJsonData> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
CollectionJsonTemplate that = (CollectionJsonTemplate) o;
|
||||
return Objects.equals(this.data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.data);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "CollectionJsonTemplate(data=" + this.data + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
@@ -153,7 +151,7 @@ public class DefaultCurieProvider implements CurieProvider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final @Getter String name;
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public Curie(String name, String href) {
|
||||
@@ -161,5 +159,9 @@ public class DefaultCurieProvider implements CurieProvider {
|
||||
super(href, "curies");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -36,7 +31,6 @@ import org.springframework.util.PathMatcher;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class HalConfiguration {
|
||||
|
||||
private static final PathMatcher MATCHER = new AntPathMatcher();
|
||||
@@ -45,20 +39,20 @@ public class HalConfiguration {
|
||||
* Configures how to render links in case there is exactly one defined for a given link relation in general. By
|
||||
* default, this single link will be rendered as nested document.
|
||||
*/
|
||||
private final @With @Getter RenderSingleLinks renderSingleLinks;
|
||||
private final @With(AccessLevel.PRIVATE) Map<String, RenderSingleLinks> singleLinksPerPattern;
|
||||
private final RenderSingleLinks renderSingleLinks;
|
||||
private final Map<String, RenderSingleLinks> singleLinksPerPattern;
|
||||
|
||||
/**
|
||||
* Configures whether the Jackson property naming strategy is applied to link relations and within {@code _embedded}
|
||||
* clauses.
|
||||
*/
|
||||
private final @With @Getter boolean applyPropertyNamingStrategy;
|
||||
private final boolean applyPropertyNamingStrategy;
|
||||
|
||||
/**
|
||||
* Configures whether to always use collections for embeddeds, even if there's only one entry for a link relation.
|
||||
* Defaults to {@literal true}.
|
||||
*/
|
||||
private final @With @Getter boolean enforceEmbeddedCollections;
|
||||
private final boolean enforceEmbeddedCollections;
|
||||
|
||||
/**
|
||||
* Creates a new default {@link HalConfiguration} rendering single links as immediate sub-document.
|
||||
@@ -71,6 +65,15 @@ public class HalConfiguration {
|
||||
this.enforceEmbeddedCollections = true;
|
||||
}
|
||||
|
||||
private HalConfiguration(RenderSingleLinks renderSingleLinks, Map<String, RenderSingleLinks> singleLinksPerPattern,
|
||||
boolean applyPropertyNamingStrategy, boolean enforceEmbeddedCollections) {
|
||||
|
||||
this.renderSingleLinks = renderSingleLinks;
|
||||
this.singleLinksPerPattern = singleLinksPerPattern;
|
||||
this.applyPropertyNamingStrategy = applyPropertyNamingStrategy;
|
||||
this.enforceEmbeddedCollections = enforceEmbeddedCollections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures how to render a single link for a given particular {@link LinkRelation}. This will override what has
|
||||
* been configured via {@link #withRenderSingleLinks(RenderSingleLinks)} for that particular link relation.
|
||||
@@ -119,6 +122,72 @@ public class HalConfiguration {
|
||||
.orElse(renderSingleLinks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalConfiguration} by copying the attributes and replacing the {@literal renderSingleLinks}.
|
||||
*
|
||||
* @param renderSingleLinks
|
||||
* @return
|
||||
*/
|
||||
public HalConfiguration withRenderSingleLinks(RenderSingleLinks renderSingleLinks) {
|
||||
|
||||
return this.renderSingleLinks == renderSingleLinks ? this
|
||||
: new HalConfiguration(renderSingleLinks, this.singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalConfiguration} by copying the attributes and replacing the {@literal singleLinksPattern}.
|
||||
*
|
||||
* @param singleLinksPerPattern
|
||||
* @return
|
||||
*/
|
||||
private HalConfiguration withSingleLinksPerPattern(Map<String, RenderSingleLinks> singleLinksPerPattern) {
|
||||
|
||||
return this.singleLinksPerPattern == singleLinksPerPattern ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalConfiguration} by copying the attributes and replacing the
|
||||
* {@literal applyProperNamingStrategy}.
|
||||
*
|
||||
* @param applyPropertyNamingStrategy
|
||||
* @return
|
||||
*/
|
||||
public HalConfiguration withApplyPropertyNamingStrategy(boolean applyPropertyNamingStrategy) {
|
||||
|
||||
return this.applyPropertyNamingStrategy == applyPropertyNamingStrategy ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, this.singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalConfiguration} by copying the attributes and replacing the
|
||||
* {@literal enforceEmbeddedCollections}.
|
||||
*
|
||||
* @param enforceEmbeddedCollections
|
||||
* @return
|
||||
*/
|
||||
public HalConfiguration withEnforceEmbeddedCollections(boolean enforceEmbeddedCollections) {
|
||||
|
||||
return this.enforceEmbeddedCollections == enforceEmbeddedCollections ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, this.singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections);
|
||||
}
|
||||
|
||||
public RenderSingleLinks getRenderSingleLinks() {
|
||||
return this.renderSingleLinks;
|
||||
}
|
||||
|
||||
public boolean isApplyPropertyNamingStrategy() {
|
||||
return this.applyPropertyNamingStrategy;
|
||||
}
|
||||
|
||||
public boolean isEnforceEmbeddedCollections() {
|
||||
return this.enforceEmbeddedCollections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration option how to render single links of a given {@link LinkRelation}.
|
||||
*
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -42,13 +38,12 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @author Dietrich Schulten
|
||||
*/
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
class HalEmbeddedBuilder {
|
||||
|
||||
private static final String INVALID_EMBEDDED_WRAPPER = "Embedded wrapper %s returned null for both the static rel and the rel target type! Make sure one of the two returns a non-null value!";
|
||||
private static final Function<String, String> NO_TRANSFORMER = Function.identity();
|
||||
|
||||
private final Map<HalLinkRelation, Object> embeddeds = new HashMap<>();
|
||||
private final Map<HalLinkRelation, Object> embeddeds = new LinkedHashMap<>(); // preserve ordering
|
||||
private final LinkRelationProvider provider;
|
||||
private final CurieProvider curieProvider;
|
||||
private final EmbeddedWrappers wrappers;
|
||||
@@ -56,7 +51,7 @@ class HalEmbeddedBuilder {
|
||||
/**
|
||||
* Returns a {@link HalEmbeddedBuilder} with the given transformer
|
||||
*/
|
||||
private final @With Function<String, String> relationTransformer;
|
||||
private final Function<String, String> relationTransformer;
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalEmbeddedBuilder} using the given {@link LinkRelationProvider} and prefer collection rels
|
||||
@@ -66,7 +61,7 @@ class HalEmbeddedBuilder {
|
||||
* @param curieProvider must not be {@literal null}.
|
||||
* @param preferCollectionRels whether to prefer to ask the provider for collection rels.
|
||||
*/
|
||||
public HalEmbeddedBuilder(LinkRelationProvider provider, CurieProvider curieProvider, boolean preferCollectionRels) {
|
||||
HalEmbeddedBuilder(LinkRelationProvider provider, CurieProvider curieProvider, boolean preferCollectionRels) {
|
||||
|
||||
Assert.notNull(provider, "LinkRelationProvider must not be null!");
|
||||
|
||||
@@ -76,13 +71,22 @@ class HalEmbeddedBuilder {
|
||||
this.relationTransformer = NO_TRANSFORMER;
|
||||
}
|
||||
|
||||
private HalEmbeddedBuilder(LinkRelationProvider provider, CurieProvider curieProvider, EmbeddedWrappers wrappers,
|
||||
Function<String, String> relationTransformer) {
|
||||
|
||||
this.provider = provider;
|
||||
this.curieProvider = curieProvider;
|
||||
this.wrappers = wrappers;
|
||||
this.relationTransformer = relationTransformer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value to the embeddeds. Will skip doing so if the value is {@literal null} or the content of a
|
||||
* {@link EntityModel} is {@literal null}.
|
||||
*
|
||||
* @param source can be {@literal null}.
|
||||
*/
|
||||
public void add(@Nullable Object source) {
|
||||
void add(@Nullable Object source) {
|
||||
|
||||
EmbeddedWrapper wrapper = wrappers.wrap(source);
|
||||
|
||||
@@ -150,7 +154,19 @@ class HalEmbeddedBuilder {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<HalLinkRelation, Object> asMap() {
|
||||
Map<HalLinkRelation, Object> asMap() {
|
||||
return Collections.unmodifiableMap(embeddeds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link HalEmbeddedBuilder} by copying attributes and replacing the {@literal relationTransformer}.
|
||||
*
|
||||
* @param relationTransformer
|
||||
* @return
|
||||
*/
|
||||
HalEmbeddedBuilder withRelationTransformer(Function<String, String> relationTransformer) {
|
||||
|
||||
return this.relationTransformer == relationTransformer ? this
|
||||
: new HalEmbeddedBuilder(this.provider, this.curieProvider, this.wrappers, relationTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -38,8 +33,6 @@ import com.fasterxml.jackson.annotation.JsonValue;
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
|
||||
public static final HalLinkRelation CURIES = HalLinkRelation.uncuried("curies");
|
||||
@@ -47,7 +40,15 @@ public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
private static final String RELATION_MESSAGE_TEMPLATE = "_links.%s.title";
|
||||
|
||||
private final @Nullable String curie;
|
||||
private final @NonNull @Getter String localPart;
|
||||
private final String localPart;
|
||||
|
||||
private HalLinkRelation(String curie, String localPart) {
|
||||
|
||||
Assert.notNull(localPart, "localPart must not be null!");
|
||||
|
||||
this.curie = curie;
|
||||
this.localPart = localPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link HalLinkRelation} for the given general {@link LinkRelation}.
|
||||
@@ -199,6 +200,10 @@ public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getLocalPart() {
|
||||
return this.localPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple builder interface to easily create multiple {@link HalLinkRelation}s for a single curie.
|
||||
*
|
||||
@@ -215,6 +220,22 @@ public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
HalLinkRelation relation(String relation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof HalLinkRelation))
|
||||
return false;
|
||||
HalLinkRelation that = (HalLinkRelation) o;
|
||||
return Objects.equals(this.curie, that.curie) && Objects.equals(this.localPart, that.localPart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.curie, this.localPart);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
@@ -41,7 +39,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RequiredArgsConstructor
|
||||
public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
|
||||
private final LinkRelationProvider relProvider;
|
||||
@@ -50,6 +47,17 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
private final @Qualifier("messageResolver") MessageResolver resolver;
|
||||
private final AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
public HalMediaTypeConfiguration(LinkRelationProvider relProvider, ObjectProvider<CurieProvider> curieProvider,
|
||||
ObjectProvider<HalConfiguration> halConfiguration, MessageResolver resolver,
|
||||
AutowireCapableBeanFactory beanFactory) {
|
||||
|
||||
this.relProvider = relProvider;
|
||||
this.curieProvider = curieProvider;
|
||||
this.halConfiguration = halConfiguration;
|
||||
this.resolver = resolver;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
LinkDiscoverer halLinkDisocoverer() {
|
||||
return new HalLinkDiscoverer();
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -319,7 +317,6 @@ public class HalModelBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class HalRepresentationModel<T> extends EntityModel<T> {
|
||||
|
||||
private final T entity;
|
||||
@@ -327,12 +324,17 @@ public class HalModelBuilder {
|
||||
|
||||
public HalRepresentationModel(@Nullable T entity, CollectionModel<T> embeddeds, Links links) {
|
||||
|
||||
this.entity = entity;
|
||||
this.embeddeds = embeddeds;
|
||||
this(entity, embeddeds);
|
||||
|
||||
add(links);
|
||||
}
|
||||
|
||||
public HalRepresentationModel(T entity, CollectionModel<?> embeddeds) {
|
||||
|
||||
this.entity = entity;
|
||||
this.embeddeds = embeddeds;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityModel#getContent()
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
@@ -53,8 +49,20 @@ import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.KeyDeserializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy.PropertyNamingStrategyBase;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
|
||||
import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
||||
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
|
||||
@@ -881,18 +889,36 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static class EmbeddedMapper {
|
||||
|
||||
private static final Function<String, String> NO_OP = Function.identity();
|
||||
|
||||
private final @lombok.NonNull LinkRelationProvider relProvider;
|
||||
private final LinkRelationProvider relProvider;
|
||||
private final CurieProvider curieProvider;
|
||||
private final boolean preferCollectionRels;
|
||||
|
||||
private Function<String, String> relationTransformer = Function.identity();
|
||||
|
||||
public EmbeddedMapper(LinkRelationProvider relProvider, CurieProvider curieProvider, boolean preferCollectionRels) {
|
||||
|
||||
Assert.notNull(relProvider, "relProvider must not be null!");
|
||||
|
||||
this.relProvider = relProvider;
|
||||
this.curieProvider = curieProvider;
|
||||
this.preferCollectionRels = preferCollectionRels;
|
||||
}
|
||||
|
||||
private EmbeddedMapper(LinkRelationProvider relProvider, CurieProvider curieProvider, boolean preferCollectionRels,
|
||||
Function<String, String> relationTransformer) {
|
||||
|
||||
Assert.notNull(relProvider, "relProvider must not be null!");
|
||||
|
||||
this.relProvider = relProvider;
|
||||
this.curieProvider = curieProvider;
|
||||
this.preferCollectionRels = preferCollectionRels;
|
||||
this.relationTransformer = relationTransformer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@link PropertyNamingStrategy} with the current mapper to forward that strategy as relation
|
||||
* transformer, so that {@link LinkRelation}s used as key for the embedding will be transformed using the given
|
||||
|
||||
@@ -17,12 +17,10 @@ package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -39,12 +37,11 @@ import org.springframework.http.MediaType;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
class HalFormsAffordanceModel extends AffordanceModel {
|
||||
|
||||
private static final Set<HttpMethod> ENTITY_ALTERING_METHODS = EnumSet.of(POST, PUT, PATCH);
|
||||
|
||||
private final @Getter List<HalFormsProperty> inputProperties;
|
||||
private final List<HalFormsProperty> inputProperties;
|
||||
|
||||
public HalFormsAffordanceModel(String name, Link link, HttpMethod httpMethod, InputPayloadMetadata inputType,
|
||||
List<QueryParameter> queryMethodParameters, PayloadMetadata outputType) {
|
||||
@@ -70,4 +67,26 @@ class HalFormsAffordanceModel extends AffordanceModel {
|
||||
.withName(it)) //
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<HalFormsProperty> getInputProperties() {
|
||||
return this.inputProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof HalFormsAffordanceModel))
|
||||
return false;
|
||||
if (!super.equals(o))
|
||||
return false;
|
||||
HalFormsAffordanceModel that = (HalFormsAffordanceModel) o;
|
||||
return Objects.equals(this.inputProperties, that.inputProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), inputProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.hateoas.AffordanceModel;
|
||||
@@ -37,7 +35,7 @@ import org.springframework.http.MediaType;
|
||||
*/
|
||||
class HalFormsAffordanceModelFactory implements AffordanceModelFactory {
|
||||
|
||||
private final @Getter MediaType mediaType = MediaTypes.HAL_FORMS_JSON;
|
||||
private final MediaType mediaType = MediaTypes.HAL_FORMS_JSON;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -48,4 +46,8 @@ class HalFormsAffordanceModelFactory implements AffordanceModelFactory {
|
||||
InputPayloadMetadata inputType, List<QueryParameter> parameters, PayloadMetadata outputType) {
|
||||
return new HalFormsAffordanceModel(name, link, httpMethod, inputType, parameters, outputType);
|
||||
}
|
||||
|
||||
public MediaType getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -31,10 +28,9 @@ import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class HalFormsConfiguration {
|
||||
|
||||
private final @Getter HalConfiguration halConfiguration;
|
||||
private final HalConfiguration halConfiguration;
|
||||
private final Map<Class<?>, String> patterns = new HashMap<>();
|
||||
|
||||
/**
|
||||
@@ -44,6 +40,10 @@ public class HalFormsConfiguration {
|
||||
this.halConfiguration = new HalConfiguration();
|
||||
}
|
||||
|
||||
public HalFormsConfiguration(HalConfiguration halConfiguration) {
|
||||
this.halConfiguration = halConfiguration;
|
||||
}
|
||||
|
||||
public HalFormsConfiguration registerPattern(Class<?> type, String pattern) {
|
||||
|
||||
patterns.put(type, pattern);
|
||||
@@ -60,4 +60,8 @@ public class HalFormsConfiguration {
|
||||
Optional<String> getTypePatternFor(ResolvableType type) {
|
||||
return Optional.ofNullable(patterns.get(type.resolve(Object.class)));
|
||||
}
|
||||
|
||||
public HalConfiguration getHalConfiguration() {
|
||||
return this.halConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,11 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
@@ -55,46 +50,54 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
@With
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
@JsonPropertyOrder({ "attributes", "entity", "entities", "embedded", "links", "templates", "metadata" })
|
||||
public class HalFormsDocument<T> {
|
||||
final class HalFormsDocument<T> {
|
||||
|
||||
@Nullable //
|
||||
@Getter(onMethod = @__(@JsonAnyGetter)) @JsonInclude(Include.NON_EMPTY) //
|
||||
@With(AccessLevel.PRIVATE) //
|
||||
private Map<String, Object> attributes;
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
@Nullable //
|
||||
@JsonUnwrapped //
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
private T entity;
|
||||
private final T entity;
|
||||
|
||||
@Nullable //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
@JsonIgnore //
|
||||
@With(AccessLevel.PRIVATE) //
|
||||
private Collection<T> entities;
|
||||
private final Collection<T> entities;
|
||||
|
||||
@JsonProperty("_embedded") //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
private Map<HalLinkRelation, Object> embedded;
|
||||
private final Map<HalLinkRelation, Object> embedded;
|
||||
|
||||
@Nullable //
|
||||
@JsonProperty("page") //
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
private PagedModel.PageMetadata pageMetadata;
|
||||
private final PagedModel.PageMetadata pageMetadata;
|
||||
|
||||
@JsonProperty("_links") //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
@JsonSerialize(using = HalLinkListSerializer.class) //
|
||||
@JsonDeserialize(using = HalFormsLinksDeserializer.class) //
|
||||
private Links links;
|
||||
private final Links links;
|
||||
|
||||
@JsonProperty("_templates") //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
private Map<String, HalFormsTemplate> templates;
|
||||
private final Map<String, HalFormsTemplate> templates;
|
||||
|
||||
HalFormsDocument(Map<String, Object> attributes, T entity, Collection<T> entities,
|
||||
Map<HalLinkRelation, Object> embedded, PageMetadata pageMetadata, Links links,
|
||||
Map<String, HalFormsTemplate> templates) {
|
||||
|
||||
this.attributes = attributes;
|
||||
this.entity = entity;
|
||||
this.entities = entities;
|
||||
this.embedded = embedded;
|
||||
this.pageMetadata = pageMetadata;
|
||||
this.links = links;
|
||||
this.templates = templates;
|
||||
}
|
||||
|
||||
private HalFormsDocument() {
|
||||
this(null, null, null, Collections.emptyMap(), null, Links.NONE, Collections.emptyMap());
|
||||
@@ -106,7 +109,7 @@ public class HalFormsDocument<T> {
|
||||
* @param model can be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public static HalFormsDocument<?> forRepresentationModel(RepresentationModel<?> model) {
|
||||
static HalFormsDocument<?> forRepresentationModel(RepresentationModel<?> model) {
|
||||
|
||||
Map<String, Object> attributes = PropertyUtils.extractPropertyValues(model);
|
||||
attributes.remove("links");
|
||||
@@ -120,7 +123,7 @@ public class HalFormsDocument<T> {
|
||||
* @param resource can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> HalFormsDocument<T> forEntity(@Nullable T resource) {
|
||||
static <T> HalFormsDocument<T> forEntity(@Nullable T resource) {
|
||||
return new HalFormsDocument<T>().withEntity(resource);
|
||||
}
|
||||
|
||||
@@ -130,7 +133,7 @@ public class HalFormsDocument<T> {
|
||||
* @param entities must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> HalFormsDocument<T> forEntities(Collection<T> entities) {
|
||||
static <T> HalFormsDocument<T> forEntities(Collection<T> entities) {
|
||||
|
||||
Assert.notNull(entities, "Resources must not be null!");
|
||||
|
||||
@@ -142,18 +145,184 @@ public class HalFormsDocument<T> {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static HalFormsDocument<?> empty() {
|
||||
static HalFormsDocument<?> empty() {
|
||||
return new HalFormsDocument<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default template of the document.
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing the {@literal attributes}.
|
||||
*
|
||||
* @param attributes
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
public HalFormsTemplate getDefaultTemplate() {
|
||||
return getTemplate(HalFormsTemplate.DEFAULT_KEY);
|
||||
private HalFormsDocument<T> withAttributes(@Nullable Map<String, Object> attributes) {
|
||||
|
||||
return this.attributes == attributes ? this
|
||||
: new HalFormsDocument<T>(attributes, this.entity, this.entities, this.embedded, this.pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing {@literal entity}.
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
private HalFormsDocument<T> withEntity(@Nullable T entity) {
|
||||
|
||||
return this.entity == entity ? this
|
||||
: new HalFormsDocument<T>(this.attributes, entity, this.entities, this.embedded, this.pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing the {@literal entities}.
|
||||
*
|
||||
* @param entities
|
||||
* @return
|
||||
*/
|
||||
private HalFormsDocument<T> withEntities(@Nullable Collection<T> entities) {
|
||||
|
||||
return this.entities == entities ? this
|
||||
: new HalFormsDocument<T>(this.attributes, this.entity, entities, this.embedded, this.pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying the attributes and adding a new embedded value.
|
||||
*
|
||||
* @param key must not be {@literal null} or empty.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> andEmbedded(HalLinkRelation key, Object value) {
|
||||
|
||||
Assert.notNull(key, "Embedded key must not be null!");
|
||||
Assert.notNull(value, "Embedded value must not be null!");
|
||||
|
||||
Map<HalLinkRelation, Object> embedded = new HashMap<>(this.embedded);
|
||||
embedded.put(key, value);
|
||||
|
||||
return new HalFormsDocument<>(this.attributes, this.entity, this.entities, embedded, this.pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing all {@literal embedded}s.
|
||||
*
|
||||
* @param embedded
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> withEmbedded(Map<HalLinkRelation, Object> embedded) {
|
||||
|
||||
return this.embedded == embedded ? this
|
||||
: new HalFormsDocument<T>(this.attributes, this.entity, this.entities, embedded, this.pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing the {@literal pageMetadata}.
|
||||
*
|
||||
* @param pageMetadata
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> withPageMetadata(@Nullable PageMetadata pageMetadata) {
|
||||
|
||||
return this.pageMetadata == pageMetadata ? this
|
||||
: new HalFormsDocument<T>(this.attributes, this.entity, this.entities, this.embedded, pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying the attributes and adding a new {@link Link}.
|
||||
*
|
||||
* @param link must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> andLink(Link link) {
|
||||
|
||||
Assert.notNull(link, "Link must not be null!");
|
||||
|
||||
return new HalFormsDocument<>(this.attributes, this.entity, this.entities, this.embedded, this.pageMetadata,
|
||||
this.links.and(link), this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing the {@literal links}.
|
||||
*
|
||||
* @param links
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> withLinks(Links links) {
|
||||
|
||||
return this.links == links ? this
|
||||
: new HalFormsDocument<T>(this.attributes, this.entity, this.entities, this.embedded, this.pageMetadata, links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying the attributes and adding a new {@link HalFormsTemplate}.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param template must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> andTemplate(String name, HalFormsTemplate template) {
|
||||
|
||||
Assert.hasText(name, "Template name must not be null or empty!");
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
Map<String, HalFormsTemplate> templates = new HashMap<>(this.templates);
|
||||
templates.put(name, template);
|
||||
|
||||
return new HalFormsDocument<>(this.attributes, this.entity, this.entities, this.embedded, this.pageMetadata,
|
||||
this.links, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsDocument} by copying attributes and replacing the {@literal templates}.
|
||||
*
|
||||
* @param templates
|
||||
* @return
|
||||
*/
|
||||
HalFormsDocument<T> withTemplates(Map<String, HalFormsTemplate> templates) {
|
||||
|
||||
return this.templates == templates ? this
|
||||
: new HalFormsDocument<T>(this.attributes, this.entity, this.entities, this.embedded, this.pageMetadata,
|
||||
this.links, templates);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@JsonAnyGetter
|
||||
Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
T getEntity() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Collection<T> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
Map<HalLinkRelation, Object> getEmbedded() {
|
||||
return this.embedded;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
PageMetadata getPageMetadata() {
|
||||
return this.pageMetadata;
|
||||
}
|
||||
|
||||
Links getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
Map<String, HalFormsTemplate> getTemplates() {
|
||||
return this.templates;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,67 +332,48 @@ public class HalFormsDocument<T> {
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
public HalFormsTemplate getTemplate(String key) {
|
||||
HalFormsTemplate getTemplate(String key) {
|
||||
|
||||
Assert.notNull(key, "Template key must not be null!");
|
||||
|
||||
return this.templates.get(key);
|
||||
}
|
||||
|
||||
public HalFormsDocument<T> withPageMetadata(@Nullable PageMetadata metadata) {
|
||||
return new HalFormsDocument<>(attributes, entity, entities, embedded, metadata, links, templates);
|
||||
}
|
||||
|
||||
private HalFormsDocument<T> withEntity(@Nullable T entity) {
|
||||
return new HalFormsDocument<>(attributes, entity, entities, embedded, pageMetadata, links, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given {@link Link} to the current document.
|
||||
* Returns the default template of the document.
|
||||
*
|
||||
* @param link must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public HalFormsDocument<T> andLink(Link link) {
|
||||
|
||||
Assert.notNull(link, "Link must not be null!");
|
||||
|
||||
return new HalFormsDocument<>(attributes, entity, entities, embedded, pageMetadata, links.and(link), templates);
|
||||
@JsonIgnore
|
||||
HalFormsTemplate getDefaultTemplate() {
|
||||
return getTemplate(HalFormsTemplate.DEFAULT_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given {@link HalFormsTemplate} to the current document.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param template must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public HalFormsDocument<T> andTemplate(String name, HalFormsTemplate template) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
Assert.hasText(name, "Template name must not be null or empty!");
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
Map<String, HalFormsTemplate> templates = new HashMap<>(this.templates);
|
||||
templates.put(name, template);
|
||||
|
||||
return new HalFormsDocument<>(attributes, entity, entities, embedded, pageMetadata, links, templates);
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof HalFormsDocument))
|
||||
return false;
|
||||
HalFormsDocument<?> that = (HalFormsDocument<?>) o;
|
||||
return Objects.equals(this.attributes, that.attributes) && Objects.equals(this.entity, that.entity)
|
||||
&& Objects.equals(this.entities, that.entities) && Objects.equals(this.embedded, that.embedded)
|
||||
&& Objects.equals(this.pageMetadata, that.pageMetadata) && Objects.equals(this.links, that.links)
|
||||
&& Objects.equals(this.templates, that.templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value as embedded one.
|
||||
*
|
||||
* @param key must not be {@literal null} or empty.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public HalFormsDocument<T> andEmbedded(HalLinkRelation key, Object value) {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
Assert.notNull(key, "Embedded key must not be null!");
|
||||
Assert.notNull(value, "Embedded value must not be null!");
|
||||
return Objects.hash(this.attributes, this.entity, this.entities, this.embedded, this.pageMetadata, this.links,
|
||||
this.templates);
|
||||
}
|
||||
|
||||
Map<HalLinkRelation, Object> embedded = new HashMap<>(this.embedded);
|
||||
embedded.put(key, value);
|
||||
public String toString() {
|
||||
|
||||
return new HalFormsDocument<>(attributes, entity, entities, embedded, pageMetadata, links, templates);
|
||||
return "HalFormsDocument(attributes=" + this.attributes + ", entity=" + this.entity + ", entities=" + this.entities
|
||||
+ ", embedded=" + this.embedded + ", pageMetadata=" + this.pageMetadata + ", links=" + this.links
|
||||
+ ", templates=" + this.templates + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
@@ -42,7 +40,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
|
||||
private final DelegatingLinkRelationProvider relProvider;
|
||||
@@ -52,20 +49,24 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
private final MessageResolver resolver;
|
||||
private final AbstractAutowireCapableBeanFactory beanFactory;
|
||||
|
||||
public HalFormsMediaTypeConfiguration(DelegatingLinkRelationProvider relProvider,
|
||||
ObjectProvider<CurieProvider> curieProvider, ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
|
||||
ObjectProvider<HalConfiguration> halConfiguration, MessageResolver resolver,
|
||||
AbstractAutowireCapableBeanFactory beanFactory) {
|
||||
|
||||
this.relProvider = relProvider;
|
||||
this.curieProvider = curieProvider;
|
||||
this.halFormsConfiguration = halFormsConfiguration;
|
||||
this.halConfiguration = halConfiguration;
|
||||
this.resolver = resolver;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
LinkDiscoverer halFormsLinkDiscoverer() {
|
||||
return new HalFormsLinkDiscoverer();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getMediaTypes()
|
||||
*/
|
||||
@Override
|
||||
public List<MediaType> getMediaTypes() {
|
||||
return HypermediaType.HAL_FORMS.getMediaTypes();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.config.HypermediaMappingInformation#configureObjectMapper(com.fasterxml.jackson.databind.ObjectMapper)
|
||||
@@ -83,4 +84,13 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getMediaTypes()
|
||||
*/
|
||||
@Override
|
||||
public List<MediaType> getMediaTypes() {
|
||||
return HypermediaType.HAL_FORMS.getMediaTypes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,12 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.AffordanceModel.Named;
|
||||
import org.springframework.hateoas.AffordanceModel.PropertyMetadata;
|
||||
import org.springframework.hateoas.AffordanceModel.PropertyMetadataConfigured;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
@@ -39,22 +33,43 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @see https://mamund.site44.com/misc/hal-forms/
|
||||
*/
|
||||
@JsonInclude(Include.NON_DEFAULT)
|
||||
@Value
|
||||
@With
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@NoArgsConstructor(force = true)
|
||||
@ToString
|
||||
public class HalFormsProperty implements PropertyMetadataConfigured<HalFormsProperty>, Named {
|
||||
final class HalFormsProperty implements PropertyMetadataConfigured<HalFormsProperty>, Named {
|
||||
|
||||
private @NonNull String name;
|
||||
private @JsonInclude(Include.NON_DEFAULT) boolean readOnly;
|
||||
private String value;
|
||||
private @JsonInclude(Include.NON_EMPTY) String prompt;
|
||||
private String regex;
|
||||
private boolean templated;
|
||||
private @JsonInclude(Include.NON_DEFAULT) boolean required;
|
||||
private boolean multi;
|
||||
private final String name;
|
||||
private @JsonInclude(Include.NON_DEFAULT) final boolean readOnly;
|
||||
private final String value;
|
||||
private @JsonInclude(Include.NON_EMPTY) final String prompt;
|
||||
private final String regex;
|
||||
private final boolean templated;
|
||||
private @JsonInclude(Include.NON_DEFAULT) final boolean required;
|
||||
private final boolean multi;
|
||||
|
||||
HalFormsProperty() {
|
||||
|
||||
this.name = null;
|
||||
this.readOnly = false;
|
||||
this.value = null;
|
||||
this.prompt = null;
|
||||
this.regex = null;
|
||||
this.templated = false;
|
||||
this.required = false;
|
||||
this.multi = false;
|
||||
}
|
||||
|
||||
private HalFormsProperty(String name, boolean readOnly, String value, String prompt, String regex, boolean templated,
|
||||
boolean required, boolean multi) {
|
||||
|
||||
Assert.notNull(name, "name must not be null!");
|
||||
|
||||
this.name = name;
|
||||
this.readOnly = readOnly;
|
||||
this.value = value;
|
||||
this.prompt = prompt;
|
||||
this.regex = regex;
|
||||
this.templated = templated;
|
||||
this.required = required;
|
||||
this.multi = multi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalFormsProperty} with the given name.
|
||||
@@ -62,7 +77,7 @@ public class HalFormsProperty implements PropertyMetadataConfigured<HalFormsProp
|
||||
* @param name must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static HalFormsProperty named(String name) {
|
||||
static HalFormsProperty named(String name) {
|
||||
return new HalFormsProperty().withName(name);
|
||||
}
|
||||
|
||||
@@ -79,4 +94,177 @@ public class HalFormsProperty implements PropertyMetadataConfigured<HalFormsProp
|
||||
.map(customized::withRegex) //
|
||||
.orElse(customized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing the {@literal name}.
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withName(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null!");
|
||||
|
||||
return this.name == name ? this
|
||||
: new HalFormsProperty(name, this.readOnly, this.value, this.prompt, this.regex, this.templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing the {@literal readOnly}.
|
||||
*
|
||||
* @param readOnly
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withReadOnly(boolean readOnly) {
|
||||
|
||||
return this.readOnly == readOnly ? this
|
||||
: new HalFormsProperty(this.name, readOnly, this.value, this.prompt, this.regex, this.templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing the {@literal value}.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withValue(String value) {
|
||||
|
||||
return this.value == value ? this
|
||||
: new HalFormsProperty(this.name, this.readOnly, value, this.prompt, this.regex, this.templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing the {@literal prompt}.
|
||||
*
|
||||
* @param prompt
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withPrompt(String prompt) {
|
||||
|
||||
return this.prompt == prompt ? this
|
||||
: new HalFormsProperty(this.name, this.readOnly, this.value, prompt, this.regex, this.templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing the {@literal regex}.
|
||||
*
|
||||
* @param regex
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withRegex(String regex) {
|
||||
|
||||
return this.regex == regex ? this
|
||||
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, regex, this.templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing {@literal templated}.
|
||||
*
|
||||
* @param templated
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withTemplated(boolean templated) {
|
||||
|
||||
return this.templated == templated ? this
|
||||
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, this.regex, templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing {@literal required}.
|
||||
*
|
||||
* @param required
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withRequired(boolean required) {
|
||||
|
||||
return this.required == required ? this
|
||||
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, this.regex, this.templated, required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalFormsProperty} by copying attributes and replacing {@literal multi}.
|
||||
*
|
||||
* @param multi
|
||||
* @return
|
||||
*/
|
||||
HalFormsProperty withMulti(boolean multi) {
|
||||
|
||||
return this.multi == multi ? this
|
||||
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, this.regex, this.templated,
|
||||
this.required, multi);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
boolean isReadOnly() {
|
||||
return this.readOnly;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
String getPrompt() {
|
||||
return this.prompt;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
String getRegex() {
|
||||
return this.regex;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
boolean isTemplated() {
|
||||
return this.templated;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
boolean isRequired() {
|
||||
return this.required;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
boolean isMulti() {
|
||||
return this.multi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof HalFormsProperty))
|
||||
return false;
|
||||
HalFormsProperty that = (HalFormsProperty) o;
|
||||
return this.readOnly == that.readOnly && this.templated == that.templated && this.required == that.required
|
||||
&& this.multi == that.multi && Objects.equals(this.name, that.name) && Objects.equals(this.value, that.value)
|
||||
&& Objects.equals(this.prompt, that.prompt) && Objects.equals(this.regex, that.regex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return Objects.hash(this.name, this.readOnly, this.value, this.prompt, this.regex, this.templated, this.required,
|
||||
this.multi);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "HalFormsProperty(name=" + this.name + ", readOnly=" + this.readOnly + ", value=" + this.value + ", prompt="
|
||||
+ this.prompt + ", regex=" + this.regex + ", templated=" + this.templated + ", required=" + this.required
|
||||
+ ", multi=" + this.multi + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,10 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.hateoas.mediatype.hal.forms.HalFormsDeserializers.MediaTypesDeserializer;
|
||||
@@ -51,21 +43,15 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
* @author Greg Turnquist
|
||||
* @see https://rwcbook.github.io/hal-forms/#_the_code__templates_code_element
|
||||
*/
|
||||
@Data
|
||||
@Setter(AccessLevel.NONE)
|
||||
@With
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@JsonAutoDetect(getterVisibility = Visibility.NON_PRIVATE)
|
||||
@JsonIgnoreProperties({ "httpMethod", "contentTypes" })
|
||||
@JsonPropertyOrder({ "title", "method", "contentType", "properties" })
|
||||
public class HalFormsTemplate {
|
||||
final class HalFormsTemplate {
|
||||
|
||||
public static final String DEFAULT_KEY = "default";
|
||||
static final String DEFAULT_KEY = "default";
|
||||
|
||||
private @Getter(onMethod = @__(@JsonInclude(Include.NON_EMPTY))) String title;
|
||||
private @With(AccessLevel.PRIVATE) HttpMethod httpMethod;
|
||||
private String title;
|
||||
private HttpMethod httpMethod;
|
||||
private List<HalFormsProperty> properties;
|
||||
private List<MediaType> contentTypes;
|
||||
|
||||
@@ -74,17 +60,38 @@ public class HalFormsTemplate {
|
||||
this(null, null, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public static HalFormsTemplate forMethod(HttpMethod httpMethod) {
|
||||
private HalFormsTemplate(String title, HttpMethod httpMethod, List<HalFormsProperty> properties,
|
||||
List<MediaType> contentTypes) {
|
||||
|
||||
this.title = title;
|
||||
this.httpMethod = httpMethod;
|
||||
this.properties = properties;
|
||||
this.contentTypes = contentTypes;
|
||||
}
|
||||
|
||||
static HalFormsTemplate forMethod(HttpMethod httpMethod) {
|
||||
return new HalFormsTemplate().withHttpMethod(httpMethod);
|
||||
}
|
||||
|
||||
HalFormsTemplate withTitle(String title) {
|
||||
|
||||
return this.title == title ? this
|
||||
: new HalFormsTemplate(title, this.httpMethod, this.properties, this.contentTypes);
|
||||
}
|
||||
|
||||
private HalFormsTemplate withHttpMethod(HttpMethod httpMethod) {
|
||||
|
||||
return this.httpMethod == httpMethod ? this
|
||||
: new HalFormsTemplate(this.title, httpMethod, this.properties, this.contentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link HalFormsTemplate} with the given {@link HalFormsProperty} added.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public HalFormsTemplate andProperty(HalFormsProperty property) {
|
||||
HalFormsTemplate andProperty(HalFormsProperty property) {
|
||||
|
||||
Assert.notNull(property, "Property must not be null!");
|
||||
|
||||
@@ -94,13 +101,19 @@ public class HalFormsTemplate {
|
||||
return new HalFormsTemplate(title, httpMethod, properties, contentTypes);
|
||||
}
|
||||
|
||||
HalFormsTemplate withProperties(List<HalFormsProperty> properties) {
|
||||
|
||||
return this.properties == properties ? this
|
||||
: new HalFormsTemplate(this.title, this.httpMethod, properties, this.contentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link HalFormsTemplate} with the given {@link MediaType} added as content type.
|
||||
*
|
||||
* @param mediaType must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public HalFormsTemplate andContentType(MediaType mediaType) {
|
||||
HalFormsTemplate andContentType(MediaType mediaType) {
|
||||
|
||||
Assert.notNull(mediaType, "Media type must not be null!");
|
||||
|
||||
@@ -110,6 +123,12 @@ public class HalFormsTemplate {
|
||||
return new HalFormsTemplate(title, httpMethod, properties, contentTypes);
|
||||
}
|
||||
|
||||
HalFormsTemplate withContentTypes(List<MediaType> contentTypes) {
|
||||
|
||||
return this.contentTypes == contentTypes ? this
|
||||
: new HalFormsTemplate(this.title, this.httpMethod, this.properties, contentTypes);
|
||||
}
|
||||
|
||||
// Jackson helper methods to create the right representation format
|
||||
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
@@ -132,6 +151,48 @@ public class HalFormsTemplate {
|
||||
}
|
||||
|
||||
Optional<HalFormsProperty> getPropertyByName(String name) {
|
||||
return properties.stream().filter(it -> it.getName().equals(name)).findFirst();
|
||||
|
||||
return properties.stream() //
|
||||
.filter(it -> it.getName().equals(name)) //
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
HttpMethod getHttpMethod() {
|
||||
return this.httpMethod;
|
||||
}
|
||||
|
||||
List<HalFormsProperty> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
List<MediaType> getContentTypes() {
|
||||
return this.contentTypes;
|
||||
}
|
||||
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof HalFormsTemplate))
|
||||
return false;
|
||||
HalFormsTemplate that = (HalFormsTemplate) o;
|
||||
return Objects.equals(this.title, that.title) && this.httpMethod == that.httpMethod
|
||||
&& Objects.equals(this.properties, that.properties) && Objects.equals(this.contentTypes, that.contentTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.title, this.httpMethod, this.properties, this.contentTypes);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "HalFormsTemplate(title=" + this.title + ", httpMethod=" + this.httpMethod + ", properties="
|
||||
+ this.properties + ", contentTypes=" + this.contentTypes + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -40,12 +38,17 @@ import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class HalFormsTemplateBuilder {
|
||||
|
||||
private final HalFormsConfiguration configuration;
|
||||
private final MessageResolver resolver;
|
||||
|
||||
public HalFormsTemplateBuilder(HalFormsConfiguration configuration, MessageResolver resolver) {
|
||||
|
||||
this.configuration = configuration;
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract template details from a {@link RepresentationModel}'s {@link Affordance}s.
|
||||
*
|
||||
@@ -100,11 +103,14 @@ class HalFormsTemplateBuilder {
|
||||
.orElse(template);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class PropertyCustomizations {
|
||||
|
||||
private final InputPayloadMetadata metadata;
|
||||
|
||||
public PropertyCustomizations(InputPayloadMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
private HalFormsProperty apply(HalFormsProperty property) {
|
||||
|
||||
String message = resolver.resolve(PropertyPrompt.of(metadata, property));
|
||||
@@ -125,7 +131,6 @@ class HalFormsTemplateBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
static class TemplateTitle implements MessageSourceResolvable {
|
||||
|
||||
private static final String TEMPLATE_TEMPLATE = "_templates.%s.title";
|
||||
@@ -133,6 +138,16 @@ class HalFormsTemplateBuilder {
|
||||
private final HalFormsAffordanceModel affordance;
|
||||
private final boolean soleTemplate;
|
||||
|
||||
private TemplateTitle(HalFormsAffordanceModel affordance, boolean soleTemplate) {
|
||||
|
||||
this.affordance = affordance;
|
||||
this.soleTemplate = soleTemplate;
|
||||
}
|
||||
|
||||
public static TemplateTitle of(HalFormsAffordanceModel affordance, boolean soleTemplate) {
|
||||
return new TemplateTitle(affordance, soleTemplate);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.MessageSourceResolvable#getCodes()
|
||||
@@ -170,7 +185,6 @@ class HalFormsTemplateBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
static class PropertyPrompt implements MessageSourceResolvable {
|
||||
|
||||
private static final String PROMPT_TEMPLATE = "%s._prompt";
|
||||
@@ -178,6 +192,16 @@ class HalFormsTemplateBuilder {
|
||||
private final InputPayloadMetadata metadata;
|
||||
private final HalFormsProperty property;
|
||||
|
||||
private PropertyPrompt(InputPayloadMetadata metadata, HalFormsProperty property) {
|
||||
|
||||
this.metadata = metadata;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public static PropertyPrompt of(InputPayloadMetadata metadata, HalFormsProperty property) {
|
||||
return new PropertyPrompt(metadata, property);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.MessageSourceResolvable#getDefaultMessage()
|
||||
|
||||
@@ -15,20 +15,11 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.problem;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
import lombok.experimental.NonFinal;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -51,20 +42,14 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@NoArgsConstructor(force = true, access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Problem {
|
||||
|
||||
private static Problem EMPTY = new Problem();
|
||||
|
||||
private final @Nullable URI type;
|
||||
private final @Nullable String title;
|
||||
private final @Nullable @Getter(onMethod = @__(@JsonIgnore)) HttpStatus status;
|
||||
private final @Nullable HttpStatus status;
|
||||
private final @Nullable String detail;
|
||||
private final @Nullable URI instance;
|
||||
|
||||
@@ -76,6 +61,19 @@ public class Problem {
|
||||
this(type, title, HttpStatus.resolve(status), detail, instance);
|
||||
}
|
||||
|
||||
private Problem(URI type, String title, HttpStatus status, String detail, URI instance) {
|
||||
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.status = status;
|
||||
this.detail = detail;
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
protected Problem() {
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty {@link Problem} instance.
|
||||
*
|
||||
@@ -114,6 +112,56 @@ public class Problem {
|
||||
return new Problem(URI.create("about:blank"), status.getReasonPhrase(), status, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Problem} by copying its attributes and replacing the {@literal type}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public Problem withType(@Nullable URI type) {
|
||||
return this.type == type ? this : new Problem(type, this.title, this.status, this.detail, this.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Problem} by copying its attributes and replacing the {@literal title}.
|
||||
*
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
public Problem withTitle(@Nullable String title) {
|
||||
return this.title == title ? this : new Problem(this.type, title, this.status, this.detail, this.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Problem} by copying its attributes and replacing the {@literal status}.
|
||||
*
|
||||
* @param status
|
||||
* @return
|
||||
*/
|
||||
public Problem withStatus(@Nullable HttpStatus status) {
|
||||
return this.status == status ? this : new Problem(this.type, this.title, status, this.detail, this.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Problem} by copying its attributes and replacing the {@literal detail}.
|
||||
*
|
||||
* @param detail
|
||||
* @return
|
||||
*/
|
||||
public Problem withDetail(@Nullable String detail) {
|
||||
return this.detail == detail ? this : new Problem(this.type, this.title, this.status, detail, this.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Problem} by copying its attributes and replacing the {@literal instance}.
|
||||
*
|
||||
* @param instance
|
||||
* @return
|
||||
*/
|
||||
public Problem withInstance(@Nullable URI instance) {
|
||||
return this.instance == instance ? this : new Problem(this.type, this.title, this.status, this.detail, instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExtendedProblem} with the given payload as additional properties.
|
||||
*
|
||||
@@ -154,6 +202,18 @@ public class Problem {
|
||||
return new ExtendedProblem<>(type, title, status, detail, instance, properties);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public URI getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@JsonProperty("status")
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@@ -161,13 +221,50 @@ public class Problem {
|
||||
return status != null ? status.value() : null;
|
||||
}
|
||||
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonIgnore))
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
|
||||
public static class ExtendedProblem<T> extends Problem {
|
||||
@JsonIgnore
|
||||
@Nullable
|
||||
public HttpStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
private @NonFinal T extendedProperties;
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getDetail() {
|
||||
return this.detail;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public URI getInstance() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof Problem))
|
||||
return false;
|
||||
Problem problem = (Problem) o;
|
||||
return Objects.equals(this.type, problem.type) && Objects.equals(this.title, problem.title)
|
||||
&& this.status == problem.status && Objects.equals(this.detail, problem.detail)
|
||||
&& Objects.equals(this.instance, problem.instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.type, this.title, this.status, this.detail, this.instance);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Problem(type=" + this.type + ", title=" + this.title + ", status=" + this.status + ", detail=" + this.detail
|
||||
+ ", instance=" + this.instance + ")";
|
||||
}
|
||||
|
||||
public static final class ExtendedProblem<T> extends Problem {
|
||||
|
||||
private T extendedProperties;
|
||||
|
||||
ExtendedProblem(@Nullable URI type, @Nullable String title, @Nullable HttpStatus status, @Nullable String detail,
|
||||
@Nullable URI instance, @Nullable T properties) {
|
||||
@@ -177,6 +274,17 @@ public class Problem {
|
||||
this.extendedProperties = properties;
|
||||
}
|
||||
|
||||
private ExtendedProblem() {
|
||||
|
||||
super(null, null, null, null, null);
|
||||
|
||||
this.extendedProperties = null;
|
||||
}
|
||||
|
||||
public ExtendedProblem(T extendedProperties) {
|
||||
this.extendedProperties = extendedProperties;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.mediatype.problem.Problem#withType(java.net.URI)
|
||||
@@ -274,5 +382,28 @@ public class Problem {
|
||||
|
||||
return (Map<String, Object>) this.extendedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof ExtendedProblem))
|
||||
return false;
|
||||
if (!super.equals(o))
|
||||
return false;
|
||||
ExtendedProblem<?> that = (ExtendedProblem<?>) o;
|
||||
return Objects.equals(this.extendedProperties, that.extendedProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), extendedProperties);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Problem.ExtendedProblem(extendedProperties=" + this.extendedProperties + ")";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.uber;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -37,15 +33,12 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @author Greg Turnquist
|
||||
* @since 1.0
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With(AccessLevel.PACKAGE)
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
class Uber {
|
||||
final class Uber {
|
||||
|
||||
private String version;
|
||||
private List<UberData> data;
|
||||
private UberError error;
|
||||
private final String version;
|
||||
private final List<UberData> data;
|
||||
private final UberError error;
|
||||
|
||||
@JsonCreator
|
||||
Uber(@JsonProperty("version") String version, @JsonProperty("data") @Nullable List<UberData> data,
|
||||
@@ -60,6 +53,36 @@ class Uber {
|
||||
this("1.0", null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Uber} by copying attributes and replacing the {@literal version}.
|
||||
*
|
||||
* @param version
|
||||
* @return
|
||||
*/
|
||||
Uber withVersion(String version) {
|
||||
return this.version == version ? this : new Uber(version, this.data, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Uber} by copying attributes and replacing the {@literal data}.
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
Uber withData(List<UberData> data) {
|
||||
return this.data == data ? this : new Uber(this.version, data, this.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Uber} by copying attributes and replacing the {@literal error}.
|
||||
*
|
||||
* @param error
|
||||
* @return
|
||||
*/
|
||||
Uber withError(UberError error) {
|
||||
return this.error == error ? this : new Uber(this.version, this.data, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract rel and url from every {@link UberData} entry.
|
||||
*
|
||||
@@ -76,4 +99,36 @@ class Uber {
|
||||
.flatMap(uberData -> uberData.getLinks().stream()) //
|
||||
.collect(Links.collector());
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public List<UberData> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public UberError getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof Uber))
|
||||
return false;
|
||||
Uber uber = (Uber) o;
|
||||
return Objects.equals(this.version, uber.version) && Objects.equals(this.data, uber.data)
|
||||
&& Objects.equals(this.error, uber.error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.version, this.data, this.error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.uber;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
@@ -43,10 +41,10 @@ class UberAffordanceModel extends AffordanceModel {
|
||||
private static final Set<HttpMethod> ENTITY_ALTERING_METHODS = EnumSet.of(HttpMethod.POST, HttpMethod.PUT,
|
||||
HttpMethod.PATCH);
|
||||
|
||||
private final @Getter Collection<MediaType> mediaTypes = Collections.singleton(MediaTypes.UBER_JSON);
|
||||
private final Collection<MediaType> mediaTypes = Collections.singleton(MediaTypes.UBER_JSON);
|
||||
|
||||
private final @Getter List<UberData> inputProperties;
|
||||
private final @Getter List<UberData> queryProperties;
|
||||
private final List<UberData> inputProperties;
|
||||
private final List<UberData> queryProperties;
|
||||
|
||||
UberAffordanceModel(String name, Link link, HttpMethod httpMethod, InputPayloadMetadata inputType,
|
||||
List<QueryParameter> queryMethodParameters, PayloadMetadata outputType) {
|
||||
@@ -92,4 +90,16 @@ class UberAffordanceModel extends AffordanceModel {
|
||||
UberAction getAction() {
|
||||
return UberAction.forRequestMethod(getHttpMethod());
|
||||
}
|
||||
|
||||
public Collection<MediaType> getMediaTypes() {
|
||||
return this.mediaTypes;
|
||||
}
|
||||
|
||||
public List<UberData> getInputProperties() {
|
||||
return this.inputProperties;
|
||||
}
|
||||
|
||||
public List<UberData> getQueryProperties() {
|
||||
return this.queryProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.uber;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.hateoas.AffordanceModel;
|
||||
@@ -37,7 +35,7 @@ import org.springframework.http.MediaType;
|
||||
*/
|
||||
class UberAffordanceModelFactory implements AffordanceModelFactory {
|
||||
|
||||
private final @Getter MediaType mediaType = MediaTypes.UBER_JSON;
|
||||
private final MediaType mediaType = MediaTypes.UBER_JSON;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -48,4 +46,8 @@ class UberAffordanceModelFactory implements AffordanceModelFactory {
|
||||
InputPayloadMetadata inputType, List<QueryParameter> queryMethodParameters, PayloadMetadata outputType) {
|
||||
return new UberAffordanceModel(name, link, httpMethod, inputType, queryMethodParameters, outputType);
|
||||
}
|
||||
|
||||
public MediaType getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.uber;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -28,6 +22,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -59,23 +54,31 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @author Greg Turnquist
|
||||
* @since 1.0
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With(AccessLevel.PACKAGE)
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class UberData {
|
||||
final class UberData {
|
||||
|
||||
private @Nullable String id, name, label;
|
||||
private @Nullable List<LinkRelation> rel;
|
||||
private @Nullable String url;
|
||||
private @Nullable UberAction action;
|
||||
private boolean transclude;
|
||||
private @Nullable String model;
|
||||
private @Nullable List<String> sending;
|
||||
private @Nullable List<String> accepting;
|
||||
private @Nullable Object value;
|
||||
private @Nullable List<UberData> data;
|
||||
private @Nullable final String id, name, label;
|
||||
private @Nullable final List<LinkRelation> rel;
|
||||
private @Nullable final String url;
|
||||
private @Nullable final UberAction action;
|
||||
private final boolean transclude;
|
||||
private @Nullable final String model;
|
||||
private @Nullable final List<String> sending;
|
||||
private @Nullable final List<String> accepting;
|
||||
private @Nullable final Object value;
|
||||
private @Nullable final List<UberData> data;
|
||||
|
||||
/**
|
||||
* Simple scalar types that can be encoded by value, not type.
|
||||
*/
|
||||
private final static HashSet<Class<?>> PRIMITIVE_TYPES = new HashSet<>(Collections.singletonList(String.class));
|
||||
|
||||
/**
|
||||
* Set of all Spring HATEOAS resource types.
|
||||
*/
|
||||
private static final HashSet<Class<?>> RESOURCE_TYPES = new HashSet<>(
|
||||
Arrays.asList(RepresentationModel.class, EntityModel.class, CollectionModel.class, PagedModel.class));
|
||||
|
||||
@JsonCreator
|
||||
UberData(@JsonProperty("id") @Nullable String id, @JsonProperty("name") @Nullable String name,
|
||||
@@ -157,17 +160,6 @@ class UberData {
|
||||
return ObjectUtils.nullSafeEquals(this.url, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple scalar types that can be encoded by value, not type.
|
||||
*/
|
||||
private final static HashSet<Class<?>> PRIMITIVE_TYPES = new HashSet<>(Collections.singletonList(String.class));
|
||||
|
||||
/**
|
||||
* Set of all Spring HATEOAS resource types.
|
||||
*/
|
||||
private static final HashSet<Class<?>> RESOURCE_TYPES = new HashSet<>(
|
||||
Arrays.asList(RepresentationModel.class, EntityModel.class, CollectionModel.class, PagedModel.class));
|
||||
|
||||
/**
|
||||
* Convert a {@link RepresentationModel} into a list of {@link UberData}s, containing links and content.
|
||||
*
|
||||
@@ -406,13 +398,294 @@ class UberData {
|
||||
.map(entry -> new UberData().withName(entry.getKey()).withValue(entry.getValue())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal id}.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
UberData withId(@Nullable String id) {
|
||||
|
||||
return this.id == id ? this
|
||||
: new UberData(id, this.name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal name}.
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
UberData withName(@Nullable String name) {
|
||||
return this.name == name ? this
|
||||
: new UberData(this.id, name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal label}.
|
||||
*
|
||||
* @param label
|
||||
* @return
|
||||
*/
|
||||
UberData withLabel(@Nullable String label) {
|
||||
|
||||
return this.label == label ? this
|
||||
: new UberData(this.id, this.name, label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal rel}.
|
||||
*
|
||||
* @param rel
|
||||
* @return
|
||||
*/
|
||||
UberData withRel(@Nullable List<LinkRelation> rel) {
|
||||
|
||||
return this.rel == rel ? this
|
||||
: new UberData(this.id, this.name, this.label, rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal url}.
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
UberData withUrl(@Nullable String url) {
|
||||
return this.url == url ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal action}.
|
||||
*
|
||||
* @param action
|
||||
* @return
|
||||
*/
|
||||
UberData withAction(@Nullable UberAction action) {
|
||||
return this.action == action ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal transclude}.
|
||||
*
|
||||
* @param transclude
|
||||
* @return
|
||||
*/
|
||||
UberData withTransclude(boolean transclude) {
|
||||
|
||||
return this.transclude == transclude ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, this.action, transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal model}.
|
||||
*
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
UberData withModel(@Nullable String model) {
|
||||
|
||||
return this.model == model ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, this.action, this.transclude, model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal sending}.
|
||||
*
|
||||
* @param sending
|
||||
* @return
|
||||
*/
|
||||
UberData withSending(@Nullable List<String> sending) {
|
||||
return this.sending == sending ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal accepting}.
|
||||
*
|
||||
* @param accepting
|
||||
* @return
|
||||
*/
|
||||
UberData withAccepting(@Nullable List<String> accepting) {
|
||||
|
||||
return this.accepting == accepting ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal value}.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
UberData withValue(@Nullable Object value) {
|
||||
|
||||
return this.value == value ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, value, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link UberData} by copying attributes and replacing {@literal data}.
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
UberData withData(@Nullable List<UberData> data) {
|
||||
|
||||
return this.data == data ? this
|
||||
: new UberData(this.id, this.name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, data);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public List<LinkRelation> getRel() {
|
||||
return this.rel;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public String getModel() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public List<String> getSending() {
|
||||
return this.sending;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public List<String> getAccepting() {
|
||||
return this.accepting;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Nullable
|
||||
public List<UberData> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof UberData))
|
||||
return false;
|
||||
UberData uberData = (UberData) o;
|
||||
return this.transclude == uberData.transclude && Objects.equals(this.id, uberData.id)
|
||||
&& Objects.equals(this.name, uberData.name) && Objects.equals(this.label, uberData.label)
|
||||
&& Objects.equals(this.rel, uberData.rel) && Objects.equals(this.url, uberData.url)
|
||||
&& this.action == uberData.action && Objects.equals(this.model, uberData.model)
|
||||
&& Objects.equals(this.sending, uberData.sending) && Objects.equals(this.accepting, uberData.accepting)
|
||||
&& Objects.equals(this.value, uberData.value) && Objects.equals(this.data, uberData.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return Objects.hash(this.id, this.name, this.label, this.rel, this.url, this.action, this.transclude, this.model,
|
||||
this.sending, this.accepting, this.value, this.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "UberData(id='" + this.id + '\'' + ", name='" + this.name + '\'' + ", label='" + this.label + '\'' + ", rel="
|
||||
+ this.rel + ", url='" + this.url + '\'' + ", action=" + this.action + ", transclude=" + this.transclude
|
||||
+ ", model='" + this.model + '\'' + ", sending=" + this.sending + ", accepting=" + this.accepting + ", value="
|
||||
+ this.value + ", data=" + this.data + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds both a {@link Link} and related {@literal rels}.
|
||||
*/
|
||||
@Data
|
||||
private static class LinkAndRels {
|
||||
|
||||
private Link link;
|
||||
private List<LinkRelation> rels = new ArrayList<>();
|
||||
|
||||
public LinkAndRels() {}
|
||||
|
||||
public Link getLink() {
|
||||
return this.link;
|
||||
}
|
||||
|
||||
public List<LinkRelation> getRels() {
|
||||
return this.rels;
|
||||
}
|
||||
|
||||
public void setLink(Link link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public void setRels(List<LinkRelation> rels) {
|
||||
this.rels = rels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof LinkAndRels))
|
||||
return false;
|
||||
LinkAndRels that = (LinkAndRels) o;
|
||||
return Objects.equals(this.link, that.link) && Objects.equals(this.rels, that.rels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.link, this.rels);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "UberData.LinkAndRels(link=" + this.link + ", rels=" + this.rels + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.uber;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -34,13 +29,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @author Greg Turnquist
|
||||
* @since 1.0
|
||||
*/
|
||||
@Value
|
||||
@Getter(onMethod = @__(@JsonProperty))
|
||||
@With(AccessLevel.PACKAGE)
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
class UberDocument {
|
||||
final class UberDocument {
|
||||
|
||||
private Uber uber;
|
||||
private final Uber uber;
|
||||
|
||||
@JsonCreator
|
||||
UberDocument(@JsonProperty("version") String version, @JsonProperty("data") @Nullable List<UberData> data,
|
||||
@@ -51,4 +42,37 @@ class UberDocument {
|
||||
UberDocument() {
|
||||
this("1.0", null, null);
|
||||
}
|
||||
|
||||
UberDocument(Uber uber) {
|
||||
this.uber = uber;
|
||||
}
|
||||
|
||||
UberDocument withUber(Uber uber) {
|
||||
return this.uber == uber ? this : new UberDocument(uber);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public Uber getUber() {
|
||||
return this.uber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof UberDocument))
|
||||
return false;
|
||||
UberDocument that = (UberDocument) o;
|
||||
return Objects.equals(this.uber, that.uber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.uber);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "UberDocument(uber=" + this.uber + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.uber;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -32,11 +29,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @author Greg Turnquist
|
||||
* @since 1.0
|
||||
*/
|
||||
@Value
|
||||
@With(AccessLevel.PACKAGE)
|
||||
class UberError {
|
||||
final class UberError {
|
||||
|
||||
private List<UberData> data;
|
||||
private final List<UberData> data;
|
||||
|
||||
@JsonCreator
|
||||
UberError(@JsonProperty("data") @Nullable List<UberData> data) {
|
||||
@@ -46,4 +41,32 @@ class UberError {
|
||||
UberError() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
UberError withData(List<UberData> data) {
|
||||
return this.data == data ? this : new UberError(data);
|
||||
}
|
||||
|
||||
public List<UberData> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof UberError))
|
||||
return false;
|
||||
UberError uberError = (UberError) o;
|
||||
return Objects.equals(this.data, uberError.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.data);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "UberError(data=" + this.data + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,13 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.vnderrors;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
@@ -51,7 +49,6 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
*/
|
||||
@JsonPropertyOrder({ "message", "logref", "total", "_links", "_embedded" })
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Deprecated
|
||||
public class VndErrors extends CollectionModel<VndErrors.VndError> {
|
||||
|
||||
@@ -72,11 +69,9 @@ public class VndErrors extends CollectionModel<VndErrors.VndError> {
|
||||
|
||||
private final List<VndError> errors;
|
||||
|
||||
@Getter //
|
||||
@JsonInclude(value = JsonInclude.Include.NON_EMPTY) //
|
||||
private final String message;
|
||||
|
||||
@Getter //
|
||||
@JsonInclude(value = JsonInclude.Include.NON_EMPTY) //
|
||||
private final Integer logref;
|
||||
|
||||
@@ -219,6 +214,33 @@ public class VndErrors extends CollectionModel<VndErrors.VndError> {
|
||||
return String.format("VndErrors[%s]", StringUtils.collectionToCommaDelimitedString(this.errors));
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public Integer getLogref() {
|
||||
return this.logref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof VndErrors))
|
||||
return false;
|
||||
if (!super.equals(o))
|
||||
return false;
|
||||
VndErrors vndErrors = (VndErrors) o;
|
||||
return Objects.equals(this.errors, vndErrors.errors) && Objects.equals(this.message, vndErrors.message)
|
||||
&& Objects.equals(this.logref, vndErrors.logref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), this.errors, this.message, this.logref);
|
||||
}
|
||||
|
||||
/**
|
||||
* A single {@link VndError}.
|
||||
*
|
||||
@@ -229,17 +251,13 @@ public class VndErrors extends CollectionModel<VndErrors.VndError> {
|
||||
*/
|
||||
@JsonPropertyOrder({ "message", "path", "logref" })
|
||||
@Relation(collectionRelation = "errors")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Deprecated
|
||||
public static class VndError extends RepresentationModel<VndError> {
|
||||
|
||||
@Getter //
|
||||
private final String message;
|
||||
|
||||
@Getter(onMethod = @__(@JsonInclude(JsonInclude.Include.NON_EMPTY))) //
|
||||
private final @Nullable String path;
|
||||
|
||||
@Getter(onMethod = @__(@JsonInclude(JsonInclude.Include.NON_EMPTY))) //
|
||||
private final Integer logref;
|
||||
|
||||
/**
|
||||
@@ -274,8 +292,43 @@ public class VndErrors extends CollectionModel<VndErrors.VndError> {
|
||||
this(message, null, Integer.parseInt(logref), Arrays.asList(links));
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
public Integer getLogref() {
|
||||
return this.logref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof VndError))
|
||||
return false;
|
||||
if (!super.equals(o))
|
||||
return false;
|
||||
VndError vndError = (VndError) o;
|
||||
return Objects.equals(this.message, vndError.message) && Objects.equals(this.path, vndError.path)
|
||||
&& Objects.equals(this.logref, vndError.logref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), this.message, this.path, this.logref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.format("VndError[logref: %s, message: %s, links: [%s]]", this.logref, this.message,
|
||||
getLinks().toString());
|
||||
}
|
||||
|
||||
@@ -15,17 +15,14 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider.LookupContext;
|
||||
import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* API to provide {@link LinkRelation}s for collections and items of the given type. Implementations can be selected
|
||||
@@ -71,17 +68,31 @@ public interface LinkRelationProvider extends Plugin<LookupContext> {
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode
|
||||
class LookupContext {
|
||||
|
||||
private final Class<?> type;
|
||||
private final @Nullable ResourceType resourceType;
|
||||
|
||||
private LookupContext(Class<?> type, ResourceType resourceType) {
|
||||
|
||||
Assert.notNull(type, "type must not be null!");
|
||||
|
||||
this.type = type;
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
private static LookupContext of(Class<?> type, ResourceType resourceType) {
|
||||
return new LookupContext(type, resourceType);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
private enum ResourceType {
|
||||
ITEM, COLLECTION
|
||||
}
|
||||
|
||||
private final @NonNull @Getter Class<?> type;
|
||||
private final @Nullable ResourceType resourceType;
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} for the type in general, i.e. both item and collection relation lookups.
|
||||
*
|
||||
@@ -140,6 +151,22 @@ public interface LinkRelationProvider extends Plugin<LookupContext> {
|
||||
return this.type.equals(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof LookupContext))
|
||||
return false;
|
||||
LookupContext that = (LookupContext) o;
|
||||
return Objects.equals(this.type, that.type) && this.resourceType == that.resourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.type, this.resourceType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.hateoas.IanaLinkRelations;
|
||||
@@ -32,11 +28,19 @@ import org.springframework.util.Assert;
|
||||
* @see EntityLinks#forType(Function)
|
||||
* @see EntityLinks#forType(Class, Function)
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class TypedEntityLinks<T> {
|
||||
|
||||
private final @NonNull Function<T, ?> identifierExtractor;
|
||||
private final @NonNull EntityLinks entityLinks;
|
||||
private final Function<T, ?> identifierExtractor;
|
||||
private final EntityLinks entityLinks;
|
||||
|
||||
TypedEntityLinks(Function<T, ?> identifierExtractor, EntityLinks entityLinks) {
|
||||
|
||||
Assert.notNull(identifierExtractor, "identifierExtractor must not be null!");
|
||||
Assert.notNull(entityLinks, "entityLinks must not be null!");
|
||||
|
||||
this.identifierExtractor = identifierExtractor;
|
||||
this.entityLinks = entityLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity. Implementations
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.core;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
@@ -32,7 +30,6 @@ import org.springframework.util.StringUtils;
|
||||
* @author Michal Stochmialek
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
|
||||
private static final Map<String, String> MAPPINGS = new ConcurrentReferenceHashMap<>();
|
||||
@@ -40,6 +37,14 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
|
||||
private final MappingDiscoverer delegate;
|
||||
|
||||
private CachingMappingDiscoverer(MappingDiscoverer delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public static CachingMappingDiscoverer of(MappingDiscoverer delegate) {
|
||||
return new CachingMappingDiscoverer(delegate);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.Class)
|
||||
|
||||
@@ -15,20 +15,17 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.core;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
|
||||
private final @NonNull PluginRegistry<LinkRelationProvider, LookupContext> providers;
|
||||
private final PluginRegistry<LinkRelationProvider, LookupContext> providers;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultLinkRelationProvider} for the given {@link LinkRelationProvider}s.
|
||||
@@ -39,6 +36,13 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
this(PluginRegistry.of(providers));
|
||||
}
|
||||
|
||||
public DelegatingLinkRelationProvider(PluginRegistry<LinkRelationProvider, LookupContext> providers) {
|
||||
|
||||
Assert.notNull(providers, "providers must not be null!");
|
||||
|
||||
this.providers = providers;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.server.LinkRelationProvider#getItemResourceRelFor(java.lang.Class)
|
||||
|
||||
@@ -15,14 +15,12 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.core;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
@@ -167,17 +165,108 @@ public class DummyInvocationUtils {
|
||||
return (T) factory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
@Value(staticConstructor = "of")
|
||||
private static class CacheKey<T> {
|
||||
Class<T> type;
|
||||
Object[] arguments;
|
||||
private static final class CacheKey<T> {
|
||||
|
||||
private final Class<T> type;
|
||||
private final Object[] arguments;
|
||||
|
||||
private CacheKey(Class<T> type, Object[] arguments) {
|
||||
|
||||
this.type = type;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public static <T> CacheKey<T> of(Class<T> type, Object[] arguments) {
|
||||
return new CacheKey<T>(type, arguments);
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Object[] getArguments() {
|
||||
return this.arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof CacheKey))
|
||||
return false;
|
||||
CacheKey<?> cacheKey = (CacheKey<?>) o;
|
||||
return Objects.equals(this.type, cacheKey.type) && Arrays.equals(this.arguments, cacheKey.arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = Objects.hash(this.type);
|
||||
result = 31 * result + Arrays.hashCode(this.arguments);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "DummyInvocationUtils.CacheKey(type=" + this.type + ", arguments=" + Arrays.deepToString(this.arguments)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class SimpleMethodInvocation implements MethodInvocation {
|
||||
private static final class SimpleMethodInvocation implements MethodInvocation {
|
||||
|
||||
@NonNull Class<?> targetType;
|
||||
@NonNull Method method;
|
||||
@NonNull Object[] arguments;
|
||||
private final Class<?> targetType;
|
||||
private final Method method;
|
||||
private final Object[] arguments;
|
||||
|
||||
public SimpleMethodInvocation(Class<?> targetType, Method method, Object[] arguments) {
|
||||
|
||||
Assert.notNull(targetType, "targetType must not be null!");
|
||||
Assert.notNull(method, "method must not be null!");
|
||||
Assert.notNull(arguments, "arguments must not be null!");
|
||||
|
||||
this.targetType = targetType;
|
||||
this.method = method;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public Class<?> getTargetType() {
|
||||
return this.targetType;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public Object[] getArguments() {
|
||||
return this.arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof SimpleMethodInvocation))
|
||||
return false;
|
||||
SimpleMethodInvocation that = (SimpleMethodInvocation) o;
|
||||
return Objects.equals(this.targetType, that.targetType) && Objects.equals(this.method, that.method)
|
||||
&& Arrays.equals(this.arguments, that.arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = Objects.hash(this.targetType, this.method);
|
||||
result = 31 * result + Arrays.hashCode(this.arguments);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "DummyInvocationUtils.SimpleMethodInvocation(targetType=" + this.targetType + ", method=" + this.method
|
||||
+ ", arguments=" + Arrays.deepToString(this.arguments) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.core;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@@ -31,11 +29,14 @@ import org.springframework.web.util.UriUtils;
|
||||
* @since 0.22
|
||||
* @soundtrack Don Philippe - Between Now And Now (Between Now And Now)
|
||||
*/
|
||||
@UtilityClass
|
||||
class EncodingUtils {
|
||||
final class EncodingUtils {
|
||||
|
||||
private static final Charset ENCODING = StandardCharsets.UTF_8;
|
||||
|
||||
private EncodingUtils() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given path value.
|
||||
*
|
||||
|
||||
@@ -17,8 +17,6 @@ package org.springframework.hateoas.server.core;
|
||||
|
||||
import static org.springframework.hateoas.server.core.EncodingUtils.*;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -48,7 +46,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
*/
|
||||
public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkBuilder {
|
||||
|
||||
private final @Getter List<Affordance> affordances;
|
||||
private final List<Affordance> affordances;
|
||||
|
||||
private UriComponents components;
|
||||
|
||||
@@ -169,9 +167,10 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sub-class.
|
||||
*
|
||||
* @param builder will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected abstract T createNewInstance(UriComponents components, List<Affordance> affordances);
|
||||
|
||||
public List<Affordance> getAffordances() {
|
||||
return this.affordances;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ import static org.springframework.hateoas.TemplateVariables.*;
|
||||
import static org.springframework.hateoas.server.core.EncodingUtils.*;
|
||||
import static org.springframework.web.util.UriComponents.UriTemplateVariables.*;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
@@ -32,6 +30,7 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -237,12 +236,55 @@ public class WebHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Value(staticConstructor = "of")
|
||||
private static class AffordanceKey {
|
||||
private static final class AffordanceKey {
|
||||
|
||||
Class<?> type;
|
||||
Method method;
|
||||
UriComponents href;
|
||||
private final Class<?> type;
|
||||
private final Method method;
|
||||
private final UriComponents href;
|
||||
|
||||
private AffordanceKey(Class<?> type, Method method, UriComponents href) {
|
||||
|
||||
this.type = type;
|
||||
this.method = method;
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
public static AffordanceKey of(Class<?> type, Method method, UriComponents href) {
|
||||
return new AffordanceKey(type, method, href);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public UriComponents getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof AffordanceKey))
|
||||
return false;
|
||||
AffordanceKey that = (AffordanceKey) o;
|
||||
return Objects.equals(this.type, that.type) && Objects.equals(this.method, that.method)
|
||||
&& Objects.equals(this.href, that.href);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.type, this.method, this.href);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "WebHandler.AffordanceKey(type=" + this.type + ", method=" + this.method + ", href=" + this.href + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private static class HandlerMethodParameters {
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.mvc;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
@@ -32,6 +29,7 @@ import org.springframework.hateoas.server.core.HeaderLinksResponseEntity;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
@@ -45,7 +43,6 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
* @since 0.20
|
||||
* @soundtrack Doppelkopf - Balance (Von Abseits)
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class RepresentationModelProcessorHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
static final ResolvableType ENTITY_MODEL_TYPE = ResolvableType.forRawClass(EntityModel.class);
|
||||
@@ -60,11 +57,21 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
}
|
||||
}
|
||||
|
||||
private final @NonNull HandlerMethodReturnValueHandler delegate;
|
||||
private final @NonNull Supplier<RepresentationModelProcessorInvoker> invoker;
|
||||
private final HandlerMethodReturnValueHandler delegate;
|
||||
private final Supplier<RepresentationModelProcessorInvoker> invoker;
|
||||
|
||||
private boolean rootLinksAsHeaders = false;
|
||||
|
||||
public RepresentationModelProcessorHandlerMethodReturnValueHandler(HandlerMethodReturnValueHandler delegate,
|
||||
Supplier<RepresentationModelProcessorInvoker> invoker) {
|
||||
|
||||
Assert.notNull(delegate, "delegate must not be null!");
|
||||
Assert.notNull(invoker, "invoker must not be null!");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rootLinksAsHeaders the rootLinksAsHeaders to set
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.hateoas.server.reactive;
|
||||
|
||||
import static org.springframework.web.filter.reactive.ServerWebExchangeContextFilter.*;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
@@ -111,11 +110,14 @@ public class WebFluxLinkBuilder extends TemplateVariableAwareLinkBuilderSupport<
|
||||
return this;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class WebFluxBuilder {
|
||||
|
||||
private final Mono<WebFluxLinkBuilder> builder;
|
||||
|
||||
public WebFluxBuilder(Mono<WebFluxLinkBuilder> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link WebFluxBuilder} appending the given path to the currently to be built link.
|
||||
*
|
||||
@@ -179,11 +181,14 @@ public class WebFluxLinkBuilder extends TemplateVariableAwareLinkBuilderSupport<
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public static class WebFluxLink {
|
||||
|
||||
private final Mono<Link> link;
|
||||
|
||||
public WebFluxLink(Mono<Link> link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the affordance created by the given virtual method invocation.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user