#833 - Changed ResourceSupport class name hierarchy.

This commit is contained in:
Oliver Drotbohm
2019-02-27 20:04:15 +01:00
parent 57f36a58db
commit a89e57eed7
93 changed files with 1606 additions and 1476 deletions

View File

@@ -31,34 +31,34 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Oliver Gierke
* @author Greg Turnquist
*/
public class Resources<T> extends ResourceSupport implements Iterable<T> {
public class CollectionModel<T> extends RepresentationModel<CollectionModel<T>> implements Iterable<T> {
private final Collection<T> content;
/**
* Creates an empty {@link Resources} instance.
* Creates an empty {@link CollectionModel} instance.
*/
protected Resources() {
protected CollectionModel() {
this(new ArrayList<>());
}
/**
* Creates a {@link Resources} instance with the given content and {@link Link}s (optional).
* Creates a {@link CollectionModel} instance with the given content and {@link Link}s (optional).
*
* @param content must not be {@literal null}.
* @param links the links to be added to the {@link Resources}.
* @param links the links to be added to the {@link CollectionModel}.
*/
public Resources(Iterable<T> content, Link... links) {
public CollectionModel(Iterable<T> content, Link... links) {
this(content, Arrays.asList(links));
}
/**
* Creates a {@link Resources} instance with the given content and {@link Link}s.
* Creates a {@link CollectionModel} instance with the given content and {@link Link}s.
*
* @param content must not be {@literal null}.
* @param links the links to be added to the {@link Resources}.
* @param links the links to be added to the {@link CollectionModel}.
*/
public Resources(Iterable<T> content, Iterable<Link> links) {
public CollectionModel(Iterable<T> content, Iterable<Link> links) {
Assert.notNull(content, "Content must not be null!");
@@ -71,23 +71,24 @@ public class Resources<T> extends ResourceSupport implements Iterable<T> {
}
/**
* Creates a new {@link Resources} instance by wrapping the given domain class instances into a {@link Resource}.
* Creates a new {@link CollectionModel} instance by wrapping the given domain class instances into a
* {@link EntityModel}.
*
* @param content must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends Resource<S>, S> Resources<T> wrap(Iterable<S> content) {
public static <T extends EntityModel<S>, S> CollectionModel<T> wrap(Iterable<S> content) {
Assert.notNull(content, "Content must not be null!");
ArrayList<T> resources = new ArrayList<>();
for (S element : content) {
resources.add((T) new Resource<>(element));
resources.add((T) new EntityModel<>(element));
}
return new Resources<>(resources);
return new CollectionModel<>(resources);
}
/**
@@ -133,7 +134,7 @@ public class Resources<T> extends ResourceSupport implements Iterable<T> {
return false;
}
Resources<?> that = (Resources<?>) obj;
CollectionModel<?> that = (CollectionModel<?>) obj;
boolean contentEqual = this.content == null ? that.content == null : this.content.equals(that.content);
return contentEqual && super.equals(obj);

View File

@@ -23,39 +23,39 @@ import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
/**
* A simple {@link Resource} wrapping a domain object and adding links to it.
*
* A simple {@link EntityModel} wrapping a domain object and adding links to it.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
public class Resource<T> extends ResourceSupport {
public class EntityModel<T> extends RepresentationModel<EntityModel<T>> {
private final T content;
/**
* Creates an empty {@link Resource}.
* Creates an empty {@link EntityModel}.
*/
protected Resource() {
protected EntityModel() {
this.content = null;
}
/**
* Creates a new {@link Resource} with the given content and {@link Link}s (optional).
*
* Creates a new {@link EntityModel} with the given content and {@link Link}s (optional).
*
* @param content must not be {@literal null}.
* @param links the links to add to the {@link Resource}.
* @param links the links to add to the {@link EntityModel}.
*/
public Resource(T content, Link... links) {
public EntityModel(T content, Link... links) {
this(content, Arrays.asList(links));
}
/**
* Creates a new {@link Resource} with the given content and {@link Link}s.
*
* Creates a new {@link EntityModel} with the given content and {@link Link}s.
*
* @param content must not be {@literal null}.
* @param links the links to add to the {@link Resource}.
* @param links the links to add to the {@link EntityModel}.
*/
public Resource(T content, Iterable<Link> links) {
public EntityModel(T content, Iterable<Link> links) {
Assert.notNull(content, "Content must not be null!");
Assert.isTrue(!(content instanceof Collection), "Content must not be a collection! Use Resources instead!");
@@ -65,7 +65,7 @@ public class Resource<T> extends ResourceSupport {
/**
* Returns the underlying entity.
*
*
* @return the content
*/
@JsonUnwrapped
@@ -73,7 +73,7 @@ public class Resource<T> extends ResourceSupport {
return content;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceSupport#toString()
*/
@@ -82,7 +82,7 @@ public class Resource<T> extends ResourceSupport {
return String.format("Resource { content: %s, %s }", getContent(), super.toString());
}
/*
/*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceSupport#equals(java.lang.Object)
*/
@@ -97,13 +97,13 @@ public class Resource<T> extends ResourceSupport {
return false;
}
Resource<?> that = (Resource<?>) obj;
EntityModel<?> that = (EntityModel<?>) obj;
boolean contentEqual = this.content == null ? that.content == null : this.content.equals(that.content);
return contentEqual && super.equals(obj);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceSupport#hashCode()
*/

View File

@@ -31,39 +31,41 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Oliver Gierke
* @author Greg Turnquist
*/
public class PagedResources<T> extends Resources<T> {
public class PagedModel<T> extends CollectionModel<T> {
public static PagedResources<?> NO_PAGE = new PagedResources<>();
public static PagedModel<?> NO_PAGE = new PagedModel<>();
private PageMetadata metadata;
/**
* Default constructor to allow instantiation by reflection.
*/
protected PagedResources() {
protected PagedModel() {
this(new ArrayList<>(), null);
}
/**
* Creates a new {@link PagedResources} from the given content, {@link PageMetadata} and {@link Link}s (optional).
* Creates a new {@link PagedModel} from the given content, {@link PageMetadata} and {@link Link}s (optional).
*
* @param content must not be {@literal null}.
* @param metadata
* @param links
*/
public PagedResources(Collection<T> content, PageMetadata metadata, Link... links) {
public PagedModel(Collection<T> content, PageMetadata metadata, Link... links) {
this(content, metadata, Arrays.asList(links));
}
/**
* Creates a new {@link PagedResources} from the given content {@link PageMetadata} and {@link Link}s.
* Creates a new {@link PagedModel} from the given content {@link PageMetadata} and {@link Link}s.
*
* @param content must not be {@literal null}.
* @param metadata
* @param links
*/
public PagedResources(Collection<T> content, PageMetadata metadata, Iterable<Link> links) {
public PagedModel(Collection<T> content, PageMetadata metadata, Iterable<Link> links) {
super(content, links);
this.metadata = metadata;
}
@@ -78,23 +80,23 @@ public class PagedResources<T> extends Resources<T> {
}
/**
* Factory method to easily create a {@link PagedResources} instance from a set of entities and pagination metadata.
* Factory method to easily create a {@link PagedModel} instance from a set of entities and pagination metadata.
*
* @param content must not be {@literal null}.
* @param metadata
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends Resource<S>, S> PagedResources<T> wrap(Iterable<S> content, PageMetadata metadata) {
public static <T extends EntityModel<S>, S> PagedModel<T> wrap(Iterable<S> content, PageMetadata metadata) {
Assert.notNull(content, "Content must not be null!");
ArrayList<T> resources = new ArrayList<>();
for (S element : content) {
resources.add((T) new Resource<>(element));
resources.add((T) new EntityModel<>(element));
}
return new PagedResources<>(resources, metadata);
return new PagedModel<>(resources, metadata);
}
/**
@@ -141,7 +143,7 @@ public class PagedResources<T> extends Resources<T> {
return false;
}
PagedResources<?> that = (PagedResources<?>) obj;
PagedModel<?> that = (PagedModel<?>) obj;
boolean metadataEquals = this.metadata == null ? that.metadata == null : this.metadata.equals(that.metadata);
return metadataEquals && super.equals(obj);

View File

@@ -33,15 +33,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Johhny Lim
* @author Greg Turnquist
*/
public class ResourceSupport implements Identifiable<Link> {
public class RepresentationModel<T extends RepresentationModel<? extends T>> implements Identifiable<Link> {
private final List<Link> links;
public ResourceSupport() {
public RepresentationModel() {
this.links = new ArrayList<>();
}
public ResourceSupport(Link initialLink) {
public RepresentationModel(Link initialLink) {
Assert.notNull(initialLink, "initialLink must not be null!");
@@ -49,7 +49,7 @@ public class ResourceSupport implements Identifiable<Link> {
this.links.add(initialLink);
}
public ResourceSupport(List<Link> initialLinks) {
public RepresentationModel(List<Link> initialLinks) {
Assert.notNull(initialLinks, "initialLinks must not be null!");
@@ -70,11 +70,14 @@ public class ResourceSupport implements Identifiable<Link> {
*
* @param link
*/
public void add(Link link) {
@SuppressWarnings("unchecked")
public T add(Link link) {
Assert.notNull(link, "Link must not be null!");
this.links.add(link);
return (T) this;
}
/**
@@ -82,11 +85,14 @@ public class ResourceSupport implements Identifiable<Link> {
*
* @param links
*/
public void add(Iterable<Link> links) {
@SuppressWarnings("unchecked")
public T add(Iterable<Link> links) {
Assert.notNull(links, "Given links must not be null!");
links.forEach(this::add);
return (T) this;
}
/**
@@ -94,11 +100,14 @@ public class ResourceSupport implements Identifiable<Link> {
*
* @param links must not be {@literal null}.
*/
public void add(Link... links) {
@SuppressWarnings("unchecked")
public T add(Link... links) {
Assert.notNull(links, "Given links must not be null!");
add(Arrays.asList(links));
return (T) this;
}
/**
@@ -137,58 +146,90 @@ public class ResourceSupport implements Identifiable<Link> {
/**
* Removes all {@link Link}s added to the resource so far.
*/
public void removeLinks() {
@SuppressWarnings("unchecked")
public T removeLinks() {
this.links.clear();
return (T) this;
}
/**
* Returns the link with the given rel.
* Returns the link with the given relation.
*
* @param rel
* @return the link with the given rel or {@link Optional#empty()} if none found.
* @param relation must not be {@literal null} or empty.
* @return the link with the given relation or {@link Optional#empty()} if none found.
*/
public Optional<Link> getLink(String rel) {
return getLink(LinkRelation.of(rel));
public Optional<Link> getLink(String relation) {
return getLink(LinkRelation.of(relation));
}
public Optional<Link> getLink(LinkRelation rel) {
/**
* Returns the link with the given {@link LinkRelation}.
*
* @param relation
* @return
*/
public Optional<Link> getLink(LinkRelation relation) {
return links.stream() //
.filter(it -> it.hasRel(rel)) //
.filter(it -> it.hasRel(relation)) //
.findFirst();
}
/**
* Returns the link with the given rel.
* Returns the link with the given relation.
*
* @param rel
* @return the link with the given rel.
* @throws IllegalArgumentException in case no link with the given rel can be found.
* @param relation must not be {@literal null} or empty.
* @return the link with the given relation.
* @throws IllegalArgumentException in case no link with the given relation can be found.
*/
public Link getRequiredLink(String rel) {
public Link getRequiredLink(String relation) {
return getLink(rel) //
.orElseThrow(() -> new IllegalArgumentException(String.format("No link with rel %s found!", rel)));
}
public Link getRequiredLink(LinkRelation rel) {
return getRequiredLink(rel.value());
return getLink(relation) //
.orElseThrow(() -> new IllegalArgumentException(String.format("No link with rel %s found!", relation)));
}
/**
* Returns all {@link Link}s with the given relation type.
* Returns the link with the given relation.
*
* @param relation must not be {@literal null}.
* @return the link with the given relation.
* @throws IllegalArgumentException in case no link with the given relation can be found.
*/
public Link getRequiredLink(LinkRelation relation) {
Assert.notNull(relation, "Link relation must not be null!");
return getRequiredLink(relation.value());
}
/**
* Returns all {@link Link}s with the given relation.
*
* @param relation must not be {@literal null}.
* @return the links in a {@link List}
*/
public List<Link> getLinks(String rel) {
public List<Link> getLinks(String relation) {
Assert.hasText(relation, "Link relation must not be null or empty!");
return links.stream() //
.filter(link -> link.hasRel(rel)) //
.filter(link -> link.hasRel(relation)) //
.collect(Collectors.toList());
}
public List<Link> getLinks(LinkRelation rel) {
return getLinks(rel.value());
/**
* Returns all {@link Link}s with the given relation.
*
* @param relation must not be {@literal null}.
* @return the links in a {@link List}
*/
public List<Link> getLinks(LinkRelation relation) {
Assert.notNull(relation, "Link relation must not be null!");
return getLinks(relation.value());
}
/*
@@ -205,6 +246,7 @@ public class ResourceSupport implements Identifiable<Link> {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
if (this == obj) {
@@ -215,9 +257,9 @@ public class ResourceSupport implements Identifiable<Link> {
return false;
}
ResourceSupport that = (ResourceSupport) obj;
T that = (T) obj;
return this.links.equals(that.links);
return getLinks().equals(that.getLinks());
}
/*

View File

@@ -26,7 +26,7 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.hateoas.server.mvc.UriComponentsContributor;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilderFactory;
@@ -84,7 +84,7 @@ class WebMvcHateoasConfiguration {
this.hypermediaTypes.forEach(hypermedia -> {
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class,
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(RepresentationModel.class,
hypermedia.getMediaTypes(), hypermedia.configureObjectMapper(mapper.copy())));
});
}

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.hateoas.mediatype;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.CollectionModel;
import com.fasterxml.jackson.databind.JavaType;
@@ -49,8 +49,8 @@ public final class JacksonHelper {
public static boolean isResourcesOfResource(JavaType type) {
return
Resources.class.isAssignableFrom(type.getRawClass())
CollectionModel.class.isAssignableFrom(type.getRawClass())
&&
Resource.class.isAssignableFrom(type.containedType(0).getRawClass());
EntityModel.class.isAssignableFrom(type.containedType(0).getRawClass());
}
}

View File

@@ -35,7 +35,7 @@ import reactor.core.publisher.Mono;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.support.WebStack;
import org.springframework.util.ReflectionUtils;
@@ -56,8 +56,8 @@ public class PropertyUtils {
public static Map<String, Object> findProperties(Object object) {
if (object.getClass().equals(Resource.class)) {
return findProperties(((Resource<?>) object).getContent());
if (object.getClass().equals(EntityModel.class)) {
return findProperties(((EntityModel<?>) object).getContent());
}
return getPropertyDescriptors(object.getClass())
@@ -83,7 +83,7 @@ public class PropertyUtils {
}
}
if (resolvableType.getRawClass().equals(Resource.class)) {
if (resolvableType.getRawClass().equals(EntityModel.class)) {
return findPropertyNames(resolvableType.resolveGeneric(0));
} else {
return findPropertyNames(resolvableType.getRawClass());

View File

@@ -17,7 +17,7 @@ package org.springframework.hateoas.mediatype.collectionjson;
import static org.springframework.hateoas.mediatype.collectionjson.Jackson2CollectionJsonModule.*;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.CollectionModel;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -27,6 +27,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* @author Greg Turnquist
*/
@JsonDeserialize(using = CollectionJsonResourcesDeserializer.class)
abstract class ResourcesMixin<T> extends Resources<T> {
abstract class CollectionRepresentationModelMixin<T> extends CollectionModel<T> {
}

View File

@@ -17,7 +17,7 @@ package org.springframework.hateoas.mediatype.collectionjson;
import static org.springframework.hateoas.mediatype.collectionjson.Jackson2CollectionJsonModule.*;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.EntityModel;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -27,6 +27,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* @author Greg Turnquist
*/
@JsonDeserialize(using = CollectionJsonResourceDeserializer.class)
abstract class ResourceMixin<T> extends Resource<T> {
abstract class EntityRepresentationModelMixin<T> extends EntityModel<T> {
}

View File

@@ -26,17 +26,17 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.hateoas.Affordance;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.Links.MergeMode;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.JacksonHelper;
import org.springframework.hateoas.mediatype.PropertyUtils;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpMethod;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -59,8 +59,8 @@ import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* Jackson 2 module implementation to render {@link Resources}, {@link Resource}, and {@link ResourceSupport} instances
* in Collection+JSON compatible JSON.
* Jackson 2 module implementation to render {@link CollectionModel}, {@link EntityModel}, and
* {@link RepresentationModel} instances in Collection+JSON compatible JSON.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
@@ -73,10 +73,10 @@ class Jackson2CollectionJsonModule extends SimpleModule {
super("collection-json-module", new Version(1, 0, 0, null, "org.springframework.hateoas", "spring-hateoas"));
setMixInAnnotation(ResourceSupport.class, ResourceSupportMixin.class);
setMixInAnnotation(Resource.class, ResourceMixin.class);
setMixInAnnotation(Resources.class, ResourcesMixin.class);
setMixInAnnotation(PagedResources.class, PagedResourcesMixin.class);
setMixInAnnotation(RepresentationModel.class, RepresentationModelMixin.class);
setMixInAnnotation(EntityModel.class, EntityRepresentationModelMixin.class);
setMixInAnnotation(CollectionModel.class, CollectionRepresentationModelMixin.class);
setMixInAnnotation(PagedModel.class, PagedResourcesMixin.class);
addSerializer(new CollectionJsonPagedResourcesSerializer());
addSerializer(new CollectionJsonResourcesSerializer());
@@ -160,7 +160,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonResourceSupportSerializer extends ContainerSerializer<ResourceSupport>
static class CollectionJsonResourceSupportSerializer extends ContainerSerializer<RepresentationModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = 6127711241993352699L;
@@ -173,12 +173,13 @@ class Jackson2CollectionJsonModule extends SimpleModule {
CollectionJsonResourceSupportSerializer(BeanProperty property) {
super(ResourceSupport.class, false);
super(RepresentationModel.class, false);
this.property = property;
}
@Override
public void serialize(ResourceSupport value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
public void serialize(RepresentationModel<?> value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
String href = value.getRequiredLink(IanaLinkRelations.SELF.value()).getHref();
@@ -220,7 +221,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
@Override
public boolean hasSingleElement(ResourceSupport value) {
public boolean hasSingleElement(RepresentationModel<?> value) {
return true;
}
@@ -230,7 +231,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonResourceSerializer extends ContainerSerializer<Resource<?>>
static class CollectionJsonResourceSerializer extends ContainerSerializer<EntityModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = 2212535956767860364L;
@@ -243,12 +244,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
CollectionJsonResourceSerializer(BeanProperty property) {
super(Resource.class, false);
super(EntityModel.class, false);
this.property = property;
}
@Override
public void serialize(Resource<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
public void serialize(EntityModel<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
String href = value.getRequiredLink(IanaLinkRelations.SELF).getHref();
Links withoutSelfLink = value.getLinks().without(IanaLinkRelations.SELF);
@@ -287,7 +288,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
@Override
public boolean hasSingleElement(Resource<?> value) {
public boolean hasSingleElement(EntityModel<?> value) {
return true;
}
@@ -297,12 +298,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonResourcesSerializer extends ContainerSerializer<Resources<?>> {
static class CollectionJsonResourcesSerializer extends ContainerSerializer<CollectionModel<?>> {
private static final long serialVersionUID = -278986431091914402L;
CollectionJsonResourcesSerializer() {
super(Resources.class, false);
super(CollectionModel.class, false);
}
/*
@@ -310,7 +311,8 @@ class Jackson2CollectionJsonModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
*/
@Override
public void serialize(Resources<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
public void serialize(CollectionModel<?> value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
CollectionJson<Object> collectionJson = new CollectionJson<>() //
.withVersion("1.0") //
@@ -349,7 +351,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
*/
@Override
public boolean isEmpty(SerializerProvider provider, Resources<?> value) {
public boolean isEmpty(SerializerProvider provider, CollectionModel<?> value) {
return value.getContent().isEmpty();
}
@@ -358,7 +360,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
*/
@Override
public boolean hasSingleElement(Resources<?> value) {
public boolean hasSingleElement(CollectionModel<?> value) {
return value.getContent().size() == 1;
}
@@ -372,7 +374,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonPagedResourcesSerializer extends ContainerSerializer<PagedResources<?>>
static class CollectionJsonPagedResourcesSerializer extends ContainerSerializer<PagedModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = -6703190072925382402L;
@@ -385,12 +387,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
CollectionJsonPagedResourcesSerializer(BeanProperty property) {
super(Resources.class, false);
super(CollectionModel.class, false);
this.property = property;
}
@Override
public void serialize(PagedResources<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
public void serialize(PagedModel<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
CollectionJson<?> collectionJson = new CollectionJson<>() //
.withVersion("1.0") //
@@ -422,12 +424,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
@Override
public boolean isEmpty(PagedResources<?> value) {
public boolean isEmpty(PagedModel<?> value) {
return value.getContent().size() == 0;
}
@Override
public boolean hasSingleElement(PagedResources<?> value) {
public boolean hasSingleElement(PagedModel<?> value) {
return value.getContent().size() == 1;
}
@@ -476,7 +478,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonResourceSupportDeserializer extends ContainerDeserializerBase<ResourceSupport>
static class CollectionJsonResourceSupportDeserializer extends ContainerDeserializerBase<RepresentationModel<?>>
implements ContextualDeserializer {
private static final long serialVersionUID = 502737712634617739L;
@@ -484,7 +486,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
private final JavaType contentType;
CollectionJsonResourceSupportDeserializer() {
this(TypeFactory.defaultInstance().constructType(ResourceSupport.class));
this(TypeFactory.defaultInstance().constructType(RepresentationModel.class));
}
CollectionJsonResourceSupportDeserializer(JavaType contentType) {
@@ -516,7 +518,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
*/
@Override
public ResourceSupport deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
public RepresentationModel<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
TypeFactory typeFactory = ctxt.getTypeFactory();
@@ -541,10 +543,9 @@ class Jackson2CollectionJsonModule extends SimpleModule {
CollectionJsonItem<?> firstItem = items.get(0).withOwnSelfLink();
ResourceSupport resource = (ResourceSupport) firstItem.toRawData(this.contentType);
resource.add(firstItem.getLinks().merge(merged));
RepresentationModel<?> resource = (RepresentationModel<?>) firstItem.toRawData(this.contentType);
return resource.add(firstItem.getLinks().merge(merged));
return resource;
}
if (withOwnSelfLink.getTemplate() != null) {
@@ -552,19 +553,14 @@ class Jackson2CollectionJsonModule extends SimpleModule {
Map<String, Object> properties = withOwnSelfLink.getTemplate().getData().stream()
.collect(Collectors.toMap(CollectionJsonData::getName, CollectionJsonData::getValue));
ResourceSupport resourceSupport = (ResourceSupport) PropertyUtils
RepresentationModel<?> resourceSupport = (RepresentationModel<?>) PropertyUtils
.createObjectFromProperties(this.contentType.getRawClass(), properties);
resourceSupport.add(withOwnSelfLink.getLinks());
return resourceSupport;
return resourceSupport.add(withOwnSelfLink.getLinks());
} else {
ResourceSupport resource = new ResourceSupport();
resource.add(withOwnSelfLink.getLinks());
return resource;
return new RepresentationModel<>().add(withOwnSelfLink.getLinks());
}
}
@@ -580,7 +576,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonResourceDeserializer extends ContainerDeserializerBase<Resource<?>>
static class CollectionJsonResourceDeserializer extends ContainerDeserializerBase<EntityModel<?>>
implements ContextualDeserializer {
private static final long serialVersionUID = -5911687423054932523L;
@@ -608,7 +604,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
@Override
public Resource<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
public EntityModel<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JavaType rootType = JacksonHelper.findRootType(this.contentType);
JavaType wrappedType = ctxt.getTypeFactory().constructParametricType(CollectionJsonDocument.class, rootType);
@@ -626,7 +622,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
Object obj = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
return new Resource<>(obj, links);
return new EntityModel<>(obj, links);
} else {
Links merged = items.stream() //
@@ -637,7 +633,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
CollectionJsonItem<?> firstItem = items.get(0).withOwnSelfLink();
return new Resource<>(firstItem.toRawData(rootType),
return new EntityModel<>(firstItem.toRawData(rootType),
merged.merge(MergeMode.REPLACE_BY_REL, firstItem.getLinks()));
}
}
@@ -655,8 +651,8 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static abstract class CollectionJsonDeserializerBase<T extends Resources<?>> extends ContainerDeserializerBase<T>
implements ContextualDeserializer {
static abstract class CollectionJsonDeserializerBase<T extends CollectionModel<?>>
extends ContainerDeserializerBase<T> implements ContextualDeserializer {
private static final long serialVersionUID = 1007769482339850545L;
@@ -731,22 +727,22 @@ class Jackson2CollectionJsonModule extends SimpleModule {
return finalizer.apply(Collections.emptyList(), links);
}
boolean isResource = contentType.hasGenericTypes() && contentType.containedType(0).hasRawClass(Resource.class);
boolean isResource = contentType.hasGenericTypes() && contentType.containedType(0).hasRawClass(EntityModel.class);
return collection.getItems().stream() //
.map(CollectionJsonItem::withOwnSelfLink) //
.<Object> map(it -> isResource //
? new Resource<>(it.toRawData(rootType), it.getLinks()) //
? new EntityModel<>(it.toRawData(rootType), it.getLinks()) //
: it.toRawData(rootType)) //
.collect(Collectors.collectingAndThen(Collectors.toList(), it -> finalizer.apply(it, links)));
}
}
static class CollectionJsonResourcesDeserializer extends CollectionJsonDeserializerBase<Resources<?>> {
static class CollectionJsonResourcesDeserializer extends CollectionJsonDeserializerBase<CollectionModel<?>> {
private static final long serialVersionUID = 6406522912020578141L;
private static final BiFunction<List<Object>, Links, Resources<?>> FINISHER = Resources::new;
private static final Function<JavaType, CollectionJsonDeserializerBase<Resources<?>>> CONTEXTUAL_CREATOR = CollectionJsonResourcesDeserializer::new;
private static final BiFunction<List<Object>, Links, CollectionModel<?>> FINISHER = CollectionModel::new;
private static final Function<JavaType, CollectionJsonDeserializerBase<CollectionModel<?>>> CONTEXTUAL_CREATOR = CollectionJsonResourcesDeserializer::new;
CollectionJsonResourcesDeserializer() {
super(FINISHER, CONTEXTUAL_CREATOR);
@@ -757,12 +753,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
static class CollectionJsonPagedResourcesDeserializer extends CollectionJsonDeserializerBase<PagedResources<?>> {
static class CollectionJsonPagedResourcesDeserializer extends CollectionJsonDeserializerBase<PagedModel<?>> {
private static final long serialVersionUID = -7465448422501330790L;
private static final BiFunction<List<Object>, Links, PagedResources<?>> FINISHER = (content,
links) -> new PagedResources<>(content, null, links);
private static final Function<JavaType, CollectionJsonDeserializerBase<PagedResources<?>>> CONTEXTUAL_CREATOR = CollectionJsonPagedResourcesDeserializer::new;
private static final BiFunction<List<Object>, Links, PagedModel<?>> FINISHER = (content,
links) -> new PagedModel<>(content, null, links);
private static final Function<JavaType, CollectionJsonDeserializerBase<PagedModel<?>>> CONTEXTUAL_CREATOR = CollectionJsonPagedResourcesDeserializer::new;
CollectionJsonPagedResourcesDeserializer() {
super(FINISHER, CONTEXTUAL_CREATOR);
@@ -773,15 +769,15 @@ class Jackson2CollectionJsonModule extends SimpleModule {
}
}
private static List<CollectionJsonItem<Object>> resourcesToCollectionJsonItems(Resources<?> resources) {
private static List<CollectionJsonItem<Object>> resourcesToCollectionJsonItems(CollectionModel<?> resources) {
return resources.getContent().stream().map(content -> {
if (!Resource.class.isInstance(content)) {
if (!EntityModel.class.isInstance(content)) {
return new CollectionJsonItem<>().withRawData(content);
}
Resource<?> resource = (Resource<?>) content;
EntityModel<?> resource = (EntityModel<?>) content;
return new CollectionJsonItem<>() //
.withHref(resource.getRequiredLink(IanaLinkRelations.SELF).getHref())
@@ -797,7 +793,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
* @param resource
* @return
*/
private static List<CollectionJsonQuery> findQueries(ResourceSupport resource) {
private static List<CollectionJsonQuery> findQueries(RepresentationModel<?> resource) {
if (!resource.hasLink(IanaLinkRelations.SELF)) {
return Collections.emptyList();
@@ -823,7 +819,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
* @param resource
* @return
*/
private static CollectionJsonTemplate findTemplate(ResourceSupport resource) {
private static CollectionJsonTemplate findTemplate(RepresentationModel<?> resource) {
if (!resource.hasLink(IanaLinkRelations.SELF)) {
return null;

View File

@@ -17,16 +17,16 @@ package org.springframework.hateoas.mediatype.collectionjson;
import static org.springframework.hateoas.mediatype.collectionjson.Jackson2CollectionJsonModule.*;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.PagedModel;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
/**
* Jackson 2 mixin to handle {@link PagedResources}.
* Jackson 2 mixin to handle {@link PagedModel}.
*
* @author Greg Turnquist
*/
@JsonDeserialize(using = CollectionJsonPagedResourcesDeserializer.class)
abstract class PagedResourcesMixin<T> extends PagedResources<T> {
abstract class PagedResourcesMixin<T> extends PagedModel<T> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2018 the original author or authors.
* Copyright 2015-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
package org.springframework.hateoas.mediatype.collectionjson;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.collectionjson.Jackson2CollectionJsonModule.CollectionJsonLinksDeserializer;
import org.springframework.hateoas.mediatype.collectionjson.Jackson2CollectionJsonModule.CollectionJsonLinksSerializer;
import org.springframework.hateoas.mediatype.collectionjson.Jackson2CollectionJsonModule.CollectionJsonResourceSupportDeserializer;
@@ -33,7 +33,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
* @author Jens Schauder
*/
@JsonDeserialize(using = CollectionJsonResourceSupportDeserializer.class)
abstract class ResourceSupportMixin extends ResourceSupport {
abstract class RepresentationModelMixin extends RepresentationModel<RepresentationModelMixin> {
@Override
@JsonProperty("collection")

View File

@@ -17,7 +17,7 @@ package org.springframework.hateoas.mediatype.hal;
import java.util.Collection;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.CollectionModel;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@@ -34,7 +34,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
* @author Greg Turnquist
*/
@JsonPropertyOrder({ "content", "links" })
abstract class ResourcesMixin<T> extends Resources<T> {
abstract class CollectionModelMixin<T> extends CollectionModel<T> {
@Override
@JsonProperty("_embedded")

View File

@@ -23,7 +23,7 @@ import java.util.List;
import java.util.Map;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.RelProvider;
import org.springframework.hateoas.server.core.EmbeddedWrapper;
import org.springframework.hateoas.server.core.EmbeddedWrappers;
@@ -63,7 +63,7 @@ class HalEmbeddedBuilder {
/**
* Adds the given value to the embeddeds. Will skip doing so if the value is {@literal null} or the content of a
* {@link Resource} is {@literal null}.
* {@link EntityModel} is {@literal null}.
*
* @param source can be {@literal null}.
*/

View File

@@ -15,18 +15,17 @@
*/
package org.springframework.hateoas.mediatype.hal;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.client.LinkDiscoverer;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.server.RelProvider;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.server.RelProvider;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -39,7 +38,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Oliver Drotbohm
*/
@Configuration
@RequiredArgsConstructor
public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
private final RelProvider relProvider;
@@ -47,6 +45,22 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
private final ObjectProvider<HalConfiguration> halConfiguration;
private final MessageSourceAccessor messageSourceAccessor;
/**
* @param relProvider
* @param curieProvider
* @param halConfiguration
* @param messageSourceAccessor
*/
public HalMediaTypeConfiguration(RelProvider relProvider, ObjectProvider<CurieProvider> curieProvider,
ObjectProvider<HalConfiguration> halConfiguration,
@Qualifier("linkRelationMessageSource") MessageSourceAccessor messageSourceAccessor) {
this.relProvider = relProvider;
this.curieProvider = curieProvider;
this.halConfiguration = halConfiguration;
this.messageSourceAccessor = messageSourceAccessor;
}
@Bean
LinkDiscoverer halLinkDisocoverer() {
return new HalLinkDiscoverer();

View File

@@ -29,12 +29,12 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.hal.HalConfiguration.RenderSingleLinks;
import org.springframework.hateoas.server.RelProvider;
import org.springframework.util.Assert;
@@ -65,7 +65,8 @@ import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* Jackson 2 module implementation to render {@link Link} and {@link ResourceSupport} instances in HAL compatible JSON.
* Jackson 2 module implementation to render {@link Link} and {@link RepresentationModel} instances in HAL compatible
* JSON.
*
* @author Alexander Baetz
* @author Oliver Gierke
@@ -81,8 +82,8 @@ public class Jackson2HalModule extends SimpleModule {
super("json-hal-module", new Version(1, 0, 0, null, "org.springframework.hateoas", "spring-hateoas"));
setMixInAnnotation(Link.class, LinkMixin.class);
setMixInAnnotation(ResourceSupport.class, ResourceSupportMixin.class);
setMixInAnnotation(Resources.class, ResourcesMixin.class);
setMixInAnnotation(RepresentationModel.class, RepresentationModelMixin.class);
setMixInAnnotation(CollectionModel.class, CollectionModelMixin.class);
}
/**
@@ -154,8 +155,8 @@ public class Jackson2HalModule extends SimpleModule {
Object currentValue = jgen.getCurrentValue();
if (currentValue instanceof Resources) {
if (mapper.hasCuriedEmbed((Resources<?>) currentValue)) {
if (currentValue instanceof CollectionModel) {
if (mapper.hasCuriedEmbed((CollectionModel<?>) currentValue)) {
curiedLinkPresent = true;
}
}
@@ -287,7 +288,8 @@ public class Jackson2HalModule extends SimpleModule {
}
/**
* Custom {@link JsonSerializer} to render {@link Resource}-Lists in HAL compatible JSON. Renders the list as a Map.
* Custom {@link JsonSerializer} to render {@link EntityModel}-Lists in HAL compatible JSON. Renders the
* list as a Map.
*
* @author Alexander Baetz
* @author Oliver Gierke
@@ -325,10 +327,10 @@ public class Jackson2HalModule extends SimpleModule {
Object currentValue = jgen.getCurrentValue();
if (currentValue instanceof ResourceSupport) {
if (currentValue instanceof RepresentationModel) {
if (embeddedMapper.hasCuriedEmbed(value)) {
((ResourceSupport) currentValue).add(CURIES_REQUIRED_DUE_TO_EMBEDS);
((RepresentationModel<?>) currentValue).add(CURIES_REQUIRED_DUE_TO_EMBEDS);
}
}

View File

@@ -17,7 +17,7 @@ package org.springframework.hateoas.mediatype.hal;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.RepresentationModel;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@@ -32,7 +32,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
* @author Oliver Gierke
* @author Greg Turnquist
*/
public abstract class ResourceSupportMixin extends ResourceSupport {
public abstract class RepresentationModelMixin extends RepresentationModel<RepresentationModelMixin> {
@Override
@JsonProperty("_links")

View File

@@ -28,7 +28,7 @@ import java.util.Map;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.PagedModel;
import org.springframework.hateoas.mediatype.hal.HalLinkRelation;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer;
import org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsModule.HalFormsLinksDeserializer;
@@ -71,7 +71,7 @@ public class HalFormsDocument<T> {
@JsonProperty("page") //
@JsonInclude(Include.NON_NULL) //
private PagedResources.PageMetadata pageMetadata;
private PagedModel.PageMetadata pageMetadata;
@Singular //
@JsonProperty("_links") //

View File

@@ -22,13 +22,13 @@ import java.util.List;
import java.util.Map;
import org.springframework.hateoas.Affordance;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.PagedModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.hal.HalLinkRelation;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
import org.springframework.http.HttpMethod;
@@ -51,9 +51,10 @@ import com.fasterxml.jackson.databind.ser.ContextualSerializer;
class HalFormsSerializers {
/**
* Serializer for {@link Resources}.
* Serializer for {@link CollectionModel}.
*/
static class HalFormsResourceSerializer extends ContainerSerializer<Resource<?>> implements ContextualSerializer {
static class HalFormsResourceSerializer extends ContainerSerializer<EntityModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = -7912243216469101379L;
@@ -61,7 +62,7 @@ class HalFormsSerializers {
HalFormsResourceSerializer(BeanProperty property) {
super(Resource.class, false);
super(EntityModel.class, false);
this.property = property;
}
@@ -70,7 +71,8 @@ class HalFormsSerializers {
}
@Override
public void serialize(Resource<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(EntityModel<?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
HalFormsDocument<?> doc = HalFormsDocument.forResource(value.getContent()) //
.withLinks(value.getLinks()) //
@@ -90,7 +92,7 @@ class HalFormsSerializers {
}
@Override
public boolean hasSingleElement(Resource<?> resource) {
public boolean hasSingleElement(EntityModel<?> resource) {
return false;
}
@@ -107,9 +109,10 @@ class HalFormsSerializers {
}
/**
* Serializer for {@link Resources}
* Serializer for {@link CollectionModel}
*/
static class HalFormsResourcesSerializer extends ContainerSerializer<Resources<?>> implements ContextualSerializer {
static class HalFormsResourcesSerializer extends ContainerSerializer<CollectionModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = -3601146866067500734L;
@@ -118,7 +121,7 @@ class HalFormsSerializers {
HalFormsResourcesSerializer(BeanProperty property, Jackson2HalModule.EmbeddedMapper embeddedMapper) {
super(Resources.class, false);
super(CollectionModel.class, false);
this.property = property;
this.embeddedMapper = embeddedMapper;
@@ -129,17 +132,18 @@ class HalFormsSerializers {
}
@Override
public void serialize(Resources<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(CollectionModel<?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
Map<HalLinkRelation, Object> embeddeds = embeddedMapper.map(value);
HalFormsDocument<?> doc;
if (value instanceof PagedResources) {
if (value instanceof PagedModel) {
doc = HalFormsDocument.empty() //
.withEmbedded(embeddeds) //
.withPageMetadata(((PagedResources<?>) value).getMetadata()) //
.withPageMetadata(((PagedModel<?>) value).getMetadata()) //
.withLinks(value.getLinks()) //
.withTemplates(findTemplates(value));
@@ -165,7 +169,7 @@ class HalFormsSerializers {
}
@Override
public boolean hasSingleElement(Resources<?> resources) {
public boolean hasSingleElement(CollectionModel<?> resources) {
return resources.getContent().size() == 1;
}
@@ -182,12 +186,12 @@ class HalFormsSerializers {
}
/**
* Extract template details from a {@link ResourceSupport}'s {@link Affordance}s.
* Extract template details from a {@link RepresentationModel}'s {@link Affordance}s.
*
* @param resource
* @return
*/
private static Map<String, HalFormsTemplate> findTemplates(ResourceSupport resource) {
private static Map<String, HalFormsTemplate> findTemplates(RepresentationModel<?> resource) {
if (!resource.hasLink(IanaLinkRelations.SELF)) {
return Collections.emptyMap();
@@ -222,7 +226,7 @@ class HalFormsSerializers {
* @param resource
* @param model
*/
private static void validate(ResourceSupport resource, HalFormsAffordanceModel model) {
private static void validate(RepresentationModel<?> resource, HalFormsAffordanceModel model) {
String affordanceUri = model.getURI();
String selfLinkUri = resource.getRequiredLink(IanaLinkRelations.SELF.value()).expand().getHref();

View File

@@ -22,18 +22,18 @@ import java.util.Map;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.PagedModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.hal.CurieProvider;
import org.springframework.hateoas.mediatype.hal.LinkMixin;
import org.springframework.hateoas.mediatype.hal.ResourceSupportMixin;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.EmbeddedMapper;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalHandlerInstantiator;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListDeserializer;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer;
import org.springframework.hateoas.mediatype.hal.LinkMixin;
import org.springframework.hateoas.mediatype.hal.RepresentationModelMixin;
import org.springframework.hateoas.mediatype.hal.forms.HalFormsDeserializers.HalFormsResourcesDeserializer;
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsResourceSerializer;
import org.springframework.hateoas.mediatype.hal.forms.HalFormsSerializers.HalFormsResourcesSerializer;
@@ -80,19 +80,16 @@ class Jackson2HalFormsModule extends SimpleModule {
super("hal-forms-module", new Version(1, 0, 0, null, "org.springframework.hateoas", "spring-hateoas"));
setMixInAnnotation(Link.class, LinkMixin.class);
setMixInAnnotation(ResourceSupport.class, ResourceSupportMixin.class);
setMixInAnnotation(Resources.class, ResourcesMixin.class);
setMixInAnnotation(PagedResources.class, PagedResourcesMixin.class);
setMixInAnnotation(RepresentationModel.class, RepresentationModelMixin.class);
setMixInAnnotation(CollectionModel.class, CollectionModelMixin.class);
setMixInAnnotation(PagedModel.class, PagedModelMixin.class);
setMixInAnnotation(MediaType.class, MediaTypeMixin.class);
addSerializer(new HalFormsResourceSerializer());
}
@JsonSerialize(using = HalFormsResourceSerializer.class)
interface ResourceMixin {}
@JsonSerialize(using = HalFormsResourcesSerializer.class)
abstract class ResourcesMixin<T> extends Resources<T> {
abstract class CollectionModelMixin<T> extends CollectionModel<T> {
@Override
@JsonProperty("_embedded")
@@ -101,7 +98,7 @@ class Jackson2HalFormsModule extends SimpleModule {
public abstract Collection<T> getContent();
}
abstract class PagedResourcesMixin<T> extends PagedResources<T> {
abstract class PagedModelMixin<T> extends PagedModel<T> {
@Override
@JsonProperty("page")

View File

@@ -26,15 +26,15 @@ import java.util.Map;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.PagedResources.PageMetadata;
import org.springframework.hateoas.PagedModel;
import org.springframework.hateoas.PagedModel.PageMetadata;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.JacksonHelper;
import org.springframework.hateoas.mediatype.PropertyUtils;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -72,74 +72,75 @@ public class Jackson2UberModule extends SimpleModule {
super("uber-module", new Version(1, 0, 0, null, "org.springframework.hateoas", "spring-hateoas"));
addSerializer(new UberPagedResourcesSerializer());
addSerializer(new UberResourcesSerializer());
addSerializer(new UberResourceSerializer());
addSerializer(new UberResourceSupportSerializer());
addSerializer(new UberPagedModelSerializer());
addSerializer(new UberCollectionModelSerializer());
addSerializer(new UberEntityModelSerializer());
addSerializer(new UberRepresentationModelSerializer());
setMixInAnnotation(ResourceSupport.class, ResourceSupportMixin.class);
setMixInAnnotation(Resource.class, ResourceMixin.class);
setMixInAnnotation(Resources.class, ResourcesMixin.class);
setMixInAnnotation(PagedResources.class, PagedResourcesMixin.class);
setMixInAnnotation(RepresentationModel.class, RepresentationModelMixin.class);
setMixInAnnotation(EntityModel.class, EntityModelMixin.class);
setMixInAnnotation(CollectionModel.class, CollectionModelMixin.class);
setMixInAnnotation(PagedModel.class, PagedModelMixin.class);
}
/**
* Jackson 2 mixin to handle {@link ResourceSupport} for {@literal UBER+JSON}.
* Jackson 2 mixin to handle {@link RepresentationModel} for {@literal UBER+JSON}.
*
* @author Greg Turnquist
* @since 1.0
*/
@JsonDeserialize(using = UberResourceSupportDeserializer.class)
abstract class ResourceSupportMixin extends ResourceSupport {}
@JsonDeserialize(using = UberRepresentationModelDeserializer.class)
abstract class RepresentationModelMixin extends RepresentationModel<RepresentationModelMixin> {}
/**
* Jackson 2 mixin to handle {@link Resource} for {@literal UBER+JSON}.
* Jackson 2 mixin to handle {@link EntityModel} for {@literal UBER+JSON}.
*
* @author Greg Turnquist
* @since 1.0
*/
@JsonDeserialize(using = UberResourceDeserializer.class)
abstract class ResourceMixin<T> extends Resource<T> {}
@JsonDeserialize(using = UberEntityModelDeserializer.class)
abstract class EntityModelMixin<T> extends EntityModel<T> {}
/**
* Jackson 2 mixin to handle {@link Resources} for {@literal UBER+JSON}.
* Jackson 2 mixin to handle {@link CollectionModel} for {@literal UBER+JSON}.
*
* @author Greg Turnquist
* @since 1.0
*/
@JsonDeserialize(using = UberResourcesDeserializer.class)
abstract class ResourcesMixin<T> extends Resources<T> {}
@JsonDeserialize(using = UberCollectionModelDeserializer.class)
abstract class CollectionModelMixin<T> extends CollectionModel<T> {}
/**
* Jackson 2 mixin to handle {@link PagedResources} for {@literal UBER+JSON}.
* Jackson 2 mixin to handle {@link PagedModel} for {@literal UBER+JSON}.
*
* @author Greg Turnquist
* @since 1.0
*/
@JsonDeserialize(using = UberPagedResourcesDeserializer.class)
abstract class PagedResourcesMixin<T> extends PagedResources<T> {}
@JsonDeserialize(using = UberPagedModelDeserializer.class)
abstract class PagedModelMixin<T> extends PagedModel<T> {}
/**
* Custom {@link JsonSerializer} to render {@link ResourceSupport} into {@literal UBER+JSON}.
* Custom {@link JsonSerializer} to render {@link RepresentationModel} into {@literal UBER+JSON}.
*/
static class UberResourceSupportSerializer extends ContainerSerializer<ResourceSupport>
static class UberRepresentationModelSerializer extends ContainerSerializer<RepresentationModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = -572866287910993300L;
private final BeanProperty property;
UberResourceSupportSerializer(BeanProperty property) {
UberRepresentationModelSerializer(BeanProperty property) {
super(ResourceSupport.class, false);
super(RepresentationModel.class, false);
this.property = property;
}
UberResourceSupportSerializer() {
UberRepresentationModelSerializer() {
this(null);
}
@Override
public void serialize(ResourceSupport value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(RepresentationModel<?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
UberDocument doc = new UberDocument() //
.withUber(new Uber() //
@@ -162,7 +163,7 @@ public class Jackson2UberModule extends SimpleModule {
}
@Override
public boolean hasSingleElement(ResourceSupport value) {
public boolean hasSingleElement(RepresentationModel<?> value) {
return false;
}
@@ -174,31 +175,33 @@ public class Jackson2UberModule extends SimpleModule {
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException {
return new UberResourceSupportSerializer(property);
return new UberRepresentationModelSerializer(property);
}
}
/**
* Custom {@link JsonSerializer} to render {@link Resource} into {@literal UBER+JSON}.
* Custom {@link JsonSerializer} to render {@link EntityModel} into {@literal UBER+JSON}.
*/
static class UberResourceSerializer extends ContainerSerializer<Resource<?>> implements ContextualSerializer {
static class UberEntityModelSerializer extends ContainerSerializer<EntityModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = -5538560800604582741L;
private final BeanProperty property;
UberResourceSerializer(BeanProperty property) {
UberEntityModelSerializer(BeanProperty property) {
super(Resource.class, false);
super(EntityModel.class, false);
this.property = property;
}
UberResourceSerializer() {
UberEntityModelSerializer() {
this(null);
}
@Override
public void serialize(Resource<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(EntityModel<?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
UberDocument doc = new UberDocument().withUber(new Uber() //
.withVersion("1.0") //
@@ -220,7 +223,7 @@ public class Jackson2UberModule extends SimpleModule {
}
@Override
public boolean hasSingleElement(Resource<?> value) {
public boolean hasSingleElement(EntityModel<?> value) {
return false;
}
@@ -232,26 +235,27 @@ public class Jackson2UberModule extends SimpleModule {
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException {
return new UberResourceSerializer(property);
return new UberEntityModelSerializer(property);
}
}
/**
* Custom {@link JsonSerializer} to render {@link Resources} into {@literal UBER+JSON}.
* Custom {@link JsonSerializer} to render {@link CollectionModel} into {@literal UBER+JSON}.
*/
static class UberResourcesSerializer extends ContainerSerializer<Resources<?>> implements ContextualSerializer {
static class UberCollectionModelSerializer extends ContainerSerializer<CollectionModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = 3422019794262694127L;
private BeanProperty property;
UberResourcesSerializer(BeanProperty property) {
UberCollectionModelSerializer(BeanProperty property) {
super(Resources.class, false);
super(CollectionModel.class, false);
this.property = property;
}
UberResourcesSerializer() {
UberCollectionModelSerializer() {
this(null);
}
@@ -260,7 +264,8 @@ public class Jackson2UberModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
*/
@Override
public void serialize(Resources<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(CollectionModel<?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
UberDocument doc = new UberDocument() //
.withUber(new Uber() //
@@ -283,7 +288,7 @@ public class Jackson2UberModule extends SimpleModule {
}
@Override
public boolean hasSingleElement(Resources<?> value) {
public boolean hasSingleElement(CollectionModel<?> value) {
return value.getContent().size() == 1;
}
@@ -295,27 +300,27 @@ public class Jackson2UberModule extends SimpleModule {
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException {
return new UberResourcesSerializer(property);
return new UberCollectionModelSerializer(property);
}
}
/**
* Custom {@link JsonSerializer} to render {@link PagedResources} into {@literal UBER+JSON}.
* Custom {@link JsonSerializer} to render {@link PagedModel} into {@literal UBER+JSON}.
*/
static class UberPagedResourcesSerializer extends ContainerSerializer<PagedResources<?>>
static class UberPagedModelSerializer extends ContainerSerializer<PagedModel<?>>
implements ContextualSerializer {
private static final long serialVersionUID = -7892297813593085984L;
private BeanProperty property;
UberPagedResourcesSerializer(BeanProperty property) {
UberPagedModelSerializer(BeanProperty property) {
super(PagedResources.class, false);
super(PagedModel.class, false);
this.property = property;
}
UberPagedResourcesSerializer() {
UberPagedModelSerializer() {
this(null);
}
@@ -324,7 +329,8 @@ public class Jackson2UberModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
*/
@Override
public void serialize(PagedResources<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(PagedModel<?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
UberDocument doc = new UberDocument() //
.withUber(new Uber() //
@@ -359,7 +365,7 @@ public class Jackson2UberModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
*/
@Override
public boolean hasSingleElement(PagedResources<?> value) {
public boolean hasSingleElement(PagedModel<?> value) {
return value.getContent().size() == 1;
}
@@ -379,24 +385,24 @@ public class Jackson2UberModule extends SimpleModule {
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException {
return new UberPagedResourcesSerializer(property);
return new UberPagedModelSerializer(property);
}
}
/**
* Custom {@link StdDeserializer} to deserialize {@link ResourceSupport}.
* Custom {@link StdDeserializer} to deserialize {@link RepresentationModel}.
*/
static class UberResourceSupportDeserializer extends ContainerDeserializerBase<ResourceSupport>
static class UberRepresentationModelDeserializer extends ContainerDeserializerBase<RepresentationModel<?>>
implements ContextualDeserializer {
private static final long serialVersionUID = -8738539821441549016L;
private final JavaType contentType;
UberResourceSupportDeserializer() {
this(TypeFactory.defaultInstance().constructType(ResourceSupport.class));
UberRepresentationModelDeserializer() {
this(TypeFactory.defaultInstance().constructType(RepresentationModel.class));
}
private UberResourceSupportDeserializer(JavaType contentType) {
private UberRepresentationModelDeserializer(JavaType contentType) {
super(contentType);
this.contentType = contentType;
@@ -407,26 +413,22 @@ public class Jackson2UberModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
*/
@Override
public ResourceSupport deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
public RepresentationModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
UberDocument doc = p.getCodec().readValue(p, UberDocument.class);
Links links = doc.getUber().getLinks();
return doc.getUber().getData().stream() //
RepresentationModel<?> result = doc.getUber().getData().stream() //
.filter(uberData -> !StringUtils.isEmpty(uberData.getName())) //
.findFirst() //
.map(uberData -> convertToResourceSupport(uberData, links)) //
.orElseGet(() -> {
.orElse(null);
ResourceSupport resourceSupport = new ResourceSupport();
resourceSupport.add(links);
return resourceSupport;
});
return result == null ? new RepresentationModel<>().add(links) : result;
}
@NotNull
private ResourceSupport convertToResourceSupport(UberData uberData, Links links) {
private RepresentationModel<?> convertToResourceSupport(UberData uberData, Links links) {
List<UberData> data = uberData.getData();
Map<String, Object> properties;
@@ -437,12 +439,11 @@ public class Jackson2UberModule extends SimpleModule {
properties = data.stream() //
.collect(Collectors.toMap(UberData::getName, UberData::getValue));
}
ResourceSupport resourceSupport = (ResourceSupport) PropertyUtils
RepresentationModel<?> resourceSupport = (RepresentationModel<?>) PropertyUtils
.createObjectFromProperties(this.getContentType().getRawClass(), properties);
resourceSupport.add(links);
return resourceSupport;
return resourceSupport.add(links);
}
/*
@@ -464,9 +465,9 @@ public class Jackson2UberModule extends SimpleModule {
if (property != null) {
JavaType vc = property.getType().getContentType();
return new UberResourceSupportDeserializer(vc);
return new UberRepresentationModelDeserializer(vc);
} else {
return new UberResourceSupportDeserializer(ctxt.getContextualType());
return new UberRepresentationModelDeserializer(ctxt.getContextualType());
}
}
@@ -480,27 +481,27 @@ public class Jackson2UberModule extends SimpleModule {
}
/**
* Custom {@link StdDeserializer} to deserialize {@link Resource}.
* Custom {@link StdDeserializer} to deserialize {@link EntityModel}.
*/
static class UberResourceDeserializer extends ContainerDeserializerBase<Resource<?>>
static class UberEntityModelDeserializer extends ContainerDeserializerBase<EntityModel<?>>
implements ContextualDeserializer {
private static final long serialVersionUID = 1776321413269082414L;
private final JavaType contentType;
UberResourceDeserializer() {
UberEntityModelDeserializer() {
this(TypeFactory.defaultInstance().constructSimpleType(UberDocument.class, new JavaType[0]));
}
private UberResourceDeserializer(JavaType contentType) {
private UberEntityModelDeserializer(JavaType contentType) {
super(contentType);
this.contentType = contentType;
}
@Override
public Resource<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
public EntityModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
UberDocument doc = p.getCodec().readValue(p, UberDocument.class);
Links links = doc.getUber().getLinks();
@@ -514,13 +515,13 @@ public class Jackson2UberModule extends SimpleModule {
}
@NotNull
private Resource<Object> convertToResource(UberData uberData, Links links) {
private EntityModel<Object> convertToResource(UberData uberData, Links links) {
// Primitive type
List<UberData> data = uberData.getData();
if (isPrimitiveType(data)) {
Object scalarValue = data.get(0).getValue();
return new Resource<>(scalarValue, links);
return new EntityModel<>(scalarValue, links);
}
Map<String, Object> properties;
@@ -534,7 +535,7 @@ public class Jackson2UberModule extends SimpleModule {
Object value = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
return new Resource<>(value, links);
return new EntityModel<>(value, links);
}
/*
@@ -556,9 +557,9 @@ public class Jackson2UberModule extends SimpleModule {
if (property != null) {
JavaType vc = property.getType().getContentType();
return new UberResourceDeserializer(vc);
return new UberEntityModelDeserializer(vc);
} else {
return new UberResourceDeserializer(ctxt.getContextualType());
return new UberEntityModelDeserializer(ctxt.getContextualType());
}
}
@@ -572,22 +573,22 @@ public class Jackson2UberModule extends SimpleModule {
}
/**
* Custom {@link StdDeserializer} to deserialize {@link Resources}.
* Custom {@link StdDeserializer} to deserialize {@link CollectionModel}.
*/
static class UberResourcesDeserializer extends ContainerDeserializerBase<Resources<?>>
static class UberCollectionModelDeserializer extends ContainerDeserializerBase<CollectionModel<?>>
implements ContextualDeserializer {
private static final long serialVersionUID = 8722467561709171145L;
private final JavaType contentType;
UberResourcesDeserializer(JavaType contentType) {
UberCollectionModelDeserializer(JavaType contentType) {
super(contentType);
this.contentType = contentType;
}
UberResourcesDeserializer() {
UberCollectionModelDeserializer() {
this(TypeFactory.defaultInstance().constructSimpleType(UberDocument.class, new JavaType[0]));
}
@@ -596,7 +597,7 @@ public class Jackson2UberModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
*/
@Override
public Resources<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
public CollectionModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JavaType rootType = JacksonHelper.findRootType(this.contentType);
@@ -619,9 +620,9 @@ public class Jackson2UberModule extends SimpleModule {
if (property != null) {
JavaType vc = property.getType().getContentType();
return new UberResourcesDeserializer(vc);
return new UberCollectionModelDeserializer(vc);
} else {
return new UberResourcesDeserializer(ctxt.getContextualType());
return new UberCollectionModelDeserializer(ctxt.getContextualType());
}
}
@@ -635,22 +636,22 @@ public class Jackson2UberModule extends SimpleModule {
}
/**
* Custom {@link StdDeserializer} to deserialize {@link PagedResources}.
* Custom {@link StdDeserializer} to deserialize {@link PagedModel}.
*/
static class UberPagedResourcesDeserializer extends ContainerDeserializerBase<PagedResources<?>>
static class UberPagedModelDeserializer extends ContainerDeserializerBase<PagedModel<?>>
implements ContextualDeserializer {
private static final long serialVersionUID = 4123359694609188745L;
private JavaType contentType;
UberPagedResourcesDeserializer(JavaType contentType) {
UberPagedModelDeserializer(JavaType contentType) {
super(contentType);
this.contentType = contentType;
}
UberPagedResourcesDeserializer() {
UberPagedModelDeserializer() {
this(TypeFactory.defaultInstance().constructSimpleType(UberDocument.class, new JavaType[0]));
}
@@ -659,16 +660,16 @@ public class Jackson2UberModule extends SimpleModule {
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
*/
@Override
public PagedResources<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
public PagedModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JavaType rootType = JacksonHelper.findRootType(this.contentType);
UberDocument doc = p.getCodec().readValue(p, UberDocument.class);
Resources<?> resources = extractResources(doc, rootType, this.contentType);
CollectionModel<?> resources = extractResources(doc, rootType, this.contentType);
PageMetadata pageMetadata = extractPagingMetadata(doc);
return new PagedResources<>(resources.getContent(), pageMetadata, resources.getLinks());
return new PagedModel<>(resources.getContent(), pageMetadata, resources.getLinks());
}
/**
@@ -685,9 +686,9 @@ public class Jackson2UberModule extends SimpleModule {
if (property != null) {
JavaType vc = property.getType().getContentType();
return new UberPagedResourcesDeserializer(vc);
return new UberPagedModelDeserializer(vc);
} else {
return new UberPagedResourcesDeserializer(ctxt.getContextualType());
return new UberPagedModelDeserializer(ctxt.getContextualType());
}
}
@@ -701,14 +702,15 @@ public class Jackson2UberModule extends SimpleModule {
}
/**
* Convert an {@link UberDocument} into a {@link Resources}.
* Convert an {@link UberDocument} into a {@link CollectionModel}.
*
* @param doc
* @param rootType
* @param contentType
* @return
*/
private static Resources<?> extractResources(UberDocument doc, JavaType rootType, JavaType contentType) {
private static CollectionModel<?> extractResources(UberDocument doc, JavaType rootType,
JavaType contentType) {
List<Object> content = new ArrayList<>();
@@ -721,7 +723,7 @@ public class Jackson2UberModule extends SimpleModule {
if (uberData.getLinks().isEmpty()) {
List<Link> resourceLinks = new ArrayList<>();
Resource<?> resource = null;
EntityModel<?> resource = null;
List<UberData> data = uberData.getData();
if (data == null) {
@@ -739,7 +741,7 @@ public class Jackson2UberModule extends SimpleModule {
if (isPrimitiveType(itemData)) {
Object scalarValue = itemData.get(0).getValue();
resource = new Resource<>(scalarValue, uberData.getLinks());
resource = new EntityModel<>(scalarValue, uberData.getLinks());
} else {
Map<String, Object> properties;
@@ -751,7 +753,7 @@ public class Jackson2UberModule extends SimpleModule {
}
Object obj = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
resource = new Resource<>(obj, uberData.getLinks());
resource = new EntityModel<>(obj, uberData.getLinks());
}
}
}
@@ -770,16 +772,16 @@ public class Jackson2UberModule extends SimpleModule {
/*
* Either return a Resources<Resource<T>>...
*/
return new Resources<>(content, doc.getUber().getLinks());
return new CollectionModel<>(content, doc.getUber().getLinks());
} else {
/*
* ...or return a Resources<T>
*/
List<Object> resourceLessContent = content.stream().map(item -> (Resource<?>) item).map(Resource::getContent)
.collect(Collectors.toList());
List<Object> resourceLessContent = content.stream().map(item -> (EntityModel<?>) item)
.map(EntityModel::getContent).collect(Collectors.toList());
return new Resources<>(resourceLessContent, doc.getUber().getLinks());
return new CollectionModel<>(resourceLessContent, doc.getUber().getLinks());
}
}

View File

@@ -31,14 +31,14 @@ import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.hateoas.Affordance;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.PagedModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.PropertyUtils;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
@@ -146,16 +146,16 @@ class UberData {
/**
* Set of all Spring HATEOAS resource types.
*/
private static final HashSet<Class<?>> RESOURCE_TYPES = new HashSet<>(
Arrays.asList(ResourceSupport.class, Resource.class, Resources.class, PagedResources.class));
private static final HashSet<Class<?>> RESOURCE_TYPES = new HashSet<>(Arrays.asList(RepresentationModel.class,
EntityModel.class, CollectionModel.class, PagedModel.class));
/**
* Convert a {@link ResourceSupport} into a list of {@link UberData}s, containing links and content.
* Convert a {@link RepresentationModel} into a list of {@link UberData}s, containing links and content.
*
* @param resource
* @return
*/
static List<UberData> extractLinksAndContent(ResourceSupport resource) {
static List<UberData> extractLinksAndContent(RepresentationModel<?> resource) {
List<UberData> data = extractLinks(resource);
@@ -165,12 +165,12 @@ class UberData {
}
/**
* Convert a {@link Resource} into a list of {@link UberData}s, containing links and content.
* Convert a {@link EntityModel} into a list of {@link UberData}s, containing links and content.
*
* @param resource
* @return
*/
static List<UberData> extractLinksAndContent(Resource<?> resource) {
static List<UberData> extractLinksAndContent(EntityModel<?> resource) {
List<UberData> data = extractLinks(resource);
@@ -180,12 +180,13 @@ class UberData {
}
/**
* Convert {@link Resources} into a list of {@link UberData}, with each item nested in a sub-UberData.
* Convert {@link CollectionModel} into a list of {@link UberData}, with each item nested in a
* sub-UberData.
*
* @param resources
* @return
*/
static List<UberData> extractLinksAndContent(Resources<?> resources) {
static List<UberData> extractLinksAndContent(CollectionModel<?> resources) {
List<UberData> data = extractLinks(resources);
@@ -195,9 +196,9 @@ class UberData {
return data;
}
static List<UberData> extractLinksAndContent(PagedResources<?> resources) {
static List<UberData> extractLinksAndContent(PagedModel<?> resources) {
List<UberData> collectionOfResources = extractLinksAndContent((Resources<?>) resources);
List<UberData> collectionOfResources = extractLinksAndContent((CollectionModel<?>) resources);
if (resources.getMetadata() != null) {
@@ -227,12 +228,12 @@ class UberData {
}
/**
* Extract all the direct {@link Link}s and {@link Affordance}-based links from a {@link ResourceSupport}.
* Extract all the direct {@link Link}s and {@link Affordance}-based links from a {@link RepresentationModel}.
*
* @param resource
* @return
*/
private static List<UberData> extractLinks(ResourceSupport resource) {
private static List<UberData> extractLinks(RepresentationModel<?> resource) {
List<UberData> data = new ArrayList<>();
@@ -268,15 +269,15 @@ class UberData {
*/
private static List<UberData> doExtractLinksAndContent(Object item) {
if (item instanceof Resource) {
return extractLinksAndContent((Resource<?>) item);
if (item instanceof EntityModel) {
return extractLinksAndContent((EntityModel<?>) item);
}
if (item instanceof ResourceSupport) {
return extractLinksAndContent((ResourceSupport) item);
if (item instanceof RepresentationModel) {
return extractLinksAndContent((RepresentationModel<?>) item);
}
return extractLinksAndContent(new Resource<>(item));
return extractLinksAndContent(new EntityModel<>(item));
}
/**
@@ -371,7 +372,7 @@ class UberData {
}
/**
* Transform the payload of a {@link Resource} into {@link UberData}.
* Transform the payload of a {@link EntityModel} into {@link UberData}.
*
* @param obj
* @return

View File

@@ -21,7 +21,7 @@ import java.util.Iterator;
import java.util.List;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -157,7 +157,7 @@ public class VndErrors implements Iterable<VndErrors.VndError> {
*
* @author Oliver Gierke
*/
public static class VndError extends ResourceSupport {
public static class VndError extends RepresentationModel<VndError> {
@JsonProperty private final String logref;
@JsonProperty private final String message;

View File

@@ -60,17 +60,17 @@ public interface EntityLinks extends Plugin<Class<?>> {
* {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
LinkBuilder linkForSingleResource(Class<?> type, Object id);
LinkBuilder linkForItemResource(Class<?> type, Object id);
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity.
*
* @see #linkForSingleResource(Class, Object)
* @see #linkForItemResource(Class, Object)
* @param entity the entity type to point to, must not be {@literal null}.
* @return the {@link LinkBuilder} pointing the given entity. Will never be {@literal null}.
* @throws IllegalArgumentException in case the type of the given entity is unknown the entity links infrastructure.
*/
LinkBuilder linkForSingleResource(Identifiable<?> entity);
LinkBuilder linkForItemResource(Identifiable<?> entity);
/**
* Creates a {@link Link} pointing to the collection resource of the given type. The relation type of the link will be
@@ -93,7 +93,7 @@ public interface EntityLinks extends Plugin<Class<?>> {
* {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
Link linkToSingleResource(Class<?> type, Object id);
Link linkToItemResource(Class<?> type, Object id);
/**
* Creates a {@link Link} pointing to single resource backing the given entity. The relation type of the link will be
@@ -103,5 +103,5 @@ public interface EntityLinks extends Plugin<Class<?>> {
* @return the {@link Link} pointing to the resource exposed for the given entity. Will never be {@literal null}.
* @throws IllegalArgumentException in case the type of the given entity is unknown the entity links infrastructure.
*/
Link linkToSingleResource(Identifiable<?> entity);
Link linkToItemResource(Identifiable<?> entity);
}

View File

@@ -15,41 +15,39 @@
*/
package org.springframework.hateoas.server;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.RepresentationModel;
/**
* Interface for components that convert a domain type into a {@link ResourceSupport}.
* Interface for components that convert a domain type into a {@link RepresentationModel}.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
public interface ResourceAssembler<T, D extends ResourceSupport> {
public interface RepresentationModelAssembler<T, D extends RepresentationModel<?>> {
/**
* Converts the given entity into a {@code D}, which extends {@link ResourceSupport}.
* Converts the given entity into a {@code D}, which extends {@link RepresentationModel}.
*
* @param entity
* @return
*/
D toResource(T entity);
D toModel(T entity);
/**
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link ResourceSupport} and wraps them in a
* {@link Resources} instance.
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link RepresentationModel} and wraps them
* in a {@link CollectionModel} instance.
*
* @param entities must not be {@literal null}.
* @return {@link Resources} containing {@code D}.
* @return {@link CollectionModel} containing {@code D}.
*/
default Resources<D> toResources(Iterable<? extends T> entities) {
default CollectionModel<D> toCollectionModel(Iterable<? extends T> entities) {
List<D> resources = new ArrayList<>();
for (T entity : entities) {
resources.add(toResource(entity));
}
return new Resources<>(resources);
return StreamSupport.stream(entities.spliterator(), false) //
.map(this::toModel) //
.collect(Collectors.collectingAndThen(Collectors.toList(), CollectionModel::new));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,23 +15,23 @@
*/
package org.springframework.hateoas.server;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.RepresentationModel;
/**
* SPI interface to allow components to process the {@link ResourceSupport} instances returned from Spring MVC
* SPI interface to allow components to process the {@link RepresentationModel} instances returned from Spring MVC
* controllers.
*
* @see Resource
* @see Resources
*
* @see EntityModel
* @see CollectionModel
* @author Oliver Gierke
*/
public interface ResourceProcessor<T extends ResourceSupport> {
public interface RepresentationModelProcessor<T extends RepresentationModel<?>> {
/**
* Processes the given resource, add links, alter the domain data etc.
*
* Processes the given representation model, add links, alter the domain data etc.
*
* @param resource
* @return the processed resource
*/

View File

@@ -18,65 +18,68 @@ package org.springframework.hateoas.server;
import java.util.ArrayList;
import java.util.List;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.util.Assert;
/**
* A {@link ResourceAssembler} based purely on the domain type, using {@code Resource<T>} as the enclosing
* "resource" type.
* A {@link RepresentationModelAssembler} based purely on the domain type, using {@code EntityRepresentationModel<T>} as
* the enclosing representation model type.
*
* @author Greg Turnquist
* @since 1.0
*/
public interface SimpleResourceAssembler<T> extends ResourceAssembler<T, Resource<T>> {
public interface SimpleRepresentationModelAssembler<T>
extends RepresentationModelAssembler<T, EntityModel<T>> {
/**
* Converts the given entity into a {@link Resource}.
* Converts the given entity into a {@link EntityModel}.
*
* @param entity
* @return
*/
default Resource<T> toResource(T entity) {
Resource<T> resource = new Resource<>(entity);
default EntityModel<T> toModel(T entity) {
EntityModel<T> resource = new EntityModel<>(entity);
addLinks(resource);
return resource;
}
/**
* Define links to add to every individual {@link Resource}.
* Define links to add to every individual {@link EntityModel}.
*
* @param resource
*/
void addLinks(Resource<T> resource);
void addLinks(EntityModel<T> resource);
/**
* Converts all given entities into resources and wraps the collection as a resource as well.
*
* @see #toResource(Object)
* @see #toModel(Object)
* @param entities must not be {@literal null}.
* @return {@link Resources} containing {@link Resource} of {@code T}.
* @return {@link CollectionModel} containing {@link EntityModel} of {@code T}.
*/
@Override
default Resources<Resource<T>> toResources(Iterable<? extends T> entities) {
default CollectionModel<EntityModel<T>> toCollectionModel(
Iterable<? extends T> entities) {
Assert.notNull(entities, "entities must not be null!");
List<Resource<T>> resourceList = new ArrayList<>();
List<EntityModel<T>> resourceList = new ArrayList<>();
for (T entity : entities) {
resourceList.add(toResource(entity));
resourceList.add(toModel(entity));
}
Resources<Resource<T>> resources = new Resources<>(resourceList);
CollectionModel<EntityModel<T>> resources = new CollectionModel<>(
resourceList);
addLinks(resources);
return resources;
}
/**
* Define links to add to the {@link Resources} collection.
* Define links to add to the {@link CollectionModel} collection.
*
* @param resources
*/
void addLinks(Resources<Resource<T>> resources);
void addLinks(CollectionModel<EntityModel<T>> resources);
}

View File

@@ -34,9 +34,9 @@ public abstract class AbstractEntityLinks implements EntityLinks {
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(org.springframework.hateoas.Identifiable)
*/
@Override
public Link linkToSingleResource(Identifiable<?> entity) {
public Link linkToItemResource(Identifiable<?> entity) {
Assert.notNull(entity, "Entity must not be null!");
return linkToSingleResource(entity.getClass(), entity.getId());
return linkToItemResource(entity.getClass(), entity.getId());
}
/*
@@ -44,7 +44,7 @@ public abstract class AbstractEntityLinks implements EntityLinks {
* @see org.springframework.hateoas.EntityLinks#linkForSingleResource(java.lang.Class, java.lang.Object)
*/
@Override
public LinkBuilder linkForSingleResource(Class<?> type, Object id) {
public LinkBuilder linkForItemResource(Class<?> type, Object id) {
return linkFor(type).slash(id);
}
@@ -53,8 +53,8 @@ public abstract class AbstractEntityLinks implements EntityLinks {
* @see org.springframework.hateoas.EntityLinks#linkForSingleResource(org.springframework.hateoas.Identifiable)
*/
@Override
public LinkBuilder linkForSingleResource(Identifiable<?> entity) {
public LinkBuilder linkForItemResource(Identifiable<?> entity) {
Assert.notNull(entity, "Entity must not be null!");
return linkForSingleResource(entity.getClass(), entity.getId());
return linkForItemResource(entity.getClass(), entity.getId());
}
}

View File

@@ -130,7 +130,7 @@ public class ControllerEntityLinks extends AbstractEntityLinks {
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(java.lang.Class, java.lang.Object)
*/
@Override
public Link linkToSingleResource(Class<?> entity, Object id) {
public Link linkToItemResource(Class<?> entity, Object id) {
return linkFor(entity).slash(id).withSelfRel();
}

View File

@@ -74,8 +74,8 @@ public class DelegatingEntityLinks extends AbstractEntityLinks {
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(java.lang.Class, java.lang.Object)
*/
@Override
public Link linkToSingleResource(Class<?> type, Object id) {
return getPluginFor(type).linkToSingleResource(type, id);
public Link linkToItemResource(Class<?> type, Object id) {
return getPluginFor(type).linkToItemResource(type, id);
}
/*

View File

@@ -18,10 +18,10 @@ package org.springframework.hateoas.server.core;
import java.util.Optional;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.EntityModel;
/**
* A wrapper to handle values to be embedded into a {@link Resource}.
* A wrapper to handle values to be embedded into a {@link EntityModel}.
*
* @author Oliver Gierke
*/

View File

@@ -21,7 +21,7 @@ import java.util.Optional;
import org.springframework.aop.support.AopUtils;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.EntityModel;
import org.springframework.util.Assert;
/**
@@ -142,7 +142,7 @@ public class EmbeddedWrappers {
return null;
}
peek = peek instanceof Resource ? ((Resource<Object>) peek).getContent() : peek;
peek = peek instanceof EntityModel ? ((EntityModel<Object>) peek).getContent() : peek;
return AopUtils.getTargetClass(peek);
}

View File

@@ -17,8 +17,8 @@ package org.springframework.hateoas.server.core;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.server.ResourceProcessor;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -26,15 +26,15 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
/**
* Special {@link ResponseEntity} that exposes {@link Link} instances in the contained {@link ResourceSupport} as link
* headers instead of in the body. Note, that this class is not intended to be used directly from user code but by
* Special {@link ResponseEntity} that exposes {@link Link} instances in the contained {@link RepresentationModel} as
* link headers instead of in the body. Note, that this class is not intended to be used directly from user code but by
* support code that will transparently invoke the header exposure. If you use this class from a controller directly,
* the {@link Link}s will not be present in the {@link ResourceSupport} instance anymore when {@link ResourceProcessor}s
* kick in.
* the {@link Link}s will not be present in the {@link RepresentationModel} instance anymore when
* {@link RepresentationModelProcessor}s kick in.
*
* @author Oliver Gierke
*/
public class HeaderLinksResponseEntity<T extends ResourceSupport> extends ResponseEntity<T> {
public class HeaderLinksResponseEntity<T extends RepresentationModel<?>> extends ResponseEntity<T> {
/**
* Creates a new {@link HeaderLinksResponseEntity} from the given {@link ResponseEntity}.
@@ -64,7 +64,7 @@ public class HeaderLinksResponseEntity<T extends ResourceSupport> extends Respon
* @param entity must not be {@literal null}.
* @return
*/
public static <S extends ResourceSupport> HeaderLinksResponseEntity<S> wrap(HttpEntity<S> entity) {
public static <S extends RepresentationModel<?>> HeaderLinksResponseEntity<S> wrap(HttpEntity<S> entity) {
Assert.notNull(entity, "Given HttpEntity must not be null!");
@@ -76,13 +76,13 @@ public class HeaderLinksResponseEntity<T extends ResourceSupport> extends Respon
}
/**
* Wraps the given {@link ResourceSupport} into a {@link HeaderLinksResponseEntity}. Will default the status code to
* {@link HttpStatus#OK}.
* Wraps the given {@link RepresentationModel} into a {@link HeaderLinksResponseEntity}. Will default the status code
* to {@link HttpStatus#OK}.
*
* @param entity must not be {@literal null}.
* @return
*/
public static <S extends ResourceSupport> HeaderLinksResponseEntity<S> wrap(S entity) {
public static <S extends RepresentationModel<?>> HeaderLinksResponseEntity<S> wrap(S entity) {
Assert.notNull(entity, "ResourceSupport must not be null!");
@@ -90,13 +90,13 @@ public class HeaderLinksResponseEntity<T extends ResourceSupport> extends Respon
}
/**
* Returns the {@link Link}s contained in the {@link ResourceSupport} of the given {@link ResponseEntity} as
* Returns the {@link Link}s contained in the {@link RepresentationModel} of the given {@link ResponseEntity} as
* {@link HttpHeaders}.
*
* @param entity must not be {@literal null}.
* @return
*/
private static <T extends ResourceSupport> HttpHeaders getHeadersWithLinks(ResponseEntity<T> entity) {
private static <T extends RepresentationModel<?>> HttpHeaders getHeadersWithLinks(ResponseEntity<T> entity) {
Links links = entity.getBody().getLinks();

View File

@@ -20,12 +20,12 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.CollectionModel;
/**
* Annotation to configure the relation to be used when embedding objects in HAL representations of {@link Resource}s
* and {@link Resources}.
* Annotation to configure the relation to be used when embedding objects in HAL representations of {@link EntityModel}s
* and {@link CollectionModel}.
*
* @author Alexander Baetz
* @author Oliver Gierke

View File

@@ -20,28 +20,28 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
import java.util.Arrays;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.server.ResourceAssembler;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.util.Assert;
/**
* Base class to implement {@link ResourceAssembler}s. Will automate {@link ResourceSupport} instance creation and make
* sure a self-link is always added.
* Base class to implement {@link RepresentationModelAssembler}s. Will automate {@link RepresentationModel} instance
* creation and make sure a self-link is always added.
*
* @author Oliver Gierke
*/
public abstract class IdentifiableResourceAssemblerSupport<T extends Identifiable<?>, D extends ResourceSupport>
extends ResourceAssemblerSupport<T, D> {
public abstract class IdentifiableRepresentationModelAssemblerSupport<T extends Identifiable<?>, D extends RepresentationModel<D>>
extends RepresentationModelAssemblerSupport<T, D> {
private final Class<?> controllerClass;
/**
* Creates a new {@link ResourceAssemblerSupport} using the given controller class and resource type.
* Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type.
*
* @param controllerClass must not be {@literal null}.
* @param resourceType must not be {@literal null}.
*/
public IdentifiableResourceAssemblerSupport(Class<?> controllerClass, Class<D> resourceType) {
public IdentifiableRepresentationModelAssemblerSupport(Class<?> controllerClass, Class<D> resourceType) {
super(controllerClass, resourceType);
this.controllerClass = controllerClass;
@@ -53,21 +53,21 @@ public abstract class IdentifiableResourceAssemblerSupport<T extends Identifiabl
* @param entity must not be {@literal null}.
* @return
*/
protected D createResource(T entity) {
return createResource(entity, new Object[0]);
protected D createModel(T entity) {
return createModel(entity, new Object[0]);
}
protected D createResource(T entity, Object... parameters) {
return createResourceWithId(entity.getId(), entity, parameters);
protected D createModel(T entity, Object... parameters) {
return createModelWithId(entity.getId(), entity, parameters);
}
@Override
protected D createResourceWithId(Object id, T entity, Object... parameters) {
protected D createModelWithId(Object id, T entity, Object... parameters) {
Assert.notNull(entity, "Entity must not be null!");
Assert.notNull(id, "Id must not be null!");
D instance = instantiateResource(entity);
D instance = instantiateModel(entity);
instance.add(linkTo(controllerClass, unwrapIdentifyables(parameters)).slash(id).withSelfRel());
return instance;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,30 +22,31 @@ import java.util.List;
import java.util.Objects;
import org.springframework.beans.BeanUtils;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.server.ResourceAssembler;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.util.Assert;
/**
* Base class to implement {@link ResourceAssembler}s. Will automate {@link ResourceSupport} instance creation and make
* sure a self-link is always added.
* Base class to implement {@link RepresentationModelAssembler}s. Will automate {@link RepresentationModel} instance
* creation and make sure a self-link is always added.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> implements ResourceAssembler<T, D> {
public abstract class RepresentationModelAssemblerSupport<T, D extends RepresentationModel<D>>
implements RepresentationModelAssembler<T, D> {
private final Class<?> controllerClass;
private final Class<D> resourceType;
/**
* Creates a new {@link ResourceAssemblerSupport} using the given controller class and resource type.
* Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type.
*
* @param controllerClass must not be {@literal null}.
* @param resourceType must not be {@literal null}.
*/
public ResourceAssemblerSupport(Class<?> controllerClass, Class<D> resourceType) {
public RepresentationModelAssemblerSupport(Class<?> controllerClass, Class<D> resourceType) {
Assert.notNull(controllerClass, "ControllerClass must not be null!");
Assert.notNull(resourceType, "ResourceType must not be null!");
@@ -56,10 +57,10 @@ public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> imp
/*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceAssembler#toResources(java.lang.Iterable)
* @see org.springframework.hateoas.server.RepresentationModelAssembler#toCollectionModel(java.lang.Iterable)
*/
@Override
public Resources<D> toResources(Iterable<? extends T> entities) {
public CollectionModel<D> toCollectionModel(Iterable<? extends T> entities) {
return this.map(entities).toResources();
}
@@ -74,16 +75,16 @@ public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> imp
* @param id must not be {@literal null}.
* @return
*/
protected D createResourceWithId(Object id, T entity) {
return createResourceWithId(id, entity, new Object[0]);
protected D createModelWithId(Object id, T entity) {
return createModelWithId(id, entity, new Object[0]);
}
protected D createResourceWithId(Object id, T entity, Object... parameters) {
protected D createModelWithId(Object id, T entity, Object... parameters) {
Assert.notNull(entity, "Entity must not be null!");
Assert.notNull(id, "Id must not be null!");
D instance = instantiateResource(entity);
D instance = instantiateModel(entity);
instance.add(linkTo(this.controllerClass, parameters).slash(id).withSelfRel());
return instance;
}
@@ -96,23 +97,23 @@ public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> imp
* @param entity
* @return
*/
protected D instantiateResource(T entity) {
protected D instantiateModel(T entity) {
return BeanUtils.instantiateClass(this.resourceType);
}
static class Builder<T, D extends ResourceSupport> {
static class Builder<T, D extends RepresentationModel<D>> {
private final Iterable<? extends T> entities;
private final ResourceAssemblerSupport<T, D> resourceAssembler;
private final RepresentationModelAssemblerSupport<T, D> resourceAssembler;
Builder(Iterable<? extends T> entities, ResourceAssemblerSupport<T, D> resourceAssembler) {
Builder(Iterable<? extends T> entities, RepresentationModelAssemblerSupport<T, D> resourceAssembler) {
this.entities = Objects.requireNonNull(entities, "entities must not null!");
this.resourceAssembler = resourceAssembler;
}
/**
* Transform a list of {@code T}s into a list of {@link ResourceSupport}s.
* Transform a list of {@code T}s into a list of {@link RepresentationModel}s.
*
* @see #toListOfResources() if you need this transformed list rendered as hypermedia
* @return
@@ -122,20 +123,21 @@ public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> imp
List<D> result = new ArrayList<>();
for (T entity : this.entities) {
result.add(this.resourceAssembler.toResource(entity));
result.add(this.resourceAssembler.toModel(entity));
}
return result;
}
/**
* Converts all given entities into resources and wraps the result in a {@link Resources} instance.
* Converts all given entities into resources and wraps the result in a {@link CollectionModel}
* instance.
*
* @see #toListOfResources() and {@link ResourceAssembler#toResource(Object)}
* @see #toListOfResources() and {@link RepresentationModelAssembler#toModel(Object)}
* @return
*/
public Resources<D> toResources() {
return new Resources<>(toListOfResources());
public CollectionModel<D> toResources() {
return new CollectionModel<>(toListOfResources());
}
}
}

View File

@@ -22,10 +22,10 @@ import java.lang.reflect.Field;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.server.ResourceProcessor;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.hateoas.server.core.HeaderLinksResponseEntity;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
@@ -36,27 +36,27 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/**
* {@link HandlerMethodReturnValueHandler} to post-process the objects returned from controller methods using the
* configured {@link ResourceProcessor}s.
*
* configured {@link RepresentationModelProcessor}s.
*
* @author Oliver Gierke
* @since 0.20
* @soundtrack Doppelkopf - Balance (Von Abseits)
*/
@RequiredArgsConstructor
public class ResourceProcessorHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
public class RepresentationModelProcessorHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
static final ResolvableType RESOURCE_TYPE = ResolvableType.forRawClass(Resource.class);
static final ResolvableType RESOURCES_TYPE = ResolvableType.forRawClass(Resources.class);
static final ResolvableType RESOURCE_TYPE = ResolvableType.forRawClass(EntityModel.class);
static final ResolvableType RESOURCES_TYPE = ResolvableType.forRawClass(CollectionModel.class);
private static final ResolvableType HTTP_ENTITY_TYPE = ResolvableType.forRawClass(HttpEntity.class);
static final Field CONTENT_FIELD = ReflectionUtils.findField(Resources.class, "content");
static final Field CONTENT_FIELD = ReflectionUtils.findField(CollectionModel.class, "content");
static {
ReflectionUtils.makeAccessible(CONTENT_FIELD);
}
private final @NonNull HandlerMethodReturnValueHandler delegate;
private final @NonNull ResourceProcessorInvoker invoker;
private final @NonNull RepresentationModelProcessorInvoker invoker;
private boolean rootLinksAsHeaders = false;
@@ -81,6 +81,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
* @see org.springframework.web.method.support.HandlerMethodReturnValueHandler#handleReturnValue(java.lang.Object, org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest)
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
@@ -91,7 +92,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
}
// No post-processable type found - proceed with delegate
if (!(value instanceof ResourceSupport)) {
if (!(value instanceof RepresentationModel)) {
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
@@ -111,7 +112,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
targetType = returnValueType;
}
ResourceSupport result = invoker.invokeProcessorsFor((ResourceSupport) value, targetType);
RepresentationModel<?> result = invoker.invokeProcessorsFor((RepresentationModel) value, targetType);
delegate.handleReturnValue(rewrapResult(result, returnValue), returnType, mavContainer, webRequest);
}
@@ -119,18 +120,18 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
* Re-wraps the result of the post-processing work into an {@link HttpEntity} or {@link ResponseEntity} if the
* original value was one of those two types. Copies headers and status code from the original value but uses the new
* body.
*
*
* @param newBody the post-processed value.
* @param originalValue the original input value.
* @return
*/
Object rewrapResult(ResourceSupport newBody, Object originalValue) {
Object rewrapResult(RepresentationModel<?> newBody, Object originalValue) {
if (!(originalValue instanceof HttpEntity)) {
return rootLinksAsHeaders ? HeaderLinksResponseEntity.wrap(newBody) : newBody;
}
HttpEntity<ResourceSupport> entity = null;
HttpEntity<RepresentationModel<?>> entity = null;
if (originalValue instanceof ResponseEntity) {
ResponseEntity<?> source = (ResponseEntity<?>) originalValue;

View File

@@ -23,46 +23,47 @@ import java.util.List;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.server.ResourceProcessor;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.hateoas.server.core.EmbeddedWrapper;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Component to easily invoke all {@link ResourceProcessor} instances registered for values of type
* {@link ResourceSupport}.
* Component to easily invoke all {@link RepresentationModelProcessor} instances registered for values of type
* {@link RepresentationModel}.
*
* @author Oliver Gierke
* @since 0.20
* @soundtrack Doppelkopf - Die fabelhaften Vier (Von Abseits)
*/
public class ResourceProcessorInvoker {
public class RepresentationModelProcessorInvoker {
private final List<ProcessorWrapper> processors;
/**
* Creates a new {@link ResourceProcessorInvoker} to consider the given {@link ResourceProcessor} to post-process the
* controller methods return value to before invoking the delegate.
*
* @param processors the {@link ResourceProcessor}s to be considered, must not be {@literal null}.
* Creates a new {@link RepresentationModelProcessorInvoker} to consider the given
* {@link RepresentationModelProcessor} to post-process the controller methods return value to before invoking the
* delegate.
*
* @param processors the {@link RepresentationModelProcessor}s to be considered, must not be {@literal null}.
*/
public ResourceProcessorInvoker(Collection<ResourceProcessor<?>> processors) {
public RepresentationModelProcessorInvoker(Collection<RepresentationModelProcessor<?>> processors) {
Assert.notNull(processors, "ResourceProcessors must not be null!");
this.processors = new ArrayList<>();
for (ResourceProcessor<?> processor : processors) {
for (RepresentationModelProcessor<?> processor : processors) {
ResolvableType processorType = ResolvableType.forClass(ResourceProcessor.class, processor.getClass());
ResolvableType processorType = ResolvableType.forClass(RepresentationModelProcessor.class, processor.getClass());
Class<?> rawType = processorType.getGeneric(0).resolve();
if (Resource.class.isAssignableFrom(rawType)) {
if (EntityModel.class.isAssignableFrom(rawType)) {
this.processors.add(new ResourceProcessorWrapper(processor));
} else if (Resources.class.isAssignableFrom(rawType)) {
} else if (CollectionModel.class.isAssignableFrom(rawType)) {
this.processors.add(new ResourcesProcessorWrapper(processor));
} else {
this.processors.add(new DefaultProcessorWrapper(processor));
@@ -73,12 +74,12 @@ public class ResourceProcessorInvoker {
}
/**
* Invokes all {@link ResourceProcessor} instances registered for the type of the given value.
*
* Invokes all {@link RepresentationModelProcessor} instances registered for the type of the given value.
*
* @param value must not be {@literal null}.
* @return
*/
public <T extends ResourceSupport> T invokeProcessorsFor(T value) {
public <T extends RepresentationModel<T>> T invokeProcessorsFor(T value) {
Assert.notNull(value, "Value must not be null!");
@@ -86,24 +87,25 @@ public class ResourceProcessorInvoker {
}
/**
* Invokes all {@link ResourceProcessor} instances registered for the type of the given value and reference type.
*
* Invokes all {@link RepresentationModelProcessor} instances registered for the type of the given value and reference
* type.
*
* @param value must not be {@literal null}.
* @param referenceType must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public <T extends ResourceSupport> T invokeProcessorsFor(T value, ResolvableType referenceType) {
public <T extends RepresentationModel<T>> T invokeProcessorsFor(T value, ResolvableType referenceType) {
Assert.notNull(value, "Value must not be null!");
Assert.notNull(referenceType, "Reference type must not be null!");
// For Resources implementations, process elements first
if (ResourceProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(referenceType)) {
if (RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(referenceType)) {
Resources<?> resources = (Resources<?>) value;
ResolvableType elementTargetType = ResolvableType.forClass(Resources.class, referenceType.getRawClass())
.getGeneric(0);
CollectionModel<?> resources = (CollectionModel<?>) value;
ResolvableType elementTargetType = ResolvableType
.forClass(CollectionModel.class, referenceType.getRawClass()).getGeneric(0);
List<Object> result = new ArrayList<>(resources.getContent().size());
for (Object element : resources) {
@@ -117,15 +119,16 @@ public class ResourceProcessorInvoker {
result.add(invokeProcessorsFor(element, elementTargetType));
}
ReflectionUtils.setField(ResourceProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD, resources, result);
ReflectionUtils.setField(RepresentationModelProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD, resources,
result);
}
return (T) invokeProcessorsFor((Object) value, referenceType);
return (T) invokeProcessorsFor(Object.class.cast(value), referenceType);
}
/**
* Invokes all registered {@link ResourceProcessor}s registered for the given {@link ResolvableType}.
*
* Invokes all registered {@link RepresentationModelProcessor}s registered for the given {@link ResolvableType}.
*
* @param value the object to process
* @param type
* @return
@@ -135,7 +138,7 @@ public class ResourceProcessorInvoker {
Object currentValue = value;
// Process actual value
for (ResourceProcessorInvoker.ProcessorWrapper wrapper : this.processors) {
for (RepresentationModelProcessorInvoker.ProcessorWrapper wrapper : this.processors) {
if (wrapper.supports(type, currentValue)) {
currentValue = wrapper.invokeProcessor(currentValue);
}
@@ -155,9 +158,9 @@ public class ResourceProcessorInvoker {
}
/**
* Interface to unify interaction with {@link ResourceProcessor}s. The {@link Ordered} rank should be determined by
* the underlying processor.
*
* Interface to unify interaction with {@link RepresentationModelProcessor}s. The {@link Ordered} rank should be
* determined by the underlying processor.
*
* @author Oliver Gierke
*/
private interface ProcessorWrapper extends Ordered {
@@ -165,7 +168,7 @@ public class ResourceProcessorInvoker {
/**
* Returns whether the underlying processor supports the given {@link ResolvableType}. It might also additionally
* inspect the object that would eventually be handed to the processor.
*
*
* @param type the type of object to be post processed, will never be {@literal null}.
* @param value the object that would be passed into the processor eventually, can be {@literal null}.
* @return
@@ -175,33 +178,33 @@ public class ResourceProcessorInvoker {
/**
* Performs the actual invocation of the processor. Implementations can be sure
* {@link #supports(ResolvableType, Object)} has been called before and returned {@literal true}.
*
*
* @param object
*/
Object invokeProcessor(Object object);
<S> S invokeProcessor(S object);
}
/**
* Default implementation of {@link ProcessorWrapper} to generically deal with {@link ResourceSupport} types.
*
* Default implementation of {@link ProcessorWrapper} to generically deal with {@link RepresentationModel} types.
*
* @author Oliver Gierke
*/
private static class DefaultProcessorWrapper implements ResourceProcessorInvoker.ProcessorWrapper {
private static class DefaultProcessorWrapper implements RepresentationModelProcessorInvoker.ProcessorWrapper {
private final ResourceProcessor<?> processor;
private final RepresentationModelProcessor<?> processor;
private final ResolvableType targetType;
/**
* Creates a new {@link DefaultProcessorWrapper} with the given {@link ResourceProcessor}.
*
* Creates a new {@link DefaultProcessorWrapper} with the given {@link RepresentationModelProcessor}.
*
* @param processor must not be {@literal null}.
*/
DefaultProcessorWrapper(ResourceProcessor<?> processor) {
DefaultProcessorWrapper(RepresentationModelProcessor<?> processor) {
Assert.notNull(processor, "Processor must not be null!");
this.processor = processor;
this.targetType = ResolvableType.forClass(ResourceProcessor.class, processor.getClass()).getGeneric(0);
this.targetType = ResolvableType.forClass(RepresentationModelProcessor.class, processor.getClass()).getGeneric(0);
}
/*
@@ -213,14 +216,15 @@ public class ResourceProcessorInvoker {
return isRawTypeAssignable(targetType, getRawType(type));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.ResourceProcessorHandlerMethodReturnValueHandler.PostProcessorWrapper#invokeProcessor(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public Object invokeProcessor(Object object) {
return ((ResourceProcessor<ResourceSupport>) processor).process((ResourceSupport) object);
return ((RepresentationModelProcessor<RepresentationModel<?>>) processor)
.process((RepresentationModel<?>) object);
}
/*
@@ -233,8 +237,8 @@ public class ResourceProcessorInvoker {
}
/**
* Returns the target type the underlying {@link ResourceProcessor} wants to get invoked for.
*
* Returns the target type the underlying {@link RepresentationModelProcessor} wants to get invoked for.
*
* @return the targetType
*/
public ResolvableType getTargetType() {
@@ -243,19 +247,19 @@ public class ResourceProcessorInvoker {
}
/**
* {@link ProcessorWrapper} to deal with {@link ResourceProcessor}s for {@link Resource}s. Will fall back to peeking
* into the {@link Resource}'s content for type resolution.
*
* {@link ProcessorWrapper} to deal with {@link RepresentationModelProcessor}s for {@link EntityModel}s.
* Will fall back to peeking into the {@link EntityModel}'s content for type resolution.
*
* @author Oliver Gierke
*/
private static class ResourceProcessorWrapper extends ResourceProcessorInvoker.DefaultProcessorWrapper {
private static class ResourceProcessorWrapper extends RepresentationModelProcessorInvoker.DefaultProcessorWrapper {
/**
* Creates a new {@link ResourceProcessorWrapper} for the given {@link ResourceProcessor}.
*
* Creates a new {@link ResourceProcessorWrapper} for the given {@link RepresentationModelProcessor}.
*
* @param processor must not be {@literal null}.
*/
public ResourceProcessorWrapper(ResourceProcessor<?> processor) {
public ResourceProcessorWrapper(RepresentationModelProcessor<?> processor) {
super(processor);
}
@@ -266,22 +270,23 @@ public class ResourceProcessorInvoker {
@Override
public boolean supports(ResolvableType type, Object value) {
if (!ResourceProcessorHandlerMethodReturnValueHandler.RESOURCE_TYPE.isAssignableFrom(type)) {
if (!RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCE_TYPE.isAssignableFrom(type)) {
return false;
}
return super.supports(type, value) && isValueTypeMatch((Resource<?>) value, getTargetType());
return super.supports(type, value) && isValueTypeMatch((EntityModel<?>) value, getTargetType());
}
/**
* Returns whether the given {@link Resource} matches the given target {@link ResolvableType}. We inspect the
* {@link Resource}'s value to determine the match.
*
* Returns whether the given {@link EntityModel} matches the given target {@link ResolvableType}. We
* inspect the {@link EntityModel}'s value to determine the match.
*
* @param resource
* @param target must not be {@literal null}.
* @return whether the given {@link Resource} can be assigned to the given target {@link ResolvableType}
* @return whether the given {@link EntityModel} can be assigned to the given target
* {@link ResolvableType}
*/
private static boolean isValueTypeMatch(Resource<?> resource, ResolvableType target) {
private static boolean isValueTypeMatch(EntityModel<?> resource, ResolvableType target) {
if (resource == null || !isRawTypeAssignable(target, resource.getClass())) {
return false;
@@ -293,7 +298,7 @@ public class ResourceProcessorInvoker {
return false;
}
ResolvableType type = findGenericType(target, Resource.class);
ResolvableType type = findGenericType(target, EntityModel.class);
return type != null && type.getGeneric(0).isAssignableFrom(ResolvableType.forClass(content.getClass()));
}
@@ -314,19 +319,19 @@ public class ResourceProcessorInvoker {
}
/**
* {@link ProcessorWrapper} for {@link ResourceProcessor}s targeting {@link Resources}. Will peek into the content of
* the {@link Resources} for type matching decisions if needed.
*
* {@link ProcessorWrapper} for {@link RepresentationModelProcessor}s targeting {@link CollectionModel}.
* Will peek into the content of the {@link CollectionModel} for type matching decisions if needed.
*
* @author Oliver Gierke
*/
public static class ResourcesProcessorWrapper extends ResourceProcessorInvoker.DefaultProcessorWrapper {
public static class ResourcesProcessorWrapper extends RepresentationModelProcessorInvoker.DefaultProcessorWrapper {
/**
* Creates a new {@link ResourcesProcessorWrapper} for the given {@link ResourceProcessor}.
*
* Creates a new {@link ResourcesProcessorWrapper} for the given {@link RepresentationModelProcessor}.
*
* @param processor must not be {@literal null}.
*/
public ResourcesProcessorWrapper(ResourceProcessor<?> processor) {
public ResourcesProcessorWrapper(RepresentationModelProcessor<?> processor) {
super(processor);
}
@@ -337,22 +342,23 @@ public class ResourceProcessorInvoker {
@Override
public boolean supports(ResolvableType type, Object value) {
if (!ResourceProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(type)) {
if (!RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(type)) {
return false;
}
return super.supports(type, value) && isValueTypeMatch((Resources<?>) value, getTargetType());
return super.supports(type, value) && isValueTypeMatch((CollectionModel<?>) value, getTargetType());
}
/**
* Returns whether the given {@link Resources} instance matches the given {@link ResolvableType}. We predict this by
* inspecting the first element of the content of the {@link Resources}.
*
* @param resources the {@link Resources} to inspect.
* Returns whether the given {@link CollectionModel} instance matches the given
* {@link ResolvableType}. We predict this by inspecting the first element of the content of the
* {@link CollectionModel}.
*
* @param resources the {@link CollectionModel} to inspect.
* @param target that target {@link ResolvableType}.
* @return
*/
static boolean isValueTypeMatch(Resources<?> resources, ResolvableType target) {
static boolean isValueTypeMatch(CollectionModel<?> resources, ResolvableType target) {
if (resources == null) {
return false;
@@ -366,7 +372,8 @@ public class ResourceProcessorInvoker {
ResolvableType superType = null;
for (Class<?> resourcesType : Arrays.<Class<?>>asList(resources.getClass(), Resources.class)) {
for (Class<?> resourcesType : Arrays.<Class<?>> asList(resources.getClass(),
CollectionModel.class)) {
superType = getSuperType(target, resourcesType);
@@ -382,8 +389,8 @@ public class ResourceProcessorInvoker {
Object element = content.iterator().next();
ResolvableType resourceType = superType.getGeneric(0);
if (element instanceof Resource) {
return ResourceProcessorWrapper.isValueTypeMatch((Resource<?>) element, resourceType);
if (element instanceof EntityModel) {
return ResourceProcessorWrapper.isValueTypeMatch((EntityModel<?>) element, resourceType);
} else if (element instanceof EmbeddedWrapper) {
return isRawTypeAssignable(resourceType, ((EmbeddedWrapper) element).getRelTargetType());
}
@@ -393,7 +400,7 @@ public class ResourceProcessorInvoker {
/**
* Returns the {@link ResolvableType} for the given raw super class.
*
*
* @param source must not be {@literal null}.
* @param superType must not be {@literal null}.
* @return
@@ -423,12 +430,12 @@ public class ResourceProcessorInvoker {
/**
* Helper extension of {@link AnnotationAwareOrderComparator} to make {@link #getOrder(Object)} public to allow it
* being used in a standalone fashion.
*
*
* @author Oliver Gierke
*/
private static class CustomOrderAwareComparator extends AnnotationAwareOrderComparator {
public static ResourceProcessorInvoker.CustomOrderAwareComparator INSTANCE = new CustomOrderAwareComparator();
public static RepresentationModelProcessorInvoker.CustomOrderAwareComparator INSTANCE = new CustomOrderAwareComparator();
@Override
protected int getOrder(Object obj) {

View File

@@ -22,7 +22,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.hateoas.server.ResourceProcessor;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
@@ -30,7 +30,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
/**
* Special {@link RequestMappingHandlerAdapter} that tweaks the {@link HandlerMethodReturnValueHandlerComposite} to be
* proxied by a {@link ResourceProcessorHandlerMethodReturnValueHandler} which will invoke the {@link ResourceProcessor}
* proxied by a {@link RepresentationModelProcessorHandlerMethodReturnValueHandler} which will invoke the {@link RepresentationModelProcessor}
* s found in the application context and eventually delegate to the originally configured
* {@link HandlerMethodReturnValueHandler}.
* <p/>
@@ -43,12 +43,12 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
* @soundtrack Dopplekopf - Regen für immer (Von Abseits)
*/
@RequiredArgsConstructor
public class ResourceProcessorInvokingHandlerAdapter extends RequestMappingHandlerAdapter {
public class RepresentationModelProcessorInvokingHandlerAdapter extends RequestMappingHandlerAdapter {
private static final Method RETURN_VALUE_HANDLER_METHOD = ReflectionUtils
.findMethod(ResourceProcessorInvokingHandlerAdapter.class, "getReturnValueHandlers");
.findMethod(RepresentationModelProcessorInvokingHandlerAdapter.class, "getReturnValueHandlers");
private @NonNull final ResourceProcessorInvoker invoker;
private @NonNull final RepresentationModelProcessorInvoker invoker;
/*
* (non-Javadoc)
@@ -64,7 +64,7 @@ public class ResourceProcessorInvokingHandlerAdapter extends RequestMappingHandl
// Set up ResourceProcessingHandlerMethodResolver to delegate to originally configured ones
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();
newHandlers.add(new ResourceProcessorHandlerMethodReturnValueHandler(oldHandlers, invoker));
newHandlers.add(new RepresentationModelProcessorHandlerMethodReturnValueHandler(oldHandlers, invoker));
// Configure the new handler to be used
this.setReturnValueHandlers(newHandlers);

View File

@@ -26,7 +26,8 @@ import org.springframework.util.Assert;
/**
* Helper to easily create {@link ParameterizedTypeReference} instances to Spring HATEOAS resource types. They're
* basically a shortcut over using a verbose {@code new ParameterizedTypeReference<Resources<DomainType>>()}.
* basically a shortcut over using a verbose
* {@code new ParameterizedTypeReference<CollectionRepresentationModel<DomainType>>()}.
*
* @author Oliver Gierke
* @since 0.17
@@ -34,31 +35,31 @@ import org.springframework.util.Assert;
public class TypeReferences {
/**
* A {@link ParameterizedTypeReference} to return a {@link org.springframework.hateoas.Resource} of some type.
* A {@link ParameterizedTypeReference} to return a {@link org.springframework.hateoas.EntityModel} of some type.
*
* @author Oliver Gierke
* @since 0.17
*/
public static class ResourceType<T>
extends SyntheticParameterizedTypeReference<org.springframework.hateoas.Resource<T>> {}
public static class EntityModelType<T>
extends SyntheticParameterizedTypeReference<org.springframework.hateoas.EntityModel<T>> {}
/**
* A {@link ParameterizedTypeReference} to return a {@link org.springframework.hateoas.Resources} of some type.
* A {@link ParameterizedTypeReference} to return a {@link org.springframework.hateoas.CollectionModel} of some type.
*
* @author Oliver Gierke
* @since 0.17
*/
public static class ResourcesType<T>
extends SyntheticParameterizedTypeReference<org.springframework.hateoas.Resources<T>> {}
public static class CollectionModelType<T>
extends SyntheticParameterizedTypeReference<org.springframework.hateoas.CollectionModel<T>> {}
/**
* A {@link ParameterizedTypeReference} to return a {@link org.springframework.hateoas.PagedResources} of some type.
* A {@link ParameterizedTypeReference} to return a {@link org.springframework.hateoas.PagedModel} of some type.
*
* @author Oliver Gierke
* @since 0.17
*/
public static class PagedResourcesType<T>
extends SyntheticParameterizedTypeReference<org.springframework.hateoas.PagedResources<T>> {}
public static class PagedModelType<T>
extends SyntheticParameterizedTypeReference<org.springframework.hateoas.PagedModel<T>> {}
/**
* Special {@link ParameterizedTypeReference} to customize the generic type detection and eventually return a
@@ -144,7 +145,7 @@ public class TypeReferences {
}
/**
* A sythetic {@link ParameterizedType}.
* A synthetic {@link ParameterizedType}.
*
* @author Oliver Gierke
* @since 0.17

View File

@@ -35,7 +35,7 @@ import org.springframework.web.util.UriComponentsBuilder;
class UriComponentsBuilderFactory {
static final String REQUEST_ATTRIBUTES_MISSING = "Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?";
private static final String CACHE_KEY = ControllerLinkBuilder.class.getName() + "#BUILDER_CACHE";
private static final String CACHE_KEY = UriComponentsBuilderFactory.class.getName() + "#BUILDER_CACHE";
/**
* Returns a {@link UriComponentsBuilder} obtained from the current servlet mapping with scheme tweaked in case the

View File

@@ -1,6 +1,6 @@
/**
* Spring MVC helper classes to build {@link org.springframework.hateoas.Link}s and assemble
* {@link org.springframework.hateoas.ResourceSupport} types.
* {@link org.springframework.hateoas.RepresentationModel} types.
*/
package org.springframework.hateoas.server.mvc;

View File

@@ -18,41 +18,40 @@ package org.springframework.hateoas.server.reactive;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.server.ResourceAssembler;
import org.springframework.hateoas.server.SimpleResourceAssembler;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.hateoas.server.SimpleRepresentationModelAssembler;
import org.springframework.web.server.ServerWebExchange;
/**
* Reactive variant of {@link ResourceAssembler} combined with {@link SimpleResourceAssembler}.
* Reactive variant of {@link RepresentationModelAssembler} combined with {@link SimpleRepresentationModelAssembler}.
*
* @author Greg Turnquist
* @since 1.0
* @author Oliver Drotbohm
*/
public interface ReactiveResourceAssembler<T, D extends ResourceSupport> {
public interface ReactiveRepresentationModelAssembler<T, D extends RepresentationModel<D>> {
/**
* Converts the given entity into a {@code D}, which extends {@link ResourceSupport}.
* Converts the given entity into a {@code D}, which extends {@link RepresentationModel}.
*
* @param entity
* @return
*/
Mono<D> toResource(T entity, ServerWebExchange exchange);
Mono<D> toModel(T entity, ServerWebExchange exchange);
/**
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link ResourceSupport} and wraps
* them in a {@link Resources} instance.
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link RepresentationModel} and wraps them
* in a {@link CollectionModel} instance.
*
* @param entities must not be {@literal null}.
* @return {@link Resources} containing {@code D}.
* @return {@link CollectionModel} containing {@code D}.
*/
default Mono<Resources<D>> toResources(Flux<? extends T> entities, ServerWebExchange exchange) {
default Mono<CollectionModel<D>> toCollectionModel(Flux<? extends T> entities,
ServerWebExchange exchange) {
return entities
.flatMap(entity -> toResource(entity, exchange))
.collectList()
.map(listOfResources -> new Resources<>(listOfResources));
return entities.flatMap(entity -> toModel(entity, exchange)) //
.collectList() //
.map(CollectionModel::new);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.hateoas.server.reactive;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.hateoas.server.SimpleRepresentationModelAssembler;
import org.springframework.web.server.ServerWebExchange;
/**
* Reactive variant of {@link RepresentationModelAssembler} combined with {@link SimpleRepresentationModelAssembler}.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
public interface SimpleReactiveRepresentationModelAssembler<T>
extends ReactiveRepresentationModelAssembler<T, EntityModel<T>> {
/**
* Converts the given entity into a {@link EntityModel} wrapped in a {@link Mono}.
*
* @param entity
* @return
*/
@Override
default Mono<EntityModel<T>> toModel(T entity, ServerWebExchange exchange) {
EntityModel<T> resource = new EntityModel<>(entity);
return Mono.just(addLinks(resource, exchange));
}
/**
* Define links to add to every individual {@link EntityModel}.
*
* @param resource
*/
default EntityModel<T> addLinks(EntityModel<T> resource, ServerWebExchange exchange) {
return resource;
}
/**
* Converts all given entities into resources and wraps the collection as a resource as well.
*
* @see #toResource(Object, ServerWebExchange)
* @param entities must not be {@literal null}.
* @return {@link CollectionModel} containing {@link EntityModel} of {@code T}.
*/
default Mono<CollectionModel<EntityModel<T>>> toCollectionModel(
Flux<? extends T> entities, ServerWebExchange exchange) {
return entities //
.flatMap(entity -> toModel(entity, exchange)) //
.collectList() //
.map(CollectionModel::new) //
.map(it -> addLinks(it, exchange));
}
/**
* Define links to add to the {@link CollectionModel} collection.
*
* @param resources
*/
default CollectionModel<EntityModel<T>> addLinks(
CollectionModel<EntityModel<T>> resources, ServerWebExchange exchange) {
return resources;
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.hateoas.server.reactive;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.server.ResourceAssembler;
import org.springframework.hateoas.server.SimpleResourceAssembler;
import org.springframework.web.server.ServerWebExchange;
/**
* Reactive variant of {@link ResourceAssembler} combined with {@link SimpleResourceAssembler}.
*
* @author Greg Turnquist
* @since 1.0
*/
public interface SimpleReactiveResourceAssembler<T> extends ReactiveResourceAssembler<T, Resource<T>> {
/**
* Converts the given entity into a {@link Resource} wrapped in a {@link Mono}.
*
* @param entity
* @return
*/
default Mono<Resource<T>> toResource(T entity, ServerWebExchange exchange) {
Resource<T> resource = new Resource<>(entity);
addLinks(resource, exchange);
return Mono.just(resource);
}
/**
* Define links to add to every individual {@link Resource}.
*
* @param resource
*/
void addLinks(Resource<T> resource, ServerWebExchange exchange);
/**
* Converts all given entities into resources and wraps the collection as a resource as well.
*
* @see #toResource(Object, ServerWebExchange)
* @param entities must not be {@literal null}.
* @return {@link Resources} containing {@link Resource} of {@code T}.
*/
default Mono<Resources<Resource<T>>> toResources(Flux<? extends T> entities, ServerWebExchange exchange) {
return entities //
.flatMap(entity -> toResource(entity, exchange)) //
.collectList() //
.map(listOfResources -> {
Resources<Resource<T>> resources = new Resources<>(listOfResources);
addLinks(resources, exchange);
return resources;
});
}
/**
* Define links to add to the {@link Resources} collection.
*
* @param resources
*/
void addLinks(Resources<Resource<T>> resources, ServerWebExchange exchange);
}