#1382 - Polishing.
Let HalForms register a custom ObjectMapper customizer to allow separate customizations. If HAL and HAL Forms are supposed to be customized the same way, the same customizer can be registered on both instances. Tweak the application of the customizer to happen inside the configuration classes, not on the outside. Prepare HalFormsConfiguration to become a fully immutable type in 1.3. Javadoc and assertion polish. Original pull request: #1383.
This commit is contained in:
@@ -62,18 +62,17 @@ public class HalConfiguration {
|
||||
* Creates a new default {@link HalConfiguration} rendering single links as immediate sub-document.
|
||||
*/
|
||||
public HalConfiguration() {
|
||||
|
||||
this.renderSingleLinks = RenderSingleLinks.AS_SINGLE;
|
||||
this.singleLinksPerPattern = new LinkedHashMap<>();
|
||||
this.applyPropertyNamingStrategy = true;
|
||||
this.enforceEmbeddedCollections = true;
|
||||
this.objectMapperCustomizer = objectMapper -> {}; // Default to no action.
|
||||
this(RenderSingleLinks.AS_SINGLE, new LinkedHashMap<>(), true, true, __ -> {});
|
||||
}
|
||||
|
||||
private HalConfiguration(RenderSingleLinks renderSingleLinks, Map<String, RenderSingleLinks> singleLinksPerPattern,
|
||||
boolean applyPropertyNamingStrategy, boolean enforceEmbeddedCollections,
|
||||
Consumer<ObjectMapper> objectMapperCustomizer) {
|
||||
|
||||
Assert.notNull(renderSingleLinks, "RenderSingleLinks must not be null!");
|
||||
Assert.notNull(singleLinksPerPattern, "Single links per pattern map must not be null!");
|
||||
Assert.notNull(objectMapperCustomizer, "ObjectMapper customizer must not be null!");
|
||||
|
||||
this.renderSingleLinks = renderSingleLinks;
|
||||
this.singleLinksPerPattern = singleLinksPerPattern;
|
||||
this.applyPropertyNamingStrategy = applyPropertyNamingStrategy;
|
||||
@@ -102,12 +101,15 @@ public class HalConfiguration {
|
||||
* relation (like {@code search}), take wildcards to e.g. match links of a given curie (like {@code acme:*}) or even
|
||||
* complete URIs (like {@code https://api.acme.com/foo/**}).
|
||||
*
|
||||
* @param pattern must not be {@literal null}.
|
||||
* @param pattern must not be {@literal null} or empty.
|
||||
* @param renderSingleLinks must not be {@literal null}.
|
||||
* @return @see PathMatcher
|
||||
*/
|
||||
public HalConfiguration withRenderSingleLinksFor(String pattern, RenderSingleLinks renderSingleLinks) {
|
||||
|
||||
Assert.hasText(pattern, "Pattern must not be null or empty!");
|
||||
Assert.notNull(renderSingleLinks, "RenderSingleLinks must not be null!");
|
||||
|
||||
Map<String, RenderSingleLinks> map = new LinkedHashMap<>(singleLinksPerPattern);
|
||||
map.put(pattern, renderSingleLinks);
|
||||
|
||||
@@ -132,27 +134,33 @@ public class HalConfiguration {
|
||||
/**
|
||||
* Create a new {@link HalConfiguration} by copying the attributes and replacing the {@literal renderSingleLinks}.
|
||||
*
|
||||
* @param renderSingleLinks
|
||||
* @return
|
||||
* @param renderSingleLinks must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
public HalConfiguration withRenderSingleLinks(RenderSingleLinks renderSingleLinks) {
|
||||
|
||||
return this.renderSingleLinks == renderSingleLinks ? this
|
||||
: new HalConfiguration(renderSingleLinks, this.singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections, this.objectMapperCustomizer);
|
||||
Assert.notNull(renderSingleLinks, "RenderSingleLinks must not be null!");
|
||||
|
||||
return this.renderSingleLinks == renderSingleLinks //
|
||||
? this //
|
||||
: new HalConfiguration(renderSingleLinks, singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HalConfiguration} by copying the attributes and replacing the {@literal singleLinksPattern}.
|
||||
*
|
||||
* @param singleLinksPerPattern
|
||||
* @return
|
||||
* @param singleLinksPerPattern must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
private HalConfiguration withSingleLinksPerPattern(Map<String, RenderSingleLinks> singleLinksPerPattern) {
|
||||
|
||||
return this.singleLinksPerPattern == singleLinksPerPattern ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections, this.objectMapperCustomizer);
|
||||
Assert.notNull(singleLinksPerPattern, "Single links per pattern map must not be null!");
|
||||
|
||||
return this.singleLinksPerPattern == singleLinksPerPattern //
|
||||
? this //
|
||||
: new HalConfiguration(renderSingleLinks, singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,9 +172,10 @@ public class HalConfiguration {
|
||||
*/
|
||||
public HalConfiguration withApplyPropertyNamingStrategy(boolean applyPropertyNamingStrategy) {
|
||||
|
||||
return this.applyPropertyNamingStrategy == applyPropertyNamingStrategy ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, this.singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections, this.objectMapperCustomizer);
|
||||
return this.applyPropertyNamingStrategy == applyPropertyNamingStrategy //
|
||||
? this //
|
||||
: new HalConfiguration(renderSingleLinks, singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,32 +187,37 @@ public class HalConfiguration {
|
||||
*/
|
||||
public HalConfiguration withEnforceEmbeddedCollections(boolean enforceEmbeddedCollections) {
|
||||
|
||||
return this.enforceEmbeddedCollections == enforceEmbeddedCollections ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, this.singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections, this.objectMapperCustomizer);
|
||||
return this.enforceEmbeddedCollections == enforceEmbeddedCollections //
|
||||
? this //
|
||||
: new HalConfiguration(renderSingleLinks, singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
public HalConfiguration withObjectMapperCustomizer(Consumer<ObjectMapper> objectMapperCustomizer) {
|
||||
|
||||
return this.objectMapperCustomizer == objectMapperCustomizer ? this
|
||||
: new HalConfiguration(this.renderSingleLinks, this.singleLinksPerPattern, this.applyPropertyNamingStrategy,
|
||||
this.enforceEmbeddedCollections, objectMapperCustomizer);
|
||||
return this.objectMapperCustomizer == objectMapperCustomizer //
|
||||
? this //
|
||||
: new HalConfiguration(renderSingleLinks, singleLinksPerPattern, applyPropertyNamingStrategy,
|
||||
enforceEmbeddedCollections, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
public RenderSingleLinks getRenderSingleLinks() {
|
||||
return this.renderSingleLinks;
|
||||
return renderSingleLinks;
|
||||
}
|
||||
|
||||
public boolean isApplyPropertyNamingStrategy() {
|
||||
return this.applyPropertyNamingStrategy;
|
||||
return applyPropertyNamingStrategy;
|
||||
}
|
||||
|
||||
public boolean isEnforceEmbeddedCollections() {
|
||||
return this.enforceEmbeddedCollections;
|
||||
return enforceEmbeddedCollections;
|
||||
}
|
||||
|
||||
public Consumer<ObjectMapper> getObjectMapperCustomizer() {
|
||||
return this.objectMapperCustomizer;
|
||||
public HalConfiguration customize(ObjectMapper mapper) {
|
||||
|
||||
this.objectMapperCustomizer.accept(mapper);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,7 +86,7 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider,
|
||||
curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver, halConfiguration, beanFactory));
|
||||
|
||||
halConfiguration.getObjectMapperCustomizer().accept(mapper);
|
||||
halConfiguration.customize(mapper);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,14 @@ package org.springframework.hateoas.mediatype.hal.forms;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* HAL-FORMS specific configuration extension of {@link HalConfiguration}.
|
||||
@@ -31,26 +36,111 @@ import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
public class HalFormsConfiguration {
|
||||
|
||||
private final HalConfiguration halConfiguration;
|
||||
private final Map<Class<?>, String> patterns = new HashMap<>();
|
||||
private final Map<Class<?>, String> patterns;
|
||||
private final Consumer<ObjectMapper> objectMapperCustomizer;
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalFormsConfiguration} backed by a default {@link HalConfiguration}.
|
||||
*/
|
||||
public HalFormsConfiguration() {
|
||||
this.halConfiguration = new HalConfiguration();
|
||||
this(new HalConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HalFormsConfiguration} for the given {@link HalConfiguration}.
|
||||
*
|
||||
* @param halConfiguration must not be {@literal null}.
|
||||
*/
|
||||
public HalFormsConfiguration(HalConfiguration halConfiguration) {
|
||||
this.halConfiguration = halConfiguration;
|
||||
this(halConfiguration, new HashMap<>(), __ -> {});
|
||||
}
|
||||
|
||||
private HalFormsConfiguration(HalConfiguration halConfiguration, Map<Class<?>, String> patterns,
|
||||
@Nullable Consumer<ObjectMapper> objectMapperCustomizer) {
|
||||
|
||||
Assert.notNull(halConfiguration, "HalConfiguration must not be null!");
|
||||
Assert.notNull(patterns, "Patterns must not be null!");
|
||||
Assert.notNull(objectMapperCustomizer, "ObjectMapper customizer must not be null!");
|
||||
|
||||
this.halConfiguration = halConfiguration;
|
||||
this.patterns = patterns;
|
||||
this.objectMapperCustomizer = objectMapperCustomizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a regular expression pattern to be used for form descriptions of the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param pattern must not be {@literal null} or empty.
|
||||
* @return will never be {@literal null}.
|
||||
* @deprecated prefer {@link #withPattern(Class, String)} that returns a fresh instance, to be removed with 1.3.
|
||||
*/
|
||||
@Deprecated
|
||||
public HalFormsConfiguration registerPattern(Class<?> type, String pattern) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
Assert.hasText(pattern, "Pattern must not be null or empty!");
|
||||
|
||||
patterns.put(type, pattern);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a regular expression pattern to be used for form descriptions of the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param pattern must not be {@literal null} or empty.
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
@Deprecated
|
||||
public HalFormsConfiguration withPattern(Class<?> type, String pattern) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
Assert.hasText(pattern, "Pattern must not be null or empty!");
|
||||
|
||||
Map<Class<?>, String> newPatterns = new HashMap<>(patterns);
|
||||
newPatterns.put(type, pattern);
|
||||
|
||||
return new HalFormsConfiguration(halConfiguration, newPatterns, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given {@link Consumer} to apply additional customizations on the {@link ObjectMapper} used to render
|
||||
* HAL documents.
|
||||
*
|
||||
* @param objectMapperCustomizer must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
public HalFormsConfiguration withObjectMapperCustomizer(Consumer<ObjectMapper> objectMapperCustomizer) {
|
||||
|
||||
Assert.notNull(objectMapperCustomizer, "ObjectMapper customizer must not be null!");
|
||||
|
||||
return this.objectMapperCustomizer == objectMapperCustomizer //
|
||||
? this //
|
||||
: new HalFormsConfiguration(halConfiguration, patterns, objectMapperCustomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizes the given {@link ObjectMapper} with the registered callback.
|
||||
*
|
||||
* @param mapper must not be {@literal null}.
|
||||
* @return
|
||||
* @see #withObjectMapperCustomizer(Consumer)
|
||||
*/
|
||||
public HalFormsConfiguration customize(ObjectMapper mapper) {
|
||||
|
||||
Assert.notNull(mapper, "ObjectMapper must not be null!");
|
||||
|
||||
objectMapperCustomizer.accept(mapper);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public HalConfiguration getHalConfiguration() {
|
||||
return halConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the regular expression pattern that is registered for the given type.
|
||||
*
|
||||
@@ -60,8 +150,4 @@ public class HalFormsConfiguration {
|
||||
Optional<String> getTypePatternFor(ResolvableType type) {
|
||||
return Optional.ofNullable(patterns.get(type.resolve(Object.class)));
|
||||
}
|
||||
|
||||
public HalConfiguration getHalConfiguration() {
|
||||
return this.halConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
|
||||
mapper.setHandlerInstantiator(new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider,
|
||||
curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver, configuration, beanFactory));
|
||||
|
||||
configuration.getHalConfiguration().getObjectMapperCustomizer().accept(mapper);
|
||||
configuration.customize(mapper);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user