#831 - Polishing.
Significant rework of the nullability annotation introduction to not produce any warnings anymore. Added nullability annotations where still missing. Removed the ones that were added erroneously. Tightened nullability contracts in a couple of places, most prominently LinkRelationProvider, that now uses a LookupContext as plugin delimiter so that implementations can selectively be looked up for item or collection link relation lookup. Removed obsolete declaration of Findbugs dependency.
This commit is contained in:
@@ -27,6 +27,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -74,6 +75,7 @@ public class Affordance {
|
||||
* @param mediaType
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends AffordanceModel> T getAffordanceModel(MediaType mediaType) {
|
||||
return (T) this.affordanceModels.get(mediaType);
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@@ -124,7 +125,7 @@ public class CollectionModel<T> extends RepresentationModel<CollectionModel<T>>
|
||||
* @see org.springframework.hateoas.ResourceSupport#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
|
||||
@@ -89,7 +89,7 @@ public class EntityModel<T> extends RepresentationModel<EntityModel<T>> {
|
||||
* @see org.springframework.hateoas.ResourceSupport#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -459,18 +458,17 @@ public class Link implements Serializable {
|
||||
|
||||
/**
|
||||
* Factory method to easily create {@link Link} instances from RFC-5988 compatible {@link String} representations of a
|
||||
* link. Will return {@literal null} if input {@link String} is either empty or {@literal null}.
|
||||
* link.
|
||||
*
|
||||
* @param element an RFC-5899 compatible representation of a link.
|
||||
* @throws IllegalArgumentException if a non-empty {@link String} was given that does not adhere to RFC-5899.
|
||||
* @throws IllegalArgumentException if a {@link String} was given that does not adhere to RFC-5899.
|
||||
* @throws IllegalArgumentException if no {@code rel} attribute could be found.
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public static Link valueOf(String element) {
|
||||
|
||||
if (!StringUtils.hasText(element)) {
|
||||
return null;
|
||||
throw new IllegalArgumentException(String.format("Given link header %s is not RFC5988 compliant!", element));
|
||||
}
|
||||
|
||||
Matcher matcher = URI_AND_ATTRIBUTES_PATTERN.matcher(element);
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -95,7 +96,7 @@ public class Links implements Iterable<Link> {
|
||||
* @param source a comma separated list of {@link Link} representations.
|
||||
* @return the {@link Links} represented by the given {@link String}.
|
||||
*/
|
||||
public static Links parse(String source) {
|
||||
public static Links parse(@Nullable String source) {
|
||||
|
||||
if (!StringUtils.hasText(source)) {
|
||||
return NONE;
|
||||
@@ -416,13 +417,13 @@ public class Links implements Iterable<Link> {
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object arg0) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (!(arg0 instanceof Links)) {
|
||||
if (!(obj instanceof Links)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Links that = (Links) arg0;
|
||||
Links that = (Links) obj;
|
||||
|
||||
return this.links.equals(that.links);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public class PagedModel<T> extends CollectionModel<T> {
|
||||
* @see org.springframework.hateoas.Resources#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
@@ -262,7 +262,7 @@ public class PagedModel<T> extends CollectionModel<T> {
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@@ -238,7 +239,7 @@ public class RepresentationModel<T extends RepresentationModel<? extends T>> {
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
@@ -91,7 +92,7 @@ class StringLinkRelation implements LinkRelation, Serializable {
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.hateoas.client;
|
||||
|
||||
import net.minidev.json.JSONArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
@@ -25,11 +27,11 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minidev.json.JSONArray;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.jayway.jsonpath.InvalidPathException;
|
||||
@@ -124,7 +126,7 @@ public class JsonPathLinkDiscoverer implements LinkDiscoverer {
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(MediaType delimiter) {
|
||||
public boolean supports(@NonNull MediaType delimiter) {
|
||||
|
||||
return this.mediaTypes.stream() //
|
||||
.anyMatch(mediaType -> mediaType.isCompatibleWith(delimiter));
|
||||
|
||||
@@ -57,11 +57,11 @@ class Rels {
|
||||
/**
|
||||
* Returns the link contained in the given representation of the given {@link MediaType}.
|
||||
*
|
||||
* @param representation
|
||||
* @param mediaType
|
||||
* @param representation will never be {@literal null}.
|
||||
* @param mediaType will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Optional<Link> findInResponse(@Nullable String representation, @Nullable MediaType mediaType);
|
||||
Optional<Link> findInResponse(String representation, MediaType mediaType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -207,6 +207,9 @@ public class Traverson {
|
||||
*/
|
||||
public class TraversalBuilder {
|
||||
|
||||
private static final String MEDIA_TYPE_HEADER_NOT_FOUND = "Response for request to %s did not expose a content type! Unable to identify links!";
|
||||
private static final String LINK_NOT_FOUND = "Expected to find link with rel '%s' in response %s!";
|
||||
|
||||
private final List<Hop> rels = new ArrayList<>();
|
||||
private Map<String, Object> templateParameters = new HashMap<>();
|
||||
private HttpHeaders headers = new HttpHeaders();
|
||||
@@ -224,9 +227,9 @@ public class Traverson {
|
||||
|
||||
Assert.notNull(rels, "Rels must not be null!");
|
||||
|
||||
for (String rel : rels) {
|
||||
this.rels.add(Hop.rel(rel));
|
||||
}
|
||||
Arrays.stream(rels) //
|
||||
.map(Hop::rel) //
|
||||
.forEach(this.rels::add);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -327,6 +330,7 @@ public class Traverson {
|
||||
HttpEntity<?> requestEntity = prepareRequest(mergeHeaders(this.headers, uriAndHeaders.getHttpHeaders()));
|
||||
|
||||
String forObject = operations.exchange(uriAndHeaders.getUri(), GET, requestEntity, String.class).getBody();
|
||||
|
||||
return JsonPath.read(forObject, jsonPath);
|
||||
}
|
||||
|
||||
@@ -359,7 +363,7 @@ public class Traverson {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the templated {@link Link} found for the last rel in the rels configured to follow.
|
||||
* Returns the templated {@link Link} found for the last relation in the rels configured to follow.
|
||||
*
|
||||
* @return
|
||||
* @since 0.17
|
||||
@@ -370,28 +374,28 @@ public class Traverson {
|
||||
|
||||
private Link traverseToLink(boolean expandFinalUrl) {
|
||||
|
||||
Assert.isTrue(this.rels.size() > 0, "At least one rel needs to be provided!");
|
||||
Assert.isTrue(rels.size() > 0, "At least one rel needs to be provided!");
|
||||
|
||||
URIAndHeaders expandedFinalUriAndHeaders = traverseToExpandedFinalUrl();
|
||||
UriStringAndHeaders finalUriAndHeaders = traverseToFinalUrl();
|
||||
|
||||
return new Link(expandFinalUrl ? expandedFinalUriAndHeaders.getUri().toString() : finalUriAndHeaders.getUri(),
|
||||
this.rels.get(this.rels.size() - 1).getRel());
|
||||
rels.get(rels.size() - 1).getRel());
|
||||
}
|
||||
|
||||
private UriStringAndHeaders traverseToFinalUrl() {
|
||||
|
||||
UriStringAndHeaders uriAndHeaders = getAndFindLinkWithRel(baseUri.toString(), this.rels.iterator(),
|
||||
HttpHeaders.EMPTY);
|
||||
UriStringAndHeaders uriAndHeaders = getAndFindLinkWithRel(baseUri.toString(), rels.iterator(), HttpHeaders.EMPTY);
|
||||
|
||||
return new UriStringAndHeaders(new UriTemplate(uriAndHeaders.getUri()).toString(),
|
||||
uriAndHeaders.getHttpHeaders());
|
||||
}
|
||||
|
||||
private URIAndHeaders traverseToExpandedFinalUrl() {
|
||||
|
||||
UriStringAndHeaders uriAndHeaders = getAndFindLinkWithRel(baseUri.toString(), this.rels.iterator(),
|
||||
HttpHeaders.EMPTY);
|
||||
return new URIAndHeaders(new UriTemplate(uriAndHeaders.getUri()).expand(this.templateParameters),
|
||||
UriStringAndHeaders uriAndHeaders = getAndFindLinkWithRel(baseUri.toString(), rels.iterator(), HttpHeaders.EMPTY);
|
||||
|
||||
return new URIAndHeaders(new UriTemplate(uriAndHeaders.getUri()).expand(templateParameters),
|
||||
uriAndHeaders.getHttpHeaders());
|
||||
}
|
||||
|
||||
@@ -402,28 +406,28 @@ public class Traverson {
|
||||
}
|
||||
|
||||
HttpEntity<?> request = prepareRequest(mergeHeaders(this.headers, extraHeaders));
|
||||
UriTemplate template = new UriTemplate(uri);
|
||||
URI target = new UriTemplate(uri).expand();
|
||||
|
||||
ResponseEntity<String> responseEntity = operations.exchange(template.expand(), GET, request, String.class);
|
||||
ResponseEntity<String> responseEntity = operations.exchange(target, GET, request, String.class);
|
||||
MediaType contentType = responseEntity.getHeaders().getContentType();
|
||||
|
||||
if (contentType == null) {
|
||||
throw new IllegalStateException(String.format(MEDIA_TYPE_HEADER_NOT_FOUND, target));
|
||||
}
|
||||
|
||||
String responseBody = responseEntity.getBody();
|
||||
|
||||
Hop thisHop = rels.next();
|
||||
Rel rel = Rels.getRelFor(thisHop.getRel(), discoverers);
|
||||
|
||||
Link link = rel.findInResponse(responseBody, contentType) //
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
String.format("Expected to find link with rel '%s' in response %s!", rel, responseBody)));
|
||||
Link link = rel.findInResponse(responseBody == null ? "" : responseBody, contentType) //
|
||||
.orElseThrow(() -> new IllegalStateException(String.format(LINK_NOT_FOUND, rel, responseBody)));
|
||||
|
||||
/*
|
||||
* Don't expand if the parameters are empty
|
||||
*/
|
||||
if (!thisHop.hasParameters()) {
|
||||
return getAndFindLinkWithRel(link.getHref(), rels, thisHop.getHeaders());
|
||||
} else {
|
||||
return getAndFindLinkWithRel(link.expand(thisHop.getMergedParameters(this.templateParameters)).getHref(), rels,
|
||||
thisHop.getHeaders());
|
||||
}
|
||||
String linkTarget = thisHop.hasParameters() //
|
||||
? link.expand(thisHop.getMergedParameters(templateParameters)).getHref() //
|
||||
: link.getHref();
|
||||
|
||||
return getAndFindLinkWithRel(linkTarget, rels, thisHop.getHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/**
|
||||
* Client side support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas.client;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -24,6 +24,7 @@ import org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
import org.springframework.hateoas.client.LinkDiscoverer;
|
||||
import org.springframework.hateoas.client.LinkDiscoverers;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider.LookupContext;
|
||||
import org.springframework.hateoas.server.core.AnnotationLinkRelationProvider;
|
||||
import org.springframework.hateoas.server.core.DefaultLinkRelationProvider;
|
||||
import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider;
|
||||
@@ -83,14 +84,15 @@ class HateoasConfiguration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
DelegatingLinkRelationProvider _relProvider(PluginRegistry<LinkRelationProvider, Class<?>> relProviderPluginRegistry) {
|
||||
DelegatingLinkRelationProvider _relProvider(
|
||||
PluginRegistry<LinkRelationProvider, LookupContext> relProviderPluginRegistry) {
|
||||
return new DelegatingLinkRelationProvider(relProviderPluginRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PluginRegistryFactoryBean<LinkRelationProvider, Class<?>> relProviderPluginRegistry() {
|
||||
PluginRegistryFactoryBean<LinkRelationProvider, LookupContext> relProviderPluginRegistry() {
|
||||
|
||||
PluginRegistryFactoryBean<LinkRelationProvider, Class<?>> factory = new PluginRegistryFactoryBean<>();
|
||||
PluginRegistryFactoryBean<LinkRelationProvider, LookupContext> factory = new PluginRegistryFactoryBean<>();
|
||||
|
||||
factory.setType(LinkRelationProvider.class);
|
||||
factory.setExclusions(new Class[] { DelegatingLinkRelationProvider.class });
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.http.codec.CodecConfigurer;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -92,6 +93,7 @@ class WebFluxHateoasConfiguration {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
@@ -143,5 +145,4 @@ class WebFluxHateoasConfiguration {
|
||||
configurer.registerDefaults(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2Http
|
||||
import org.springframework.hateoas.server.mvc.UriComponentsContributor;
|
||||
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilderFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@@ -107,6 +108,7 @@ class WebMvcHateoasConfiguration {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/**
|
||||
* Spring container configuration support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-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,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.beans.FeatureDescriptor;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -31,8 +34,6 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
@@ -46,6 +47,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public class PropertyUtils {
|
||||
|
||||
@@ -67,43 +69,45 @@ public class PropertyUtils {
|
||||
}
|
||||
|
||||
return getPropertyDescriptors(object.getClass()) //
|
||||
.collect(HashMap::new, //
|
||||
(hashMap, descriptor) -> {
|
||||
try {
|
||||
Method readMethod = descriptor.getReadMethod();
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
hashMap.put(descriptor.getName(), readMethod.invoke(object));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, //
|
||||
HashMap::putAll);
|
||||
.collect(HashMap::new, (hashMap, descriptor) -> {
|
||||
|
||||
try {
|
||||
|
||||
Method readMethod = descriptor.getReadMethod();
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
hashMap.put(descriptor.getName(), readMethod.invoke(object));
|
||||
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}, HashMap::putAll);
|
||||
}
|
||||
|
||||
public static List<String> findPropertyNames(ResolvableType resolvableType) {
|
||||
|
||||
Class<?> type = resolvableType.getRawClass();
|
||||
|
||||
if (WebStack.WEBFLUX.isAvailable()) {
|
||||
if (Mono.class.equals(resolvableType.getRawClass()) || Flux.class.equals(resolvableType.getRawClass())) {
|
||||
if (Mono.class.equals(type) || Flux.class.equals(type)) {
|
||||
ResolvableType generic = resolvableType.getGeneric(0);
|
||||
return findPropertyNames(generic);
|
||||
}
|
||||
}
|
||||
|
||||
if (resolvableType.getRawClass() == null) {
|
||||
if (type == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (resolvableType.getRawClass().equals(EntityModel.class)) {
|
||||
Class<?> genericEntityModelParameter = resolvableType.resolveGeneric(0);
|
||||
|
||||
if (genericEntityModelParameter == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return findPropertyNames(genericEntityModelParameter);
|
||||
} else {
|
||||
return findPropertyNames(resolvableType.getRawClass());
|
||||
if (!type.equals(EntityModel.class)) {
|
||||
return findPropertyNames(type);
|
||||
}
|
||||
|
||||
Class<?> genericEntityModelParameter = resolvableType.resolveGeneric(0);
|
||||
|
||||
return genericEntityModelParameter == null //
|
||||
? Collections.emptyList() //
|
||||
: findPropertyNames(genericEntityModelParameter);
|
||||
}
|
||||
|
||||
public static List<String> findPropertyNames(Class<?> clazz) {
|
||||
@@ -118,16 +122,19 @@ public class PropertyUtils {
|
||||
T obj = BeanUtils.instantiateClass(clazz);
|
||||
|
||||
properties.forEach((key, value) -> {
|
||||
Optional<PropertyDescriptor> possibleProperty = Optional.ofNullable(BeanUtils.getPropertyDescriptor(clazz, key));
|
||||
possibleProperty.ifPresent(property -> {
|
||||
try {
|
||||
Method writeMethod = property.getWriteMethod();
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
writeMethod.invoke(obj, value);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
Optional.ofNullable(BeanUtils.getPropertyDescriptor(clazz, key)) //
|
||||
.ifPresent(property -> {
|
||||
try {
|
||||
|
||||
Method writeMethod = property.getWriteMethod();
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
writeMethod.invoke(obj, value);
|
||||
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return obj;
|
||||
@@ -135,7 +142,7 @@ public class PropertyUtils {
|
||||
|
||||
/**
|
||||
* Take a {@link Class} and find all properties that are NOT to be ignored, and return them as a {@link Stream}.
|
||||
*
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
@@ -159,16 +166,14 @@ public class PropertyUtils {
|
||||
|
||||
Field descriptorField = ReflectionUtils.findField(clazz, descriptor.getName());
|
||||
|
||||
if (descriptorField == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return toBeIgnoredByJackson(AnnotationUtils.getAnnotations(descriptorField));
|
||||
return descriptorField == null //
|
||||
? false //
|
||||
: toBeIgnoredByJackson(AnnotationUtils.getAnnotations(descriptorField));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given {@link PropertyDescriptor} has {@link JsonIgnore} on the getter.
|
||||
*
|
||||
*
|
||||
* @param descriptor
|
||||
* @return
|
||||
*/
|
||||
@@ -178,26 +183,24 @@ public class PropertyUtils {
|
||||
|
||||
/**
|
||||
* Scan a list of {@link Annotation}s for {@link JsonIgnore} annotations.
|
||||
*
|
||||
*
|
||||
* @param annotations
|
||||
* @return
|
||||
*/
|
||||
private static boolean toBeIgnoredByJackson(@Nullable Annotation[] annotations) {
|
||||
|
||||
if (annotations == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.stream(annotations) //
|
||||
.filter(annotation -> annotation.annotationType().equals(JsonIgnore.class)) //
|
||||
.findFirst() //
|
||||
.map(annotation -> (Boolean) AnnotationUtils.getAnnotationAttributes(annotation).get("value")) //
|
||||
.orElse(false);
|
||||
return annotations == null //
|
||||
? false
|
||||
: Arrays.stream(annotations) //
|
||||
.filter(annotation -> annotation.annotationType().equals(JsonIgnore.class)) //
|
||||
.findFirst() //
|
||||
.map(annotation -> (Boolean) AnnotationUtils.getAnnotationAttributes(annotation).get("value")) //
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a field name is to be ignored due to {@link JsonIgnoreProperties}.
|
||||
*
|
||||
*
|
||||
* @param clazz
|
||||
* @param field
|
||||
* @return
|
||||
@@ -206,15 +209,12 @@ public class PropertyUtils {
|
||||
|
||||
Annotation[] annotations = AnnotationUtils.getAnnotations(clazz);
|
||||
|
||||
if (annotations == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.stream(annotations) //
|
||||
.filter(annotation -> annotation.annotationType().equals(JsonIgnoreProperties.class)) //
|
||||
.map(annotation -> (String[]) AnnotationUtils.getAnnotationAttributes(annotation).get("value")) //
|
||||
.flatMap(Arrays::stream) //
|
||||
.anyMatch(propertyName -> propertyName.equalsIgnoreCase(field));
|
||||
return annotations == null //
|
||||
? false //
|
||||
: Arrays.stream(annotations) //
|
||||
.filter(annotation -> annotation.annotationType().equals(JsonIgnoreProperties.class)) //
|
||||
.map(annotation -> (String[]) AnnotationUtils.getAnnotationAttributes(annotation).get("value")) //
|
||||
.flatMap(Arrays::stream) //
|
||||
.anyMatch(propertyName -> propertyName.equalsIgnoreCase(field));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* Value objects to build ALPS metadata.
|
||||
*
|
||||
*
|
||||
* @see https://alps.io
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas.mediatype.alps;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -43,28 +43,28 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
class CollectionJson<T> {
|
||||
|
||||
private String version;
|
||||
private String href;
|
||||
private @Nullable String href;
|
||||
|
||||
private @JsonInclude(Include.NON_EMPTY) Links links;
|
||||
private @JsonInclude(Include.NON_EMPTY) List<CollectionJsonItem<T>> items;
|
||||
private @JsonInclude(Include.NON_EMPTY) List<CollectionJsonQuery> queries;
|
||||
private @JsonInclude(Include.NON_NULL) CollectionJsonTemplate template;
|
||||
private @JsonInclude(Include.NON_NULL) CollectionJsonError error;
|
||||
private @JsonInclude(Include.NON_NULL) @Nullable CollectionJsonTemplate template;
|
||||
private @JsonInclude(Include.NON_NULL) @Nullable CollectionJsonError error;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJson(@JsonProperty("version") String version, //
|
||||
@JsonProperty("href") @Nullable String href, //
|
||||
@JsonProperty("links") @Nullable Links links, //
|
||||
@JsonProperty("items") @Nullable List<CollectionJsonItem<T>> items, //
|
||||
@JsonProperty("queries") @Nullable List<CollectionJsonQuery> queries, //
|
||||
@JsonProperty("template") @Nullable CollectionJsonTemplate template, //
|
||||
@JsonProperty("error") @Nullable CollectionJsonError error) {
|
||||
@JsonProperty("href") @Nullable String href, //
|
||||
@JsonProperty("links") @Nullable Links links, //
|
||||
@JsonProperty("items") @Nullable List<CollectionJsonItem<T>> items, //
|
||||
@JsonProperty("queries") @Nullable List<CollectionJsonQuery> queries, //
|
||||
@JsonProperty("template") @Nullable CollectionJsonTemplate template, //
|
||||
@JsonProperty("error") @Nullable CollectionJsonError error) {
|
||||
|
||||
this.version = version;
|
||||
this.href = href;
|
||||
this.links = links == null ? Links.NONE : links;
|
||||
this.items = items == null ? Collections.emptyList() : items;
|
||||
this.queries = queries;
|
||||
this.queries = queries == null ? Collections.emptyList() : queries;
|
||||
this.template = template;
|
||||
this.error = error;
|
||||
}
|
||||
@@ -92,6 +92,8 @@ class CollectionJson<T> {
|
||||
|
||||
CollectionJson<T> withOwnSelfLink() {
|
||||
|
||||
String href = this.href;
|
||||
|
||||
if (href == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -35,16 +35,17 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
class CollectionJsonData {
|
||||
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
private String name;
|
||||
private @Nullable String name;
|
||||
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
private Object value;
|
||||
private @Nullable Object value;
|
||||
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
private String prompt;
|
||||
private @Nullable String prompt;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonData(@JsonProperty("name") @Nullable String name, @JsonProperty("value") @Nullable Object value,
|
||||
CollectionJsonData(@JsonProperty("name") @Nullable String name, //
|
||||
@JsonProperty("value") @Nullable Object value, //
|
||||
@JsonProperty("prompt") @Nullable String prompt) {
|
||||
|
||||
this.name = name;
|
||||
|
||||
@@ -49,10 +49,10 @@ import com.fasterxml.jackson.databind.JavaType;
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
class CollectionJsonItem<T> {
|
||||
|
||||
private String href;
|
||||
private @Nullable String href;
|
||||
private List<CollectionJsonData> data;
|
||||
private @JsonInclude(Include.NON_EMPTY) Links links;
|
||||
private @Getter(onMethod = @__({ @JsonIgnore }), value = AccessLevel.PRIVATE) T rawData;
|
||||
private @Nullable @Getter(onMethod = @__({ @JsonIgnore }), value = AccessLevel.PRIVATE) T rawData;
|
||||
|
||||
@JsonCreator
|
||||
CollectionJsonItem(@JsonProperty("href") @Nullable String href, //
|
||||
@@ -60,7 +60,7 @@ class CollectionJsonItem<T> {
|
||||
@JsonProperty("links") @Nullable Links links) {
|
||||
|
||||
this.href = href;
|
||||
this.data = data;
|
||||
this.data = data == null ? Collections.emptyList() : data;
|
||||
this.links = links == null ? Links.NONE : links;
|
||||
this.rawData = null;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ class CollectionJsonItem<T> {
|
||||
*/
|
||||
public List<CollectionJsonData> getData() {
|
||||
|
||||
if (this.data != null) {
|
||||
if (!this.data.isEmpty()) {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@@ -89,8 +89,10 @@ class CollectionJsonItem<T> {
|
||||
return Collections.singletonList(new CollectionJsonData().withValue(this.rawData));
|
||||
}
|
||||
|
||||
return PropertyUtils.findProperties(this.rawData).entrySet().stream()
|
||||
.map(entry -> new CollectionJsonData().withName(entry.getKey()).withValue(entry.getValue()))
|
||||
return PropertyUtils.findProperties(this.rawData).entrySet().stream() //
|
||||
.map(entry -> new CollectionJsonData() //
|
||||
.withName(entry.getKey()) //
|
||||
.withValue(entry.getValue())) //
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -103,7 +105,7 @@ class CollectionJsonItem<T> {
|
||||
@Nullable
|
||||
public Object toRawData(JavaType javaType) {
|
||||
|
||||
if (this.data == null) {
|
||||
if (this.data.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -125,6 +127,8 @@ class CollectionJsonItem<T> {
|
||||
|
||||
public CollectionJsonItem<T> withOwnSelfLink() {
|
||||
|
||||
String href = this.href;
|
||||
|
||||
if (href == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.client.LinkDiscoverer;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.config.HypermediaMappingInformation;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
|
||||
@@ -53,6 +54,7 @@ class CollectionJsonMediaTypeConfiguration implements HypermediaMappingInformati
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getJacksonModule()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Module getJacksonModule() {
|
||||
return new Jackson2CollectionJsonModule();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2019 the original author or authors.
|
||||
stand bisher hauptsächlich darin, dass * 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,11 +16,9 @@
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -105,8 +103,8 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(Links links, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
JavaType type = provider.getTypeFactory().constructCollectionType(List.class, Link.class);
|
||||
@@ -120,6 +118,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(SerializerProvider provider, Links value) {
|
||||
return value.isEmpty();
|
||||
}
|
||||
@@ -148,6 +147,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(Links value) {
|
||||
return false;
|
||||
}
|
||||
@@ -158,6 +158,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -180,7 +181,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(RepresentationModel<?> value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
|
||||
@@ -207,31 +213,54 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
provider.findValueSerializer(CollectionJsonDocument.class, property).serialize(doc, jgen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new CollectionJsonResourceSupportSerializer(property);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(RepresentationModel<?> value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -254,7 +283,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(EntityModel<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
String href = value.getRequiredLink(IanaLinkRelations.SELF).getHref();
|
||||
@@ -277,31 +311,54 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
.serialize(doc, jgen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new CollectionJsonResourceSerializer(property);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(EntityModel<?> value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -320,6 +377,7 @@ 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
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(CollectionModel<?> value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
|
||||
@@ -362,6 +420,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(SerializerProvider provider, CollectionModel<?> value) {
|
||||
return value.getContent().isEmpty();
|
||||
}
|
||||
@@ -371,6 +430,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(CollectionModel<?> value) {
|
||||
return value.getContent().size() == 1;
|
||||
}
|
||||
@@ -381,6 +441,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -403,7 +464,12 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(PagedModel<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
CollectionJson<?> collectionJson = new CollectionJson<>() //
|
||||
@@ -419,36 +485,64 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
provider.findValueSerializer(CollectionJsonDocument.class, property).serialize(doc, jgen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new CollectionJsonPagedResourcesSerializer(property);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(PagedModel<?> value) {
|
||||
return value.getContent().size() == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(PagedModel<?> value) {
|
||||
return value.getContent().size() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -487,6 +581,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public Links deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
|
||||
|
||||
JavaType type = ctx.getTypeFactory().constructCollectionLikeType(List.class, Link.class);
|
||||
@@ -537,6 +632,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
public RepresentationModel<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
TypeFactory typeFactory = ctxt.getTypeFactory();
|
||||
@@ -561,7 +657,6 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
(left, right) -> right);
|
||||
|
||||
CollectionJsonItem<?> firstItem = items.get(0).withOwnSelfLink();
|
||||
|
||||
RepresentationModel<?> resource = (RepresentationModel<?>) firstItem.toRawData(this.contentType);
|
||||
|
||||
if (resource != null) {
|
||||
@@ -571,9 +666,11 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
return resource;
|
||||
}
|
||||
|
||||
if (withOwnSelfLink.getTemplate() != null) {
|
||||
CollectionJsonTemplate template = withOwnSelfLink.getTemplate();
|
||||
|
||||
Map<String, Object> properties = withOwnSelfLink.getTemplate().getData().stream()
|
||||
if (template != null) {
|
||||
|
||||
Map<String, Object> properties = template.getData().stream()
|
||||
.collect(Collectors.toMap(CollectionJsonData::getName, CollectionJsonData::getValue));
|
||||
|
||||
RepresentationModel<?> resourceSupport = (RepresentationModel<?>) PropertyUtils
|
||||
@@ -582,20 +679,24 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
return resourceSupport.add(withOwnSelfLink.getLinks());
|
||||
|
||||
} else {
|
||||
|
||||
return new RepresentationModel<>().add(withOwnSelfLink.getLinks());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext context, @Nullable BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
if (property != null) {
|
||||
return new CollectionJsonResourceSupportDeserializer(property.getType().getContentType());
|
||||
} else {
|
||||
return new CollectionJsonResourceSupportDeserializer(ctxt.getContextualType());
|
||||
}
|
||||
JavaType type = property == null //
|
||||
? context.getContextualType() //
|
||||
: property.getType().getContentType();
|
||||
|
||||
return new CollectionJsonResourceSupportDeserializer(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,18 +717,31 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase#getContentType()
|
||||
*/
|
||||
@Override
|
||||
public JavaType getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase#getContentDeserializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonDeserializer<Object> getContentDeserializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public EntityModel<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
JavaType rootType = JacksonHelper.findRootType(this.contentType);
|
||||
@@ -635,18 +749,20 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
|
||||
CollectionJsonDocument<?> document = jp.getCodec().readValue(jp, wrappedType);
|
||||
|
||||
List<? extends CollectionJsonItem<?>> items = Optional.ofNullable(document.getCollection().getItems())
|
||||
.orElse(new ArrayList<>());
|
||||
Links links = document.getCollection().withOwnSelfLink().getLinks();
|
||||
CollectionJson<?> collection = document.getCollection();
|
||||
List<? extends CollectionJsonItem<?>> items = collection.getItems();
|
||||
Links links = collection.withOwnSelfLink().getLinks();
|
||||
CollectionJsonTemplate template = collection.getTemplate();
|
||||
|
||||
if (items.size() == 0 && document.getCollection().getTemplate() != null) {
|
||||
if (items.isEmpty() && template != null) {
|
||||
|
||||
Map<String, Object> properties = document.getCollection().getTemplate().getData().stream()
|
||||
Map<String, Object> properties = template.getData().stream()
|
||||
.collect(Collectors.toMap(CollectionJsonData::getName, CollectionJsonData::getValue));
|
||||
|
||||
Object obj = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
|
||||
|
||||
return new EntityModel<>(obj, links);
|
||||
|
||||
} else {
|
||||
|
||||
Links merged = items.stream() //
|
||||
@@ -667,6 +783,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
@@ -723,7 +840,8 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, @Nullable BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
JavaType contextualType = property == null //
|
||||
@@ -738,6 +856,7 @@ class Jackson2CollectionJsonModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public T deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
|
||||
JavaType rootType = JacksonHelper.findRootType(contentType);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/**
|
||||
* Value objects to build Collection+JSON representations.
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas.mediatype.collectionjson;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -20,7 +20,6 @@ import java.util.Collection;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* API to provide HAL curie information for links.
|
||||
@@ -32,6 +31,24 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public interface CurieProvider {
|
||||
|
||||
public static CurieProvider NONE = new CurieProvider() {
|
||||
|
||||
@Override
|
||||
public HalLinkRelation getNamespacedRelFrom(Link link) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HalLinkRelation getNamespacedRelFor(LinkRelation rel) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Object> getCurieInformation(Links links) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the rel to be rendered for the given {@link Link}. Will potentially prefix the rel but also might decide
|
||||
* not to, depending on the actual rel.
|
||||
@@ -49,7 +66,6 @@ public interface CurieProvider {
|
||||
* @return
|
||||
* @since 0.17
|
||||
*/
|
||||
@Nullable
|
||||
HalLinkRelation getNamespacedRelFor(LinkRelation rel);
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,7 +110,6 @@ public class DefaultCurieProvider implements CurieProvider {
|
||||
* @see org.springframework.hateoas.hal.CurieProvider#getNamespacedRelFrom(org.springframework.hateoas.Link)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public HalLinkRelation getNamespacedRelFrom(Link link) {
|
||||
return getNamespacedRelFor(link.getRel());
|
||||
}
|
||||
@@ -120,7 +119,6 @@ public class DefaultCurieProvider implements CurieProvider {
|
||||
* @see org.springframework.hateoas.hal.CurieProvider#getNamespacedRelFrom(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public HalLinkRelation getNamespacedRelFor(LinkRelation relation) {
|
||||
|
||||
HalLinkRelation result = HalLinkRelation.of(relation);
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class HalEmbeddedBuilder {
|
||||
|
||||
private static final HalLinkRelation DEFAULT_REL = HalLinkRelation.uncuried("content");
|
||||
private static final String INVALID_EMBEDDED_WRAPPER = "Embedded wrapper %s returned null for both the static rel and the rel target type! Make sure one of the two returns a non-null value!";
|
||||
|
||||
private final Map<HalLinkRelation, Object> embeddeds = new HashMap<>();
|
||||
@@ -51,12 +50,13 @@ class HalEmbeddedBuilder {
|
||||
* Creates a new {@link HalEmbeddedBuilder} using the given {@link LinkRelationProvider} and prefer collection rels
|
||||
* flag.
|
||||
*
|
||||
* @param provider can be {@literal null}.
|
||||
* @param provider must not be {@literal null}.
|
||||
* @param curieProvider must not be {@literal null}.
|
||||
* @param preferCollectionRels whether to prefer to ask the provider for collection rels.
|
||||
*/
|
||||
public HalEmbeddedBuilder(LinkRelationProvider provider, CurieProvider curieProvider, boolean preferCollectionRels) {
|
||||
|
||||
Assert.notNull(provider, "Relprovider must not be null!");
|
||||
Assert.notNull(provider, "LinkRelationProvider must not be null!");
|
||||
|
||||
this.provider = provider;
|
||||
this.curieProvider = curieProvider;
|
||||
@@ -69,7 +69,7 @@ class HalEmbeddedBuilder {
|
||||
*
|
||||
* @param source can be {@literal null}.
|
||||
*/
|
||||
public void add(Object source) {
|
||||
public void add(@Nullable Object source) {
|
||||
|
||||
EmbeddedWrapper wrapper = wrappers.wrap(source);
|
||||
|
||||
@@ -114,10 +114,6 @@ class HalEmbeddedBuilder {
|
||||
.map(HalLinkRelation::of) //
|
||||
.orElseGet(() -> {
|
||||
|
||||
if (provider == null) {
|
||||
return DEFAULT_REL;
|
||||
}
|
||||
|
||||
Class<?> type = wrapper.getRelTargetType();
|
||||
|
||||
if (type == null) {
|
||||
@@ -128,12 +124,9 @@ class HalEmbeddedBuilder {
|
||||
? provider.getCollectionResourceRelFor(type) //
|
||||
: provider.getItemResourceRelFor(type);
|
||||
|
||||
if (curieProvider != null) {
|
||||
rel = curieProvider.getNamespacedRelFor(rel);
|
||||
}
|
||||
|
||||
return rel == null ? DEFAULT_REL : HalLinkRelation.of(rel);
|
||||
|
||||
return curieProvider != CurieProvider.NONE //
|
||||
? curieProvider.getNamespacedRelFor(rel) //
|
||||
: HalLinkRelation.of(rel);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
|
||||
private static final String RELATION_MESSAGE_TEMPLATE = "_links.%s.title";
|
||||
|
||||
private final String curie;
|
||||
private final @Nullable String curie;
|
||||
private final @NonNull @Getter String localPart;
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
* @param relation must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static HalLinkRelation of(@Nullable LinkRelation relation) {
|
||||
public static HalLinkRelation of(LinkRelation relation) {
|
||||
|
||||
Assert.notNull(relation, "LinkRelation must not be null!");
|
||||
|
||||
@@ -166,6 +166,7 @@ public class HalLinkRelation implements LinkRelation, MessageSourceResolvable {
|
||||
* @see org.springframework.context.MessageSourceResolvable#getCodes()
|
||||
*/
|
||||
@Override
|
||||
@org.springframework.lang.NonNull
|
||||
public String[] getCodes() {
|
||||
|
||||
return Stream.of(value(), localPart) //
|
||||
|
||||
@@ -85,7 +85,8 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
mapper.registerModule(new Jackson2HalModule());
|
||||
mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider,
|
||||
curieProvider.getIfAvailable(), messageSourceAccessor, halConfiguration.getIfAvailable(HalConfiguration::new)));
|
||||
curieProvider.getIfAvailable(() -> CurieProvider.NONE), messageSourceAccessor,
|
||||
halConfiguration.getIfAvailable(HalConfiguration::new)));
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.mediatype.hal.HalConfiguration.RenderSingleLinks;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -109,19 +110,19 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
|
||||
private static final long serialVersionUID = -1844788111509966406L;
|
||||
|
||||
private final BeanProperty property;
|
||||
private final @Nullable BeanProperty property;
|
||||
private final CurieProvider curieProvider;
|
||||
private final EmbeddedMapper mapper;
|
||||
private final MessageSourceAccessor accessor;
|
||||
private final HalConfiguration halConfiguration;
|
||||
|
||||
public HalLinkListSerializer(@Nullable CurieProvider curieProvider, EmbeddedMapper mapper,
|
||||
@Nullable MessageSourceAccessor accessor, HalConfiguration halConfiguration) {
|
||||
public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper, MessageSourceAccessor accessor,
|
||||
HalConfiguration halConfiguration) {
|
||||
this(null, curieProvider, mapper, accessor, halConfiguration);
|
||||
}
|
||||
|
||||
public HalLinkListSerializer(@Nullable BeanProperty property, @Nullable CurieProvider curieProvider,
|
||||
@Nullable EmbeddedMapper mapper, @Nullable MessageSourceAccessor accessor, HalConfiguration halConfiguration) {
|
||||
public HalLinkListSerializer(@Nullable BeanProperty property, CurieProvider curieProvider, EmbeddedMapper mapper,
|
||||
MessageSourceAccessor accessor, HalConfiguration halConfiguration) {
|
||||
|
||||
super(TypeFactory.defaultInstance().constructType(Links.class));
|
||||
|
||||
@@ -132,25 +133,19 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
this.halConfiguration = halConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed to support Jackson
|
||||
*/
|
||||
HalLinkListSerializer() {
|
||||
this(null, null, null, null, new HalConfiguration().withRenderSingleLinks(RenderSingleLinks.AS_SINGLE));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(Links value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
// sort links according to their relation
|
||||
Map<LinkRelation, List<Object>> sortedLinks = new LinkedHashMap<>();
|
||||
List<Link> links = new ArrayList<>();
|
||||
|
||||
boolean prefixingRequired = curieProvider != null;
|
||||
boolean prefixingRequired = curieProvider != CurieProvider.NONE;
|
||||
boolean curiedLinkPresent = false;
|
||||
boolean skipCuries = !jgen.getOutputContext().getParent().inRoot();
|
||||
|
||||
@@ -238,6 +233,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new HalLinkListSerializer(property, curieProvider, mapper, accessor, halConfiguration);
|
||||
@@ -268,6 +264,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(SerializerProvider provider, Links value) {
|
||||
return value.isEmpty();
|
||||
}
|
||||
@@ -277,6 +274,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(Links value) {
|
||||
return value.toList().size() == 1;
|
||||
}
|
||||
@@ -287,6 +285,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -326,6 +325,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* org.codehaus.jackson.map.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
Map<HalLinkRelation, Object> embeddeds = embeddedMapper.map(value);
|
||||
@@ -342,36 +342,64 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
provider.findValueSerializer(Map.class, property).serialize(embeddeds, jgen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new HalResourcesSerializer(property, embeddedMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(SerializerProvider provider, Collection<?> value) {
|
||||
return value.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(Collection<?> value) {
|
||||
return value.size() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -389,7 +417,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
|
||||
private static final long serialVersionUID = 3700806118177419817L;
|
||||
|
||||
private final BeanProperty property;
|
||||
private final @Nullable BeanProperty property;
|
||||
private final Map<Class<?>, JsonSerializer<Object>> serializers;
|
||||
private final HalConfiguration halConfiguration;
|
||||
|
||||
@@ -398,7 +426,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
public OptionalListJackson2Serializer(BeanProperty property, HalConfiguration halConfiguration) {
|
||||
public OptionalListJackson2Serializer(@Nullable BeanProperty property, HalConfiguration halConfiguration) {
|
||||
|
||||
super(TypeFactory.defaultInstance().constructType(List.class));
|
||||
|
||||
@@ -412,6 +440,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
@@ -421,6 +450,7 @@ public class Jackson2HalModule 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
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
List<?> list = (List<?>) value;
|
||||
@@ -475,6 +505,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(Object arg0) {
|
||||
return false;
|
||||
}
|
||||
@@ -484,6 +515,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(SerializerProvider provider, Object value) {
|
||||
return false;
|
||||
}
|
||||
@@ -495,6 +527,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new OptionalListJackson2Serializer(property, halConfiguration);
|
||||
@@ -552,6 +585,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public List<Link> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
List<Link> result = new ArrayList<>();
|
||||
@@ -631,6 +665,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public List<Object> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
List<Object> result = new ArrayList<>();
|
||||
@@ -658,13 +693,18 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
JavaType vc = property.getType().getContentType();
|
||||
HalResourcesDeserializer des = new HalResourcesDeserializer(vc);
|
||||
return des;
|
||||
JavaType type = property.getType().getContentType();
|
||||
|
||||
return new HalResourcesDeserializer(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,7 +716,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
public static class HalHandlerInstantiator extends HandlerInstantiator {
|
||||
|
||||
private final Map<Class<?>, Object> serializers = new HashMap<>();
|
||||
private final AutowireCapableBeanFactory delegate;
|
||||
private final @Nullable AutowireCapableBeanFactory delegate;
|
||||
|
||||
public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider,
|
||||
MessageSourceAccessor messageSourceAccessor) {
|
||||
@@ -692,7 +732,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @param curieProvider can be {@literal null}.
|
||||
* @param messageSourceAccessor can be {@literal null}.
|
||||
*/
|
||||
public HalHandlerInstantiator(LinkRelationProvider provider, @Nullable CurieProvider curieProvider,
|
||||
public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider,
|
||||
MessageSourceAccessor messageSourceAccessor, HalConfiguration halConfiguration) {
|
||||
this(provider, curieProvider, messageSourceAccessor, true, halConfiguration);
|
||||
}
|
||||
@@ -708,17 +748,17 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @param accessor can be {@literal null}.
|
||||
* @param enforceEmbeddedCollections
|
||||
*/
|
||||
public HalHandlerInstantiator(@Nullable LinkRelationProvider provider, @Nullable CurieProvider curieProvider,
|
||||
@Nullable MessageSourceAccessor accessor, boolean enforceEmbeddedCollections,
|
||||
HalConfiguration halConfiguration) {
|
||||
public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider,
|
||||
MessageSourceAccessor accessor, boolean enforceEmbeddedCollections, HalConfiguration halConfiguration) {
|
||||
this(provider, curieProvider, accessor, enforceEmbeddedCollections, null, halConfiguration);
|
||||
}
|
||||
|
||||
private HalHandlerInstantiator(@Nullable LinkRelationProvider provider, @Nullable CurieProvider curieProvider,
|
||||
@Nullable MessageSourceAccessor accessor, boolean enforceEmbeddedCollections,
|
||||
private HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider,
|
||||
MessageSourceAccessor accessor, boolean enforceEmbeddedCollections,
|
||||
@Nullable AutowireCapableBeanFactory delegate, HalConfiguration halConfiguration) {
|
||||
|
||||
Assert.notNull(provider, "RelProvider must not be null!");
|
||||
Assert.notNull(curieProvider, "CurieProvider must not be null!");
|
||||
|
||||
EmbeddedMapper mapper = new EmbeddedMapper(provider, curieProvider, enforceEmbeddedCollections);
|
||||
|
||||
@@ -734,8 +774,9 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#deserializerInstance(com.fasterxml.jackson.databind.DeserializationConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated,
|
||||
Class<?> deserClass) {
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> deserializerInstance(@NonNull DeserializationConfig config, @NonNull Annotated annotated,
|
||||
@NonNull Class<?> deserClass) {
|
||||
return (JsonDeserializer<?>) findInstance(deserClass);
|
||||
}
|
||||
|
||||
@@ -744,8 +785,9 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#keyDeserializerInstance(com.fasterxml.jackson.databind.DeserializationConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, Annotated annotated,
|
||||
Class<?> keyDeserClass) {
|
||||
@SuppressWarnings("null")
|
||||
public KeyDeserializer keyDeserializerInstance(@NonNull DeserializationConfig config, @NonNull Annotated annotated,
|
||||
@NonNull Class<?> keyDeserClass) {
|
||||
return (KeyDeserializer) findInstance(keyDeserClass);
|
||||
}
|
||||
|
||||
@@ -754,7 +796,9 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#serializerInstance(com.fasterxml.jackson.databind.SerializationConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public JsonSerializer<?> serializerInstance(SerializationConfig config, Annotated annotated, Class<?> serClass) {
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> serializerInstance(@NonNull SerializationConfig config, @NonNull Annotated annotated,
|
||||
@NonNull Class<?> serClass) {
|
||||
return (JsonSerializer<?>) findInstance(serClass);
|
||||
}
|
||||
|
||||
@@ -763,8 +807,9 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#typeResolverBuilderInstance(com.fasterxml.jackson.databind.cfg.MapperConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, Annotated annotated,
|
||||
Class<?> builderClass) {
|
||||
@SuppressWarnings("null")
|
||||
public TypeResolverBuilder<?> typeResolverBuilderInstance(@NonNull MapperConfig<?> config,
|
||||
@NonNull Annotated annotated, @NonNull Class<?> builderClass) {
|
||||
return (TypeResolverBuilder<?>) findInstance(builderClass);
|
||||
}
|
||||
|
||||
@@ -773,7 +818,9 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#typeIdResolverInstance(com.fasterxml.jackson.databind.cfg.MapperConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, Annotated annotated, Class<?> resolverClass) {
|
||||
@SuppressWarnings("null")
|
||||
public TypeIdResolver typeIdResolverInstance(@NonNull MapperConfig<?> config, @NonNull Annotated annotated,
|
||||
@NonNull Class<?> resolverClass) {
|
||||
return (TypeIdResolver) findInstance(resolverClass);
|
||||
}
|
||||
|
||||
@@ -804,6 +851,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean isEmpty(SerializerProvider provider, Boolean value) {
|
||||
return value == null || Boolean.FALSE.equals(value);
|
||||
}
|
||||
@@ -813,6 +861,7 @@ public class Jackson2HalModule 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
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
jgen.writeBoolean(value);
|
||||
}
|
||||
@@ -822,6 +871,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdScalarSerializer#getSchema(com.fasterxml.jackson.databind.SerializerProvider, java.lang.reflect.Type)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
|
||||
return createSchemaNode("boolean", true);
|
||||
}
|
||||
@@ -831,8 +881,10 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdScalarSerializer#acceptJsonFormatVisitor(com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper, com.fasterxml.jackson.databind.JavaType)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
|
||||
throws JsonMappingException {
|
||||
|
||||
if (visitor != null) {
|
||||
visitor.expectBooleanFormat(typeHint);
|
||||
}
|
||||
@@ -855,11 +907,10 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
* whether to prefer collection relations.
|
||||
*
|
||||
* @param relProvider must not be {@literal null}.
|
||||
* @param curieProvider can be {@literal null}.
|
||||
* @param curieProvider must not be {@literal null}.
|
||||
* @param preferCollectionRels
|
||||
*/
|
||||
public EmbeddedMapper(LinkRelationProvider relProvider, @Nullable CurieProvider curieProvider,
|
||||
boolean preferCollectionRels) {
|
||||
public EmbeddedMapper(LinkRelationProvider relProvider, CurieProvider curieProvider, boolean preferCollectionRels) {
|
||||
|
||||
Assert.notNull(relProvider, "RelProvider must not be null!");
|
||||
|
||||
@@ -880,9 +931,7 @@ public class Jackson2HalModule extends SimpleModule {
|
||||
|
||||
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(relProvider, curieProvider, preferCollectionRels);
|
||||
|
||||
for (Object resource : source) {
|
||||
builder.add(resource);
|
||||
}
|
||||
source.forEach(builder::add);
|
||||
|
||||
return builder.asMap();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-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.
|
||||
@@ -36,7 +36,7 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
|
||||
/**
|
||||
* Collection of components needed to deserialize a HAL-FORMS document.
|
||||
*
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
class HalFormsDeserializers {
|
||||
@@ -58,7 +58,12 @@ class HalFormsDeserializers {
|
||||
this(TypeFactory.defaultInstance().constructCollectionLikeType(List.class, Object.class));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public List<Object> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
List<Object> result = new ArrayList<>();
|
||||
@@ -86,18 +91,31 @@ class HalFormsDeserializers {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase#getContentType()
|
||||
*/
|
||||
@Override
|
||||
public JavaType getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase#getContentDeserializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonDeserializer<Object> getContentDeserializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
@@ -142,6 +160,7 @@ class HalFormsDeserializers {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public List<MediaType> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return MediaType.parseMediaTypes(p.getText());
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Singular;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.Wither;
|
||||
|
||||
@@ -29,6 +28,7 @@ import java.util.Map;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.PagedModel;
|
||||
import org.springframework.hateoas.PagedModel.PageMetadata;
|
||||
import org.springframework.hateoas.mediatype.hal.HalLinkRelation;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer;
|
||||
import org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsModule.HalFormsLinksDeserializer;
|
||||
@@ -57,12 +57,14 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
@JsonPropertyOrder({ "resource", "resources", "embedded", "links", "templates", "metadata" })
|
||||
public class HalFormsDocument<T> {
|
||||
|
||||
@Nullable //
|
||||
@JsonUnwrapped //
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
@Wither(value = AccessLevel.PRIVATE, onMethod = @__({ @Nullable })) //
|
||||
private T resource;
|
||||
|
||||
@JsonInclude(Include.NON_EMPTY) @JsonIgnore //
|
||||
@Nullable //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
@JsonIgnore //
|
||||
@Wither(AccessLevel.PRIVATE) //
|
||||
private Collection<T> resources;
|
||||
|
||||
@@ -70,19 +72,19 @@ public class HalFormsDocument<T> {
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
private Map<HalLinkRelation, Object> embedded;
|
||||
|
||||
@Nullable //
|
||||
@JsonProperty("page") //
|
||||
@JsonInclude(Include.NON_NULL) //
|
||||
@Wither(onMethod = @__({ @Nullable })) //
|
||||
private PagedModel.PageMetadata pageMetadata;
|
||||
|
||||
@Singular //
|
||||
// @Singular //
|
||||
@JsonProperty("_links") //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
@JsonSerialize(using = HalLinkListSerializer.class) //
|
||||
@JsonDeserialize(using = HalFormsLinksDeserializer.class) //
|
||||
private Links links;
|
||||
|
||||
@Singular //
|
||||
// @Singular //
|
||||
@JsonProperty("_templates") //
|
||||
@JsonInclude(Include.NON_EMPTY) //
|
||||
private Map<String, HalFormsTemplate> templates;
|
||||
@@ -147,6 +149,14 @@ public class HalFormsDocument<T> {
|
||||
return this.templates.get(key);
|
||||
}
|
||||
|
||||
public HalFormsDocument<T> withPageMetadata(@Nullable PageMetadata metadata) {
|
||||
return new HalFormsDocument<T>(resource, resources, embedded, metadata, links, templates);
|
||||
}
|
||||
|
||||
private HalFormsDocument<T> withResource(@Nullable T resource) {
|
||||
return new HalFormsDocument<T>(resource, resources, embedded, pageMetadata, links, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given {@link Link} to the current document.
|
||||
*
|
||||
|
||||
@@ -23,10 +23,10 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||
import org.springframework.hateoas.client.LinkDiscoverer;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.config.HypermediaMappingInformation;
|
||||
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||
import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@@ -71,9 +71,9 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
mapper.registerModule(new Jackson2HalFormsModule());
|
||||
mapper.setHandlerInstantiator(
|
||||
new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider, curieProvider.getIfAvailable(),
|
||||
messageSourceAccessor, true, halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new)));
|
||||
mapper.setHandlerInstantiator(new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider,
|
||||
curieProvider.getIfAvailable(() -> CurieProvider.NONE), messageSourceAccessor, true,
|
||||
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new)));
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@@ -54,8 +54,7 @@ class HalFormsSerializers {
|
||||
/**
|
||||
* Serializer for {@link CollectionModel}.
|
||||
*/
|
||||
static class HalFormsResourceSerializer extends ContainerSerializer<EntityModel<?>>
|
||||
implements ContextualSerializer {
|
||||
static class HalFormsResourceSerializer extends ContainerSerializer<EntityModel<?>> implements ContextualSerializer {
|
||||
|
||||
private static final long serialVersionUID = -7912243216469101379L;
|
||||
|
||||
@@ -71,9 +70,13 @@ class HalFormsSerializers {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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(EntityModel<?> value, JsonGenerator gen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(EntityModel<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
|
||||
HalFormsDocument<?> doc = HalFormsDocument.forResource(value.getContent()) //
|
||||
.withLinks(value.getLinks()) //
|
||||
@@ -82,30 +85,53 @@ class HalFormsSerializers {
|
||||
provider.findValueSerializer(HalFormsDocument.class, property).serialize(doc, gen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(EntityModel<?> resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer typeSerializer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new HalFormsResourceSerializer(property);
|
||||
@@ -135,9 +161,13 @@ class HalFormsSerializers {
|
||||
this(null, embeddedMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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(CollectionModel<?> value, JsonGenerator gen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(CollectionModel<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
|
||||
Map<HalLinkRelation, Object> embeddeds = embeddedMapper.map(value);
|
||||
|
||||
@@ -162,30 +192,53 @@ class HalFormsSerializers {
|
||||
provider.findValueSerializer(HalFormsDocument.class, property).serialize(doc, gen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(CollectionModel<?> resources) {
|
||||
return resources.getContent().size() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer typeSerializer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new HalFormsResourcesSerializer(property, embeddedMapper);
|
||||
|
||||
@@ -65,6 +65,7 @@ public class HalFormsTemplate {
|
||||
private List<HalFormsProperty> properties;
|
||||
private List<MediaType> contentTypes;
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private HalFormsTemplate() {
|
||||
this(null, null, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
@@ -75,7 +76,7 @@ public class HalFormsTemplate {
|
||||
|
||||
/**
|
||||
* Returns a new {@link HalFormsTemplate} with the given {@link HalFormsProperty} added.
|
||||
*
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -91,7 +92,7 @@ public class HalFormsTemplate {
|
||||
|
||||
/**
|
||||
* Returns a new {@link HalFormsTemplate} with the given {@link MediaType} added as content type.
|
||||
*
|
||||
*
|
||||
* @param mediaType must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -101,6 +101,7 @@ class Jackson2HalFormsModule extends SimpleModule {
|
||||
|
||||
abstract class PagedModelMixin<T> extends PagedModel<T> {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@JsonProperty("page")
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
@@ -138,6 +139,7 @@ class Jackson2HalFormsModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public Links deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
return Links.of(delegate.deserialize(p, ctxt));
|
||||
}
|
||||
@@ -150,7 +152,7 @@ class Jackson2HalFormsModule extends SimpleModule {
|
||||
|
||||
private final Map<Class<?>, Object> serializers = new HashMap<>();
|
||||
|
||||
public HalFormsHandlerInstantiator(LinkRelationProvider resolver, @Nullable CurieProvider curieProvider,
|
||||
public HalFormsHandlerInstantiator(LinkRelationProvider resolver, CurieProvider curieProvider,
|
||||
MessageSourceAccessor accessor, boolean enforceEmbeddedCollections,
|
||||
HalFormsConfiguration halFormsConfiguration) {
|
||||
|
||||
@@ -166,6 +168,7 @@ class Jackson2HalFormsModule extends SimpleModule {
|
||||
public HalFormsHandlerInstantiator(LinkRelationProvider relProvider, CurieProvider curieProvider,
|
||||
MessageSourceAccessor messageSource, boolean enforceEmbeddedCollections,
|
||||
AutowireCapableBeanFactory beanFactory) {
|
||||
|
||||
this(relProvider, curieProvider, messageSource, enforceEmbeddedCollections,
|
||||
beanFactory.getBean(HalFormsConfiguration.class));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* HAL-specific extensions, SPIs and Jackson customizations.
|
||||
*
|
||||
*
|
||||
* @see http://stateless.co/hal_specification.html
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas.mediatype.hal;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -1,7 +1,5 @@
|
||||
/**
|
||||
* Spring container configuration support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas.mediatype;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -23,12 +23,14 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
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.LinkRelation;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.PagedModel;
|
||||
import org.springframework.hateoas.PagedModel.PageMetadata;
|
||||
@@ -139,7 +141,12 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(RepresentationModel<?> value, JsonGenerator gen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
|
||||
@@ -153,30 +160,53 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
.serialize(doc, gen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(RepresentationModel<?> value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new UberRepresentationModelSerializer(property);
|
||||
@@ -202,7 +232,12 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(EntityModel<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
|
||||
UberDocument doc = new UberDocument().withUber(new Uber() //
|
||||
@@ -214,30 +249,53 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
.serialize(doc, gen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(EntityModel<?> value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new UberEntityModelSerializer(property);
|
||||
@@ -269,6 +327,7 @@ 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
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(CollectionModel<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
|
||||
UberDocument doc = new UberDocument() //
|
||||
@@ -281,30 +340,53 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
.serialize(doc, gen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonSerializer<?> getContentSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(CollectionModel<?> value) {
|
||||
return value.getContent().size() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
return new UberCollectionModelSerializer(property);
|
||||
@@ -335,6 +417,7 @@ 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
|
||||
@SuppressWarnings("null")
|
||||
public void serialize(PagedModel<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
|
||||
UberDocument doc = new UberDocument() //
|
||||
@@ -372,6 +455,7 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public boolean hasSingleElement(PagedModel<?> value) {
|
||||
return value.getContent().size() == 1;
|
||||
}
|
||||
@@ -382,6 +466,7 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
|
||||
return null;
|
||||
}
|
||||
@@ -391,6 +476,7 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
|
||||
return new UberPagedModelSerializer(property);
|
||||
}
|
||||
@@ -420,6 +506,7 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public RepresentationModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
UberDocument doc = p.getCodec().readValue(p, UberDocument.class);
|
||||
@@ -467,15 +554,13 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
if (property != null) {
|
||||
JavaType vc = property.getType().getContentType();
|
||||
return new UberRepresentationModelDeserializer(vc);
|
||||
} else {
|
||||
return new UberRepresentationModelDeserializer(ctxt.getContextualType());
|
||||
}
|
||||
JavaType type = property == null ? ctxt.getContextualType() : property.getType().getContentType();
|
||||
|
||||
return new UberRepresentationModelDeserializer(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,7 +593,12 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public EntityModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
UberDocument doc = p.getCodec().readValue(p, UberDocument.class);
|
||||
@@ -527,20 +617,24 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
|
||||
// Primitive type
|
||||
List<UberData> data = uberData.getData();
|
||||
|
||||
if (data == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
if (isPrimitiveType(data)) {
|
||||
Object scalarValue = data.get(0).getValue();
|
||||
|
||||
UberData firstItem = data.get(0);
|
||||
|
||||
Object scalarValue = firstItem.getValue();
|
||||
return new EntityModel<>(scalarValue, links);
|
||||
}
|
||||
|
||||
Map<String, Object> properties;
|
||||
if (data == null) {
|
||||
properties = new HashMap<>();
|
||||
} else {
|
||||
properties = data.stream().collect(Collectors.toMap(UberData::getName, UberData::getValue));
|
||||
}
|
||||
Map<String, Object> properties = data == null //
|
||||
? new HashMap<>() //
|
||||
: data.stream().collect(Collectors.toMap(UberData::getName, UberData::getValue));
|
||||
|
||||
JavaType rootType = JacksonHelper.findRootType(this.contentType);
|
||||
|
||||
Object value = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
|
||||
|
||||
return new EntityModel<>(value, links);
|
||||
@@ -560,15 +654,13 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
if (property != null) {
|
||||
JavaType vc = property.getType().getContentType();
|
||||
return new UberEntityModelDeserializer(vc);
|
||||
} else {
|
||||
return new UberEntityModelDeserializer(ctxt.getContextualType());
|
||||
}
|
||||
JavaType type = property == null ? ctxt.getContextualType() : property.getType().getContentType();
|
||||
|
||||
return new UberEntityModelDeserializer(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -606,10 +698,10 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public CollectionModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
JavaType rootType = JacksonHelper.findRootType(this.contentType);
|
||||
|
||||
UberDocument doc = p.getCodec().readValue(p, UberDocument.class);
|
||||
|
||||
return extractResources(doc, rootType, this.contentType);
|
||||
@@ -623,16 +715,18 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
if (property != null) {
|
||||
JavaType vc = property.getType().getContentType();
|
||||
return new UberCollectionModelDeserializer(vc);
|
||||
} else {
|
||||
return new UberCollectionModelDeserializer(ctxt.getContextualType());
|
||||
}
|
||||
JavaType type = property == null ? ctxt.getContextualType() : property.getType().getContentType();
|
||||
|
||||
return new UberCollectionModelDeserializer(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -670,6 +764,7 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public PagedModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
|
||||
JavaType rootType = JacksonHelper.findRootType(this.contentType);
|
||||
@@ -690,16 +785,18 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
|
||||
throws JsonMappingException {
|
||||
|
||||
if (property != null) {
|
||||
JavaType vc = property.getType().getContentType();
|
||||
return new UberPagedModelDeserializer(vc);
|
||||
} else {
|
||||
return new UberPagedModelDeserializer(ctxt.getContextualType());
|
||||
}
|
||||
JavaType type = property == null ? ctxt.getContextualType() : property.getType().getContentType();
|
||||
|
||||
return new UberPagedModelDeserializer(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -726,55 +823,65 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
|
||||
for (UberData uberData : doc.getUber().getData()) {
|
||||
|
||||
if (uberData.getName() != null && uberData.getName().equals("page")) {
|
||||
String name = uberData.getName();
|
||||
|
||||
if (name != null && name.equals("page")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uberData.getLinks().isEmpty()) {
|
||||
if (!uberData.getLinks().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Link> resourceLinks = new ArrayList<>();
|
||||
EntityModel<?> resource = null;
|
||||
List<Link> resourceLinks = new ArrayList<>();
|
||||
EntityModel<?> resource = null;
|
||||
|
||||
List<UberData> data = uberData.getData();
|
||||
if (data == null) {
|
||||
throw new RuntimeException("No content!");
|
||||
}
|
||||
List<UberData> data = uberData.getData();
|
||||
|
||||
for (UberData item : data) {
|
||||
if (data == null) {
|
||||
throw new RuntimeException("No content!");
|
||||
}
|
||||
|
||||
for (UberData item : data) {
|
||||
|
||||
List<LinkRelation> rel = item.getRel();
|
||||
|
||||
if (rel != null) {
|
||||
item.getLinks().forEach(resourceLinks::add);
|
||||
} else {
|
||||
|
||||
// Primitive type
|
||||
List<UberData> itemData = item.getData();
|
||||
|
||||
if (isPrimitiveType(itemData)) {
|
||||
|
||||
if (itemData == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
UberData firstItem = itemData.get(0);
|
||||
Object scalarValue = firstItem.getValue();
|
||||
resource = new EntityModel<>(scalarValue, uberData.getLinks());
|
||||
|
||||
if (item.getRel() != null) {
|
||||
item.getRel().forEach(rel -> resourceLinks.add(new Link(item.getUrl(), rel)));
|
||||
} else {
|
||||
|
||||
// Primitive type
|
||||
List<UberData> itemData = item.getData();
|
||||
if (isPrimitiveType(itemData)) {
|
||||
|
||||
Object scalarValue = itemData.get(0).getValue();
|
||||
resource = new EntityModel<>(scalarValue, uberData.getLinks());
|
||||
} else {
|
||||
|
||||
Map<String, Object> properties;
|
||||
if (itemData == null) {
|
||||
properties = new HashMap<>();
|
||||
} else {
|
||||
properties = itemData.stream() //
|
||||
Map<String, Object> properties = itemData == null //
|
||||
? new HashMap<>() //
|
||||
: itemData.stream() //
|
||||
.collect(Collectors.toMap(UberData::getName, UberData::getValue));
|
||||
}
|
||||
|
||||
Object obj = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
|
||||
resource = new EntityModel<>(obj, uberData.getLinks());
|
||||
}
|
||||
Object obj = PropertyUtils.createObjectFromProperties(rootType.getRawClass(), properties);
|
||||
resource = new EntityModel<>(obj, uberData.getLinks());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resource != null) {
|
||||
if (resource != null) {
|
||||
|
||||
resource.add(resourceLinks);
|
||||
content.add(resource);
|
||||
} else {
|
||||
throw new RuntimeException("No content!");
|
||||
}
|
||||
resource.add(resourceLinks);
|
||||
content.add(resource);
|
||||
} else {
|
||||
throw new RuntimeException("No content!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,12 +910,12 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
private static PageMetadata extractPagingMetadata(UberDocument doc) {
|
||||
|
||||
return doc.getUber().getData().stream() //
|
||||
.filter(uberData -> uberData.getName() != null && uberData.getName().equals("page")) //
|
||||
.filter(uberData -> Optional.ofNullable(uberData.getName()).map("page"::equals).orElse(false)) //
|
||||
.findFirst().map(Jackson2UberModule::convertUberDataToPageMetaData) //
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("null")
|
||||
private static PageMetadata convertUberDataToPageMetaData(UberData uberData) {
|
||||
|
||||
int size = 0;
|
||||
@@ -817,26 +924,30 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
int totalPages = 0;
|
||||
|
||||
List<UberData> content = uberData.getData();
|
||||
|
||||
if (content != null) {
|
||||
|
||||
for (UberData data : content) {
|
||||
|
||||
String name = data.getName();
|
||||
Object value = data.getValue();
|
||||
|
||||
switch (name) {
|
||||
|
||||
case "size":
|
||||
size = (int) data.getValue();
|
||||
size = (int) value;
|
||||
break;
|
||||
|
||||
case "number":
|
||||
number = (int) data.getValue();
|
||||
number = (int) value;
|
||||
break;
|
||||
|
||||
case "totalElements":
|
||||
totalElements = (int) data.getValue();
|
||||
totalElements = (int) value;
|
||||
break;
|
||||
|
||||
case "totalPages":
|
||||
totalPages = (int) data.getValue();
|
||||
totalPages = (int) value;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -863,6 +974,7 @@ public class Jackson2UberModule extends SimpleModule {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public UberAction deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return UberAction.valueOf(p.getText().toUpperCase());
|
||||
}
|
||||
|
||||
@@ -63,18 +63,16 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class UberData {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String label;
|
||||
private List<LinkRelation> rel;
|
||||
private String url;
|
||||
private UberAction action;
|
||||
private @Nullable String id, name, label;
|
||||
private @Nullable List<LinkRelation> rel;
|
||||
private @Nullable String url;
|
||||
private @Nullable UberAction action;
|
||||
private boolean transclude;
|
||||
private String model;
|
||||
private List<String> sending;
|
||||
private List<String> accepting;
|
||||
private Object value;
|
||||
private List<UberData> data;
|
||||
private @Nullable String model;
|
||||
private @Nullable List<String> sending;
|
||||
private @Nullable List<String> accepting;
|
||||
private @Nullable Object value;
|
||||
private @Nullable List<UberData> data;
|
||||
|
||||
@JsonCreator
|
||||
UberData(@JsonProperty("id") @Nullable String id, @JsonProperty("name") @Nullable String name,
|
||||
@@ -136,13 +134,15 @@ class UberData {
|
||||
@JsonIgnore
|
||||
public List<Link> getLinks() {
|
||||
|
||||
if (this.url == null) {
|
||||
String url = this.url;
|
||||
|
||||
if (url == null) {
|
||||
return Links.NONE.toList();
|
||||
}
|
||||
|
||||
return Optional.ofNullable(this.rel) //
|
||||
.map(rels -> rels.stream() //
|
||||
.map(rel -> new Link(this.url, rel)) //
|
||||
.map(rel -> new Link(url, rel)) //
|
||||
.collect(Collectors.toList())) //
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
@@ -204,6 +204,7 @@ class UberData {
|
||||
return data;
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
static List<UberData> extractLinksAndContent(PagedModel<?> resources) {
|
||||
|
||||
List<UberData> collectionOfResources = extractLinksAndContent((CollectionModel<?>) resources);
|
||||
@@ -266,7 +267,7 @@ class UberData {
|
||||
private static Optional<UberData> extractContent(@Nullable Object content) {
|
||||
|
||||
return Optional.ofNullable(content) //
|
||||
.filter(it -> !RESOURCE_TYPES.contains(content.getClass())) //
|
||||
.filter(it -> !RESOURCE_TYPES.contains(it.getClass())) //
|
||||
.map(it -> new UberData() //
|
||||
.withName(StringUtils.uncapitalize(it.getClass().getSimpleName())) //
|
||||
.withData(extractProperties(it)));
|
||||
|
||||
@@ -19,10 +19,11 @@ import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.client.LinkDiscoverer;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.config.HypermediaMappingInformation;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
|
||||
@@ -53,6 +54,7 @@ class UberMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getJacksonModule()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Module getJacksonModule() {
|
||||
return new Jackson2UberModule();
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -138,7 +139,7 @@ public class VndErrors implements Iterable<VndErrors.VndError> {
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
@@ -235,7 +236,7 @@ public class VndErrors implements Iterable<VndErrors.VndError> {
|
||||
* @see org.springframework.hateoas.ResourceSupport#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Value objects to ease creating {@link org.springframework.hateoas.Link}s and link driven representations for REST webservices.
|
||||
* Value objects to ease creating {@link org.springframework.hateoas.Link}s and link driven representations for REST
|
||||
* webservices.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
* @author Jens Schauder
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -20,21 +20,23 @@ import java.net.URI;
|
||||
import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Builder to ease building {@link Link} instances.
|
||||
*
|
||||
* @author Ricardo Gladwell
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public interface LinkBuilder {
|
||||
|
||||
/**
|
||||
* Adds the given object's {@link String} representation as sub-resource to the current URI.
|
||||
*
|
||||
* @param object
|
||||
* @param object can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
LinkBuilder slash(Object object);
|
||||
LinkBuilder slash(@Nullable Object object);
|
||||
|
||||
/**
|
||||
* Creates a URI of the link built by the current builder instance.
|
||||
|
||||
@@ -15,15 +15,26 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider.LookupContext;
|
||||
import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
|
||||
/**
|
||||
* API to provide {@link LinkRelation}s for collections and items of the given type.
|
||||
* API to provide {@link LinkRelation}s for collections and items of the given type. Implementations can be selected
|
||||
* based on the {@link LookupContext}, for item resource relations, collection resource relations or both.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see #supports(LookupContext)
|
||||
*/
|
||||
public interface LinkRelationProvider extends Plugin<Class<?>> {
|
||||
public interface LinkRelationProvider extends Plugin<LookupContext> {
|
||||
|
||||
/**
|
||||
* Returns the relation type to be used to point to an item resource of the given type.
|
||||
@@ -40,4 +51,106 @@ public interface LinkRelationProvider extends Plugin<Class<?>> {
|
||||
* @return
|
||||
*/
|
||||
LinkRelation getCollectionResourceRelFor(Class<?> type);
|
||||
|
||||
/**
|
||||
* Callback method to manually select {@link LinkRelationProvider} implementations based on a given
|
||||
* {@link LookupContext}. User code shouldn't need to call this method explicitly but rather use
|
||||
* {@link DelegatingLinkRelationProvider}, equip that with a set of {@link LinkRelationProvider} implementations as
|
||||
* that will perform the selection of the matching one on invocations of {@link #getItemResourceRelFor(Class)} and
|
||||
* {@link #getCollectionResourceRelFor(Class)} transparently.
|
||||
*
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
boolean supports(LookupContext delimiter);
|
||||
|
||||
/**
|
||||
* {@link LinkRelationProvider} selection context for item resource relation lookups
|
||||
* ({@link #forItemResourceRelLookup(Class)}, collection resource relation lookups
|
||||
* {@link #forCollectionResourceRelLookup(Class)} or both {@link #forType(Class)}.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode
|
||||
static class LookupContext {
|
||||
|
||||
private enum ResourceType {
|
||||
ITEM, COLLECTION;
|
||||
}
|
||||
|
||||
private final @NonNull @Getter Class<?> type;
|
||||
private final @Nullable ResourceType resourceType;
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} for the type in general, i.e. both item and collection relation lookups.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LookupContext forType(Class<?> type) {
|
||||
return new LookupContext(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} to lookup the item resource relation for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LookupContext forItemResourceRelLookup(Class<?> type) {
|
||||
return new LookupContext(type, ResourceType.ITEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} to lookup the collection resource relation for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LookupContext forCollectionResourceRelLookup(Class<?> type) {
|
||||
return new LookupContext(type, ResourceType.COLLECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current context includes the item relation lookup.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isItemRelationLookup() {
|
||||
return resourceType == null || ResourceType.ITEM.equals(resourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current context includes the collection relation lookup.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCollectionRelationLookup() {
|
||||
return resourceType == null || ResourceType.COLLECTION.equals(resourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the lookup is executed for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean handlesType(Class<?> type) {
|
||||
return this.type.equals(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
ResourceType resourceType = this.resourceType;
|
||||
|
||||
return String.format("LookupContext for %s for %s resource relations.", type.getName(),
|
||||
resourceType == null ? "ITEM & COLLECTION" : resourceType.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,12 +19,15 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link LinkRelationProvider} that evaluates the {@link Relation} annotation on entity types.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Alexander Baetz
|
||||
* @author Greg Turnquist
|
||||
@@ -38,13 +41,12 @@ public class AnnotationLinkRelationProvider implements LinkRelationProvider, Ord
|
||||
* @see org.springframework.hateoas.server.LinkRelationProvider#getCollectionResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public LinkRelation getCollectionResourceRelFor(Class<?> type) {
|
||||
|
||||
Relation annotation = lookupAnnotation(type);
|
||||
|
||||
if (annotation == null || Relation.NO_RELATION.equals(annotation.collectionRelation())) {
|
||||
return null;
|
||||
throw new IllegalArgumentException(String.format("No collection relation found for type %s!", type.getName()));
|
||||
}
|
||||
|
||||
return LinkRelation.of(annotation.collectionRelation());
|
||||
@@ -55,13 +57,14 @@ public class AnnotationLinkRelationProvider implements LinkRelationProvider, Ord
|
||||
* @see org.springframework.hateoas.server.LinkRelationProvider#getItemResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public LinkRelation getItemResourceRelFor(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
|
||||
Relation annotation = lookupAnnotation(type);
|
||||
|
||||
if (annotation == null || Relation.NO_RELATION.equals(annotation.value())) {
|
||||
return null;
|
||||
throw new IllegalStateException(String.format("Type %s is not supported!", type.getName()));
|
||||
}
|
||||
|
||||
return LinkRelation.of(annotation.value());
|
||||
@@ -81,12 +84,27 @@ public class AnnotationLinkRelationProvider implements LinkRelationProvider, Ord
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return lookupAnnotation(delimiter) != null;
|
||||
public boolean supports(LookupContext context) {
|
||||
|
||||
Relation relation = lookupAnnotation(context.getType());
|
||||
|
||||
if (relation == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.isItemRelationLookup()) {
|
||||
return !relation.value().equals(Relation.NO_RELATION);
|
||||
}
|
||||
|
||||
if (context.isCollectionRelationLookup()) {
|
||||
return !relation.collectionRelation().equals(Relation.NO_RELATION);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Relation lookupAnnotation(Class<?> type) {
|
||||
return annotationCache.computeIfAbsent(type, key -> AnnotationUtils.getAnnotation(key, Relation.class));
|
||||
return annotationCache.computeIfAbsent(type, key -> AnnotatedElementUtils.getMergedAnnotation(key, Relation.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.Class)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMapping(Class<?> type) {
|
||||
|
||||
@@ -56,6 +57,7 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.reflect.Method)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMapping(Method method) {
|
||||
|
||||
@@ -68,6 +70,7 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.Class, java.lang.reflect.Method)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMapping(Class<?> type, Method method) {
|
||||
|
||||
|
||||
@@ -28,16 +28,17 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.hateoas.server.ExposesResourceFor;
|
||||
import org.springframework.hateoas.server.LinkBuilder;
|
||||
import org.springframework.hateoas.server.LinkBuilderFactory;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} implementation to create {@link ControllerEntityLinks} instances looking up controller classes
|
||||
* from an {@link ApplicationContext}. The controller types are identified by the annotation type configured.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<ControllerEntityLinks> implements
|
||||
ApplicationContextAware {
|
||||
public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<ControllerEntityLinks>
|
||||
implements ApplicationContextAware {
|
||||
|
||||
private Class<? extends Annotation> annotation;
|
||||
private LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory;
|
||||
@@ -45,7 +46,7 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
|
||||
/**
|
||||
* Configures the annotation type to inspect the {@link ApplicationContext} for beans that carry the given annotation.
|
||||
*
|
||||
*
|
||||
* @param annotation must not be {@literal null}.
|
||||
*/
|
||||
public void setAnnotation(Class<? extends Annotation> annotation) {
|
||||
@@ -55,14 +56,14 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
|
||||
/**
|
||||
* Configures the {@link LinkBuilderFactory} to be used to create {@link LinkBuilder} instances.
|
||||
*
|
||||
*
|
||||
* @param linkBuilderFactory the linkBuilderFactory to set
|
||||
*/
|
||||
public void setLinkBuilderFactory(LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory) {
|
||||
this.linkBuilderFactory = linkBuilderFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@@ -71,16 +72,17 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ControllerEntityLinks.class;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@@ -98,7 +100,7 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
return new ControllerEntityLinks(controllerTypes, linkBuilderFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#afterPropertiesSet()
|
||||
*/
|
||||
|
||||
@@ -62,7 +62,7 @@ public class DefaultLinkRelationProvider implements LinkRelationProvider, Ordere
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
public boolean supports(LookupContext delimiter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,16 @@ import org.springframework.plugin.core.PluginRegistry;
|
||||
@RequiredArgsConstructor
|
||||
public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
|
||||
private final @NonNull PluginRegistry<LinkRelationProvider, Class<?>> providers;
|
||||
private final @NonNull PluginRegistry<LinkRelationProvider, LookupContext> providers;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultLinkRelationProvider} for the given {@link LinkRelationProvider}s.
|
||||
*
|
||||
* @param providers must not be {@literal null}.
|
||||
*/
|
||||
public DelegatingLinkRelationProvider(LinkRelationProvider... providers) {
|
||||
this(PluginRegistry.of(providers));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -36,7 +45,10 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getItemResourceRelFor(Class<?> type) {
|
||||
return providers.getRequiredPluginFor(type).getItemResourceRelFor(type);
|
||||
|
||||
LookupContext context = LookupContext.forItemResourceRelLookup(type);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getItemResourceRelFor(type);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -45,7 +57,10 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getCollectionResourceRelFor(java.lang.Class<?> type) {
|
||||
return providers.getRequiredPluginFor(type).getCollectionResourceRelFor(type);
|
||||
|
||||
LookupContext context = LookupContext.forCollectionResourceRelLookup(type);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getCollectionResourceRelFor(type);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -53,7 +68,7 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(java.lang.Class<?> delimiter) {
|
||||
public boolean supports(LookupContext delimiter) {
|
||||
return providers.hasPluginFor(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,12 @@ import lombok.Value;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.objenesis.ObjenesisStd;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -41,9 +36,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
public class DummyInvocationUtils {
|
||||
|
||||
private static final ObjenesisStd OBJENESIS = new ObjenesisStd();
|
||||
private static final Map<Class<?>, Class<?>> CLASS_CACHE = new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK);
|
||||
|
||||
/**
|
||||
* Method interceptor that records the last method invocation and creates a proxy for the return value that exposes
|
||||
* the method invocation.
|
||||
@@ -86,6 +78,7 @@ public class DummyInvocationUtils {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
public Object invoke(org.aopalliance.intercept.MethodInvocation invocation) {
|
||||
|
||||
Method method = invocation.getMethod();
|
||||
@@ -164,30 +157,6 @@ public class DummyInvocationUtils {
|
||||
return (T) factory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the already created proxy class for the given source type or creates a new one.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param classLoader must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getOrCreateEnhancedClass(Class<?> type, ClassLoader classLoader) {
|
||||
|
||||
Assert.notNull(type, "Source type must not be null!");
|
||||
Assert.notNull(classLoader, "ClassLoader must not be null!");
|
||||
|
||||
return CLASS_CACHE.computeIfAbsent(type, key -> {
|
||||
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(key);
|
||||
enhancer.setInterfaces(new Class<?>[] { LastInvocationAware.class });
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
enhancer.setClassLoader(classLoader);
|
||||
|
||||
return enhancer.createClass();
|
||||
});
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class SimpleMethodInvocation implements MethodInvocation {
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -186,6 +187,7 @@ public class EmbeddedWrappers {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.hal.EmbeddedWrapper#getValue()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return value;
|
||||
@@ -195,6 +197,7 @@ public class EmbeddedWrappers {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EmbeddedWrappers.AbstractElementWrapper#peek()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
protected Object peek() {
|
||||
return getValue();
|
||||
@@ -283,6 +286,7 @@ public class EmbeddedWrappers {
|
||||
public EmptyCollectionEmbeddedWrapper(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Element type must not be null!");
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -308,6 +312,7 @@ public class EmbeddedWrappers {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#getRelTargetType()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<?> getRelTargetType() {
|
||||
return type;
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.hateoas.server.core;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
@@ -40,7 +39,7 @@ class EncodingUtils {
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static String encodePath(@Nullable Object source) {
|
||||
public static String encodePath(Object source) {
|
||||
|
||||
Assert.notNull(source, "Path value must not be null!");
|
||||
|
||||
@@ -57,7 +56,7 @@ class EncodingUtils {
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static String encodeParameter(@Nullable Object source) {
|
||||
public static String encodeParameter(Object source) {
|
||||
|
||||
Assert.notNull(source, "Request parameter value must not be null!");
|
||||
|
||||
|
||||
@@ -44,8 +44,11 @@ public class HeaderLinksResponseEntity<T extends RepresentationModel<?>> extends
|
||||
private HeaderLinksResponseEntity(ResponseEntity<T> entity) {
|
||||
|
||||
super(entity.getBody(), getHeadersWithLinks(entity), entity.getStatusCode());
|
||||
if (entity.getBody() != null) {
|
||||
entity.getBody().removeLinks();
|
||||
|
||||
T body = entity.getBody();
|
||||
|
||||
if (body != null) {
|
||||
body.removeLinks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +103,9 @@ public class HeaderLinksResponseEntity<T extends RepresentationModel<?>> extends
|
||||
*/
|
||||
private static <T extends RepresentationModel<?>> HttpHeaders getHeadersWithLinks(ResponseEntity<T> entity) {
|
||||
|
||||
Links links = entity.getBody() != null ? entity.getBody().getLinks() : Links.NONE;
|
||||
T body = entity.getBody();
|
||||
|
||||
Links links = body != null ? body.getLinks() : Links.NONE;
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(entity.getHeaders());
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
@@ -84,7 +85,7 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.LinkBuilder#slash(java.lang.Object)
|
||||
*/
|
||||
public T slash(Object object) {
|
||||
public T slash(@Nullable Object object) {
|
||||
|
||||
object = object instanceof Optional ? ((Optional<?>) object).orElse(null) : object;
|
||||
|
||||
@@ -117,7 +118,7 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
|
||||
|
||||
String fragment = components.getFragment();
|
||||
|
||||
if (StringUtils.hasText(fragment)) {
|
||||
if (fragment != null && !fragment.trim().isEmpty()) {
|
||||
builder.fragment(encoded ? fragment : encodeFragment(fragment));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Strategy interface to discover a URI mapping and related {@link org.springframework.hateoas.Affordance}s for either a
|
||||
* given type or method.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ public interface MappingDiscoverer {
|
||||
|
||||
/**
|
||||
* Returns the mapping associated with the given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return the type-level mapping or {@literal null} in case none is present.
|
||||
*/
|
||||
@@ -41,16 +41,17 @@ public interface MappingDiscoverer {
|
||||
|
||||
/**
|
||||
* Returns the mapping associated with the given {@link Method}. This will include the type-level mapping.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @return the method mapping including the type-level one or {@literal null} if neither of them present.
|
||||
*/
|
||||
@Nullable
|
||||
String getMapping(Method method);
|
||||
|
||||
/**
|
||||
* Returns the mapping for the given {@link Method} invoked on the given type. This can be used to calculate the
|
||||
* mapping for a super type method being invoked on a sub-type with a type mapping.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param method must not be {@literal null}.
|
||||
* @return the method mapping including the type-level one or {@literal null} if neither of them present.
|
||||
@@ -61,7 +62,7 @@ public interface MappingDiscoverer {
|
||||
/**
|
||||
* Returns the HTTP verbs for the given {@link Method} invoked on the given type. This can be used to build hypermedia
|
||||
* templates.
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param method
|
||||
* @return
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Value object to represent {@link MethodParameters} to allow to easily find the ones with a given annotation.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MethodParameters {
|
||||
@@ -45,7 +45,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Creates a new {@link MethodParameters} from the given {@link Method}.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
*/
|
||||
public MethodParameters(Method method) {
|
||||
@@ -55,7 +55,7 @@ public class MethodParameters {
|
||||
/**
|
||||
* Creates a new {@link MethodParameters} for the given {@link Method} and {@link AnnotationAttribute}. If the latter
|
||||
* is given, method parameter names will be looked up from the annotation attribute if present.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param namingAnnotation can be {@literal null}.
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns all {@link MethodParameter}s.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<MethodParameter> getParameters() {
|
||||
@@ -80,7 +80,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns the {@link MethodParameter} with the given name or {@literal null} if none found.
|
||||
*
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
@@ -95,7 +95,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns all parameters of the given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
* @since 0.9
|
||||
@@ -111,7 +111,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns all {@link MethodParameter}s annotated with the given annotation type.
|
||||
*
|
||||
*
|
||||
* @param annotation must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -130,7 +130,7 @@ public class MethodParameters {
|
||||
/**
|
||||
* Custom {@link MethodParameter} extension that will favor the name configured in the {@link AnnotationAttribute} if
|
||||
* set over discovering it.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AnnotationNamingMethodParameter extends SynthesizingMethodParameter {
|
||||
@@ -141,7 +141,7 @@ public class MethodParameters {
|
||||
/**
|
||||
* Creates a new {@link AnnotationNamingMethodParameter} for the given {@link Method}'s parameter with the given
|
||||
* index.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param parameterIndex
|
||||
* @param attribute can be {@literal null}
|
||||
@@ -153,10 +153,11 @@ public class MethodParameters {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.MethodParameter#getParameterName()
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getParameterName() {
|
||||
|
||||
@@ -172,8 +173,7 @@ public class MethodParameters {
|
||||
}
|
||||
}
|
||||
|
||||
name = super.getParameterName();
|
||||
return name;
|
||||
return super.getParameterName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,13 +20,14 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -37,15 +38,24 @@ public @interface Relation {
|
||||
String NO_RELATION = "";
|
||||
|
||||
/**
|
||||
* Defines the relation to be used when referring to a single resource.
|
||||
*
|
||||
* Defines the relation to be used when referring to a single resource. Alias for {@link #itemRelation()}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@AliasFor("itemRelation")
|
||||
String value() default NO_RELATION;
|
||||
|
||||
/**
|
||||
* Defines the relation to be used when referring to a single resource. Alias of {@link #value()}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String itemRelation() default NO_RELATION;
|
||||
|
||||
/**
|
||||
* Defines the relation to be used when referring to a collection of resources.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String collectionRelation() default NO_RELATION;
|
||||
|
||||
@@ -109,7 +109,7 @@ public class TypeReferences {
|
||||
* @see org.springframework.core.ParameterizedTypeReference#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return this == obj || obj instanceof SyntheticParameterizedTypeReference
|
||||
&& this.type.equals(((SyntheticParameterizedTypeReference<?>) obj).type);
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ public class WebHandler {
|
||||
|
||||
for (AnnotatedParametersParameterAccessor.BoundMethodParameter parameter : PATH_VARIABLE_ACCESSOR
|
||||
.getBoundParameters(invocation)) {
|
||||
|
||||
values.put(parameter.getVariableName(), encodePath(parameter.asString()));
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ public class WebHandler {
|
||||
* @see org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor#createParameter(org.springframework.core.MethodParameter, java.lang.Object, org.springframework.hateoas.core.AnnotationAttribute)
|
||||
*/
|
||||
@Override
|
||||
protected BoundMethodParameter createParameter(final MethodParameter parameter, Object value,
|
||||
protected BoundMethodParameter createParameter(final MethodParameter parameter, @Nullable Object value,
|
||||
AnnotationAttribute attribute) {
|
||||
|
||||
return new BoundMethodParameter(parameter, value, attribute) {
|
||||
@@ -252,7 +253,7 @@ public class WebHandler {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object verifyParameterValue(MethodParameter parameter, Object value) {
|
||||
protected Object verifyParameterValue(MethodParameter parameter, @Nullable Object value) {
|
||||
|
||||
RequestParam annotation = parameter.getParameterAnnotation(RequestParam.class);
|
||||
|
||||
@@ -334,7 +335,7 @@ public class WebHandler {
|
||||
* @return the verified value.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object verifyParameterValue(MethodParameter parameter, Object value) {
|
||||
protected Object verifyParameterValue(MethodParameter parameter, @Nullable Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -414,12 +415,21 @@ public class WebHandler {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public String asString() {
|
||||
|
||||
return value == null //
|
||||
? null //
|
||||
: (String) CONVERSION_SERVICE.convert(value, parameterTypeDescriptor, STRING_DESCRIPTOR);
|
||||
Object value = this.value;
|
||||
|
||||
if (value == null) {
|
||||
throw new IllegalStateException("Cannot turn null value into required String!");
|
||||
}
|
||||
|
||||
Object result = CONVERSION_SERVICE.convert(value, parameterTypeDescriptor, STRING_DESCRIPTOR);
|
||||
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(String.format("Conversion of value %s resulted in null!", value));
|
||||
}
|
||||
|
||||
return (String) result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.mvc;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.ExposesResourceFor;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
@@ -23,21 +23,34 @@ import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* {@link LinkRelationProvider} inspecting {@link ExposesResourceFor} annotations on controller classes.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
|
||||
private final Class<?> controllerType;
|
||||
private final Class<?> entityType;
|
||||
private final PluginRegistry<LinkRelationProvider, Class<?>> providers;
|
||||
private final PluginRegistry<LinkRelationProvider, LookupContext> providers;
|
||||
|
||||
public ControllerLinkRelationProvider(Class<?> controller, PluginRegistry<LinkRelationProvider, Class<?>> providers) {
|
||||
/**
|
||||
* Creates a new {@link ControllerLinkRelationProvider}
|
||||
*
|
||||
* @param controller must not be {@literal null}.
|
||||
* @param providers must not be {@literal null}.
|
||||
*/
|
||||
public ControllerLinkRelationProvider(Class<?> controller,
|
||||
PluginRegistry<LinkRelationProvider, LookupContext> providers) {
|
||||
|
||||
Assert.notNull(controller, "Controller must not be null!");
|
||||
Assert.notNull(providers, "LinkRelationProviders must not be null!");
|
||||
|
||||
ExposesResourceFor annotation = AnnotationUtils.findAnnotation(controller, ExposesResourceFor.class);
|
||||
ExposesResourceFor annotation = AnnotatedElementUtils.findMergedAnnotation(controller, ExposesResourceFor.class);
|
||||
|
||||
Assert.notNull(annotation, "Controller must be annotated with ExposesResourceFor!");
|
||||
if (annotation == null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Controller %s must be annotated with @ExposesResourceFor(…)!", controller.getName()));
|
||||
}
|
||||
|
||||
this.controllerType = controller;
|
||||
this.entityType = annotation.value();
|
||||
@@ -50,7 +63,10 @@ public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getItemResourceRelFor(Class<?> resource) {
|
||||
return providers.getRequiredPluginFor(entityType).getItemResourceRelFor(resource);
|
||||
|
||||
LookupContext context = LookupContext.forItemResourceRelLookup(entityType);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getItemResourceRelFor(resource);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -59,7 +75,10 @@ public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getCollectionResourceRelFor(Class<?> resource) {
|
||||
return providers.getRequiredPluginFor(entityType).getCollectionResourceRelFor(resource);
|
||||
|
||||
LookupContext context = LookupContext.forCollectionResourceRelLookup(entityType);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getCollectionResourceRelFor(resource);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -67,7 +86,7 @@ public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return controllerType.equals(delimiter);
|
||||
public boolean supports(LookupContext context) {
|
||||
return context.handlesType(controllerType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public class JacksonSerializers {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public MediaType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return MediaType.parseMediaType(p.getText());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.
|
||||
@@ -19,6 +19,7 @@ import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.hateoas.server.core.HeaderLinksResponseEntity;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
@@ -84,8 +86,8 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
Object value = returnValue;
|
||||
|
||||
@@ -99,8 +101,14 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
return;
|
||||
}
|
||||
|
||||
Method method = returnType.getMethod();
|
||||
|
||||
if (method == null) {
|
||||
throw new IllegalStateException(String.format("Return type %s does not expose a method!", returnType));
|
||||
}
|
||||
|
||||
// We have a Resource or Resources - find suitable processors
|
||||
ResolvableType targetType = ResolvableType.forMethodReturnType(returnType.getMethod());
|
||||
ResolvableType targetType = ResolvableType.forMethodReturnType(method);
|
||||
|
||||
// Unbox HttpEntity
|
||||
if (HTTP_ENTITY_TYPE.isAssignableFrom(targetType)) {
|
||||
@@ -127,7 +135,7 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
* @param originalValue the original input value.
|
||||
* @return
|
||||
*/
|
||||
Object rewrapResult(RepresentationModel<?> newBody, Object originalValue) {
|
||||
Object rewrapResult(RepresentationModel<?> newBody, @Nullable Object originalValue) {
|
||||
|
||||
if (!(originalValue instanceof HttpEntity)) {
|
||||
return rootLinksAsHeaders ? HeaderLinksResponseEntity.wrap(newBody) : newBody;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-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.
|
||||
@@ -107,8 +107,13 @@ public class RepresentationModelProcessorInvoker {
|
||||
if (RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(referenceType)) {
|
||||
|
||||
CollectionModel<?> resources = (CollectionModel<?>) value;
|
||||
ResolvableType elementTargetType = ResolvableType.forClass(CollectionModel.class, referenceType.getRawClass())
|
||||
.getGeneric(0);
|
||||
Class<?> rawClass = referenceType.getRawClass();
|
||||
|
||||
if (rawClass == null) {
|
||||
throw new IllegalArgumentException(String.format("%s does not expose a raw type!", referenceType));
|
||||
}
|
||||
|
||||
ResolvableType elementTargetType = ResolvableType.forClass(CollectionModel.class, rawClass).getGeneric(0);
|
||||
List<Object> result = new ArrayList<>(resources.getContent().size());
|
||||
|
||||
for (Object element : resources) {
|
||||
@@ -421,7 +426,9 @@ public class RepresentationModelProcessorInvoker {
|
||||
*/
|
||||
private static ResolvableType getSuperType(ResolvableType source, Class<?> superType) {
|
||||
|
||||
if (source.getRawClass() != null && source.getRawClass().equals(superType)) {
|
||||
Class<?> rawType = source.getRawClass();
|
||||
|
||||
if (rawType != null && rawType.equals(superType)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -452,7 +459,7 @@ public class RepresentationModelProcessorInvoker {
|
||||
public static RepresentationModelProcessorInvoker.CustomOrderAwareComparator INSTANCE = new CustomOrderAwareComparator();
|
||||
|
||||
@Override
|
||||
protected int getOrder(Object obj) {
|
||||
protected int getOrder(@Nullable Object obj) {
|
||||
return super.getOrder(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -28,7 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* Extension of {@link MappingJackson2HttpMessageConverter} to constrain the ability to read and write HTTP message
|
||||
* based on the target type. Useful in case the {@link ObjectMapper} about to be configured has customizations that
|
||||
* shall only be applied to object trees of a certain base type.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class TypeConstrainedMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
|
||||
@@ -37,7 +38,7 @@ public class TypeConstrainedMappingJackson2HttpMessageConverter extends MappingJ
|
||||
|
||||
/**
|
||||
* Creates a new {@link TypeConstrainedMappingJackson2HttpMessageConverter} for the given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter(Class<?> type) {
|
||||
@@ -53,38 +54,39 @@ public class TypeConstrainedMappingJackson2HttpMessageConverter extends MappingJ
|
||||
* @param supportedMediaTypes
|
||||
* @param objectMapper
|
||||
*/
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter(Class<?> type, List<MediaType> supportedMediaTypes, ObjectMapper objectMapper) {
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter(Class<?> type, List<MediaType> supportedMediaTypes,
|
||||
ObjectMapper objectMapper) {
|
||||
|
||||
this(type);
|
||||
setSupportedMediaTypes(supportedMediaTypes);
|
||||
setObjectMapper(objectMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#canRead(java.lang.Class, org.springframework.http.MediaType)
|
||||
*/
|
||||
@Override
|
||||
public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
|
||||
return type.isAssignableFrom(clazz) && super.canRead(clazz, mediaType);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#canRead(java.lang.reflect.Type, java.lang.Class, org.springframework.http.MediaType)
|
||||
*/
|
||||
@Override
|
||||
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
|
||||
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
|
||||
return this.type.isAssignableFrom(getJavaType(type, contextClass).getRawClass())
|
||||
&& super.canRead(type, contextClass, mediaType);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#canWrite(java.lang.Class, org.springframework.http.MediaType)
|
||||
*/
|
||||
@Override
|
||||
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
|
||||
return type.isAssignableFrom(clazz) && super.canWrite(clazz, mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ class UriComponentsBuilderFactory {
|
||||
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
|
||||
if (requestAttributes == null) {
|
||||
throw new IllegalStateException("Could not look up RequestAttributes!");
|
||||
}
|
||||
|
||||
Assert.state(requestAttributes != null, REQUEST_ATTRIBUTES_MISSING);
|
||||
Assert.isInstanceOf(ServletRequestAttributes.class, requestAttributes);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.hateoas.server.mvc;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.hateoas.server.MethodLinkBuilderFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
@@ -24,7 +25,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* SPI callback to enhance a {@link UriComponentsBuilder} when referring to a method through a dummy method invocation.
|
||||
* Will usually be implemented in implementations of {@link HandlerMethodArgumentResolver} as they represent exactly the
|
||||
* same functionality inverted.
|
||||
*
|
||||
*
|
||||
* @see MethodLinkBuilderFactory#linkTo(Object)
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -32,7 +33,7 @@ public interface UriComponentsContributor {
|
||||
|
||||
/**
|
||||
* Returns whether the {@link UriComponentsBuilder} supports the given {@link MethodParameter}.
|
||||
*
|
||||
*
|
||||
* @param parameter will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -40,10 +41,10 @@ public interface UriComponentsContributor {
|
||||
|
||||
/**
|
||||
* Enhance the given {@link UriComponentsBuilder} with the given value.
|
||||
*
|
||||
*
|
||||
* @param builder will never be {@literal null}.
|
||||
* @param parameter will never be {@literal null}.
|
||||
* @param parameter can be {@literal null}.
|
||||
* @param value can be {@literal null}.
|
||||
*/
|
||||
void enhance(UriComponentsBuilder builder, MethodParameter parameter, Object value);
|
||||
void enhance(UriComponentsBuilder builder, @Nullable MethodParameter parameter, @Nullable Object value);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.web.util.UriTemplate;
|
||||
* @author Oliver Trosien
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class WebMvcLinkBuilder extends TemplateVariableAwareLinkBuilderSupport<WebMvcLinkBuilder> {
|
||||
|
||||
private static final MappingDiscoverer DISCOVERER = CachingMappingDiscoverer
|
||||
|
||||
Reference in New Issue
Block a user