diff --git a/pom.xml b/pom.xml
index 1db9b70ebb..087370b2ce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-commons
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATACMNS-1035-SNAPSHOT
Spring Data Core
diff --git a/readme.md b/readme.md
index 6ce69386d3..3a7ae02c97 100644
--- a/readme.md
+++ b/readme.md
@@ -31,7 +31,7 @@ This README as well as the [reference documentation](http://docs.spring.io/sprin
The main project [website](http://projects.spring.io/spring-data/) contains links to basic project information such as source code, JavaDocs, Issue tracking, etc.
-For more detailed questions, please refer to [spring-data on stackoverflow](http://stackoverflow.com/questions/tagged/spring-data). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](https://spring.io/projects).
+For more detailed questions, please refer to [spring-data on stackoverflow](http://stackoverflow.com/questions/tagged/spring-data). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](https://spring.io/projects).
## Contributing to Spring Data Commons
diff --git a/src/main/java/org/springframework/data/convert/CustomConversions.java b/src/main/java/org/springframework/data/convert/CustomConversions.java
new file mode 100644
index 0000000000..d8d1cc7507
--- /dev/null
+++ b/src/main/java/org/springframework/data/convert/CustomConversions.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2011-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.convert;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Value;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.core.GenericTypeResolver;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+import org.springframework.core.convert.converter.GenericConverter;
+import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
+import org.springframework.core.convert.support.GenericConversionService;
+import org.springframework.data.mapping.model.SimpleTypeHolder;
+import org.springframework.data.util.Optionals;
+import org.springframework.data.util.Streamable;
+import org.springframework.util.Assert;
+
+/**
+ * Value object to capture custom conversion. That is essentially a {@link List} of converters and some additional logic
+ * around them. The converters build up two sets of types which store-specific basic types can be converted into and
+ * from. These types will be considered simple ones (which means they neither need deeper inspection nor nested
+ * conversion. Thus the {@link CustomConversions} also act as factory for {@link SimpleTypeHolder} .
+ *
+ * @author Oliver Gierke
+ * @author Thomas Darimont
+ * @author Christoph Strobl
+ * @author Mark Paluch
+ * @since 2.0
+ */
+@Slf4j
+public class CustomConversions {
+
+ private static final String READ_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.";
+ private static final String WRITE_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as writing converter although it doesn't convert to a store-supported type! You might wanna check you annotation setup at the converter implementation.";
+ private static final String NOT_A_CONVERTER = "Converter %s is neither a Spring Converter, GenericConverter or ConverterFactory!";
+ private static final List DEFAULT_CONVERTERS;
+
+ static {
+
+ List defaults = new ArrayList<>();
+
+ defaults.addAll(JodaTimeConverters.getConvertersToRegister());
+ defaults.addAll(Jsr310Converters.getConvertersToRegister());
+ defaults.addAll(ThreeTenBackPortConverters.getConvertersToRegister());
+
+ DEFAULT_CONVERTERS = Collections.unmodifiableList(defaults);
+ }
+
+ private final Set readingPairs;
+ private final Set writingPairs;
+ private final Set> customSimpleTypes;
+ private final SimpleTypeHolder simpleTypeHolder;
+
+ private final List converters;
+
+ private final Map>> customReadTargetTypes;
+ private final Map>> customWriteTargetTypes;
+ private final Map, Optional>> rawWriteTargetTypes;
+
+ /**
+ * Creates a new {@link CustomConversions} instance registering the given converters.
+ *
+ * @param converters
+ */
+ public CustomConversions(StoreConversions storeConversions, List> converters) {
+
+ Assert.notNull(converters, "List of converters must not be null!");
+
+ this.readingPairs = new LinkedHashSet<>();
+ this.writingPairs = new LinkedHashSet<>();
+ this.customSimpleTypes = new HashSet<>();
+ this.customReadTargetTypes = new ConcurrentHashMap<>();
+ this.customWriteTargetTypes = new ConcurrentHashMap<>();
+ this.rawWriteTargetTypes = new ConcurrentHashMap<>();
+
+ List toRegister = new ArrayList();
+
+ // Add user provided converters to make sure they can override the defaults
+ toRegister.addAll(converters);
+ toRegister.addAll(storeConversions.getStoreConverters());
+ toRegister.addAll(DEFAULT_CONVERTERS);
+
+ toRegister.stream()//
+ .flatMap(it -> storeConversions.getRegistrationsFor(it).stream())//
+ .forEach(this::register);
+
+ Collections.reverse(toRegister);
+
+ this.converters = Collections.unmodifiableList(toRegister);
+ this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, storeConversions.getStoreTypeHolder());
+ }
+
+ /**
+ * Returns the underlying {@link SimpleTypeHolder}.
+ *
+ * @return
+ */
+ public SimpleTypeHolder getSimpleTypeHolder() {
+ return simpleTypeHolder;
+ }
+
+ /**
+ * Returns whether the given type is considered to be simple. That means it's either a general simple type or we have
+ * a writing {@link Converter} registered for a particular type.
+ *
+ * @see SimpleTypeHolder#isSimpleType(Class)
+ * @param type
+ * @return
+ */
+ public boolean isSimpleType(Class> type) {
+
+ Assert.notNull(type, "Type must not be null!");
+
+ return simpleTypeHolder.isSimpleType(type);
+ }
+
+ /**
+ * Populates the given {@link GenericConversionService} with the converters registered.
+ *
+ * @param conversionService
+ */
+ public void registerConvertersIn(GenericConversionService conversionService) {
+
+ Assert.notNull(conversionService, "ConversionService must not be null!");
+
+ converters.forEach(it -> {
+
+ boolean added = false;
+
+ if (it instanceof Converter) {
+ conversionService.addConverter(Converter.class.cast(it));
+ added = true;
+ }
+
+ if (it instanceof ConverterFactory) {
+ conversionService.addConverterFactory(ConverterFactory.class.cast(it));
+ added = true;
+ }
+
+ if (it instanceof GenericConverter) {
+ conversionService.addConverter(GenericConverter.class.cast(it));
+ added = true;
+ }
+
+ if (!added) {
+ throw new IllegalArgumentException(String.format(NOT_A_CONVERTER, it));
+ }
+ });
+ }
+
+ /**
+ * Registers the given {@link ConvertiblePair} as reading or writing pair depending on the type sides being basic
+ * Mongo types.
+ *
+ * @param pair
+ */
+ private void register(ConverterRegistration converterRegistration) {
+
+ Assert.notNull(converterRegistration, "Converter registration must not be null!");
+
+ ConvertiblePair pair = converterRegistration.getConvertiblePair();
+
+ if (converterRegistration.isReading()) {
+
+ readingPairs.add(pair);
+
+ if (LOG.isWarnEnabled() && !converterRegistration.isSimpleSourceType()) {
+ LOG.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
+ }
+ }
+
+ if (converterRegistration.isWriting()) {
+
+ writingPairs.add(pair);
+ customSimpleTypes.add(pair.getSourceType());
+
+ if (LOG.isWarnEnabled() && !converterRegistration.isSimpleTargetType()) {
+ LOG.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
+ }
+ }
+ }
+
+ /**
+ * Returns the target type to convert to in case we have a custom conversion registered to convert the given source
+ * type into a Mongo native one.
+ *
+ * @param sourceType must not be {@literal null}
+ * @return
+ */
+ public Optional> getCustomWriteTarget(Class> sourceType) {
+
+ Assert.notNull(sourceType, "Source type must not be null!");
+
+ return rawWriteTargetTypes.computeIfAbsent(sourceType,
+ it -> getCustomTarget(sourceType, Optional.empty(), writingPairs));
+ }
+
+ /**
+ * Returns the target type we can readTargetWriteLocl an inject of the given source type to. The returned type might
+ * be a subclass of the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply
+ * return the first target type matching or {@literal null} if no conversion can be found.
+ *
+ * @param sourceType must not be {@literal null}
+ * @param requestedTargetType must not be {@literal null}.
+ * @return
+ */
+ public Optional> getCustomWriteTarget(Class> sourceType, Class> requestedTargetType) {
+
+ Assert.notNull(sourceType, "Source type must not be null!");
+ Assert.notNull(requestedTargetType, "Target type must not be null!");
+
+ return customWriteTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, requestedTargetType),
+ it -> getCustomTarget(sourceType, Optional.of(requestedTargetType), writingPairs));
+ }
+
+ /**
+ * Returns whether we have a custom conversion registered to readTargetWriteLocl into a Mongo native type. The
+ * returned type might be a subclass of the given expected type though.
+ *
+ * @param sourceType must not be {@literal null}
+ * @return
+ */
+ public boolean hasCustomWriteTarget(Class> sourceType) {
+
+ Assert.notNull(sourceType, "Source type must not be null!");
+
+ return getCustomWriteTarget(sourceType).isPresent();
+ }
+
+ /**
+ * Returns whether we have a custom conversion registered to readTargetWriteLocl an object of the given source type
+ * into an object of the given Mongo native target type.
+ *
+ * @param sourceType must not be {@literal null}.
+ * @param targetType must not be {@literal null}.
+ * @return
+ */
+ public boolean hasCustomWriteTarget(Class> sourceType, Class> targetType) {
+
+ Assert.notNull(sourceType, "Source type must not be null!");
+ Assert.notNull(targetType, "Target type must not be null!");
+
+ return getCustomWriteTarget(sourceType, targetType).isPresent();
+ }
+
+ /**
+ * Returns whether we have a custom conversion registered to readTargetReadLock the given source into the given target
+ * type.
+ *
+ * @param sourceType must not be {@literal null}
+ * @param targetType must not be {@literal null}
+ * @return
+ */
+ public boolean hasCustomReadTarget(Class> sourceType, Class> targetType) {
+
+ Assert.notNull(sourceType, "Source type must not be null!");
+ Assert.notNull(targetType, "Target type must not be null!");
+
+ return getCustomReadTarget(sourceType, targetType).isPresent();
+ }
+
+ /**
+ * Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
+ * returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
+ *
+ * @param sourceType must not be {@literal null}.
+ * @param targetType must not be {@literal null}.
+ * @return
+ */
+ private Optional> getCustomReadTarget(Class> sourceType, Class> targetType) {
+
+ return customReadTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, targetType),
+ it -> getCustomTarget(sourceType, Optional.of(targetType), readingPairs));
+ }
+
+ /**
+ * Inspects the given {@link ConvertiblePair}s for ones that have a source compatible type as source. Additionally
+ * checks assignability of the target type if one is given.
+ *
+ * @param sourceType must not be {@literal null}.
+ * @param targetType can be {@literal null}.
+ * @param pairs must not be {@literal null}.
+ * @return
+ */
+ private static Optional> getCustomTarget(Class> sourceType, Optional> targetType,
+ Collection pairs) {
+
+ Assert.notNull(sourceType, "Source Class must not be null!");
+ Assert.notNull(pairs, "Collection of ConvertiblePair must not be null!");
+
+ return Optionals.firstNonEmpty(//
+ () -> targetType.filter(it -> pairs.contains(new ConvertiblePair(sourceType, it))), //
+ () -> pairs.stream()//
+ .filter(it -> hasAssignableSourceType(it, sourceType)) //
+ .> map(ConvertiblePair::getTargetType)//
+ .filter(it -> requestTargetTypeIsAssignable(targetType, it))//
+ .findFirst());
+ }
+
+ private static boolean hasAssignableSourceType(ConvertiblePair pair, Class> sourceType) {
+ return pair.getSourceType().isAssignableFrom(sourceType);
+ }
+
+ private static boolean requestTargetTypeIsAssignable(Optional> requestedTargetType, Class> targetType) {
+
+ return !requestedTargetType.isPresent() //
+ ? true //
+ : requestedTargetType.map(it -> targetType.isAssignableFrom(it)).orElse(false);
+ }
+
+ /**
+ * Conversion registration information.
+ *
+ * @author Oliver Gierke
+ * @author Mark Paluch
+ */
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ private static class ConverterRegistration {
+
+ private final @NonNull ConvertiblePair convertiblePair;
+ private final @NonNull StoreConversions storeConversions;
+ private final boolean reading;
+ private final boolean writing;
+
+ /**
+ * Returns whether the converter shall be used for writing.
+ *
+ * @return
+ */
+ public boolean isWriting() {
+ return writing == true || (!reading && isSimpleTargetType());
+ }
+
+ /**
+ * Returns whether the converter shall be used for reading.
+ *
+ * @return
+ */
+ public boolean isReading() {
+ return reading == true || (!writing && isSimpleSourceType());
+ }
+
+ /**
+ * Returns the actual conversion pair.
+ *
+ * @return
+ */
+ public ConvertiblePair getConvertiblePair() {
+ return convertiblePair;
+ }
+
+ /**
+ * Returns whether the source type is a Mongo simple one.
+ *
+ * @return
+ */
+ public boolean isSimpleSourceType() {
+ return storeConversions.isStoreSimpleType(convertiblePair.getSourceType());
+ }
+
+ /**
+ * Returns whether the target type is a Mongo simple one.
+ *
+ * @return
+ */
+ public boolean isSimpleTargetType() {
+ return storeConversions.isStoreSimpleType(convertiblePair.getTargetType());
+ }
+ }
+
+ /**
+ * Value type to capture store-specific extensions to the {@link CustomConversions}. Allows to forward store specific
+ * default conversions and a set of types that are supposed to be considered simple.
+ *
+ * @author Oliver Gierke
+ */
+ @Value
+ @Getter(AccessLevel.PACKAGE)
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ public static class StoreConversions {
+
+ public static final StoreConversions NONE = StoreConversions.of(SimpleTypeHolder.DEFAULT, Collections.emptyList());
+
+ SimpleTypeHolder storeTypeHolder;
+ Collection> storeConverters;
+
+ /**
+ * Creates a new {@link StoreConversions} for the given store-specific {@link SimpleTypeHolder} and the given
+ * converters.
+ *
+ * @param storeTypeHolder must not be {@literal null}.
+ * @param converters must not be {@literal null}.
+ * @return
+ */
+ public static StoreConversions of(SimpleTypeHolder storeTypeHolder, Object... converters) {
+
+ Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null!");
+ Assert.notNull(converters, "Converters must not be null!");
+
+ return new StoreConversions(storeTypeHolder, Arrays.asList(converters));
+ }
+
+ /**
+ * Creates a new {@link StoreConversions} for the given store-specific {@link SimpleTypeHolder} and the given
+ * converters.
+ *
+ * @param storeTypeHolder must not be {@literal null}.
+ * @param converters must not be {@literal null}.
+ * @return
+ */
+ public static StoreConversions of(SimpleTypeHolder storeTypeHolder, Collection> converters) {
+
+ Assert.notNull(storeTypeHolder, "SimpleTypeHolder must not be null!");
+ Assert.notNull(converters, "Converters must not be null!");
+
+ return new StoreConversions(storeTypeHolder, converters);
+ }
+
+ /**
+ * Returns {@link ConverterRegistration}s for the given converter.
+ *
+ * @param converter must not be {@literal null}.
+ * @return
+ */
+ public Streamable getRegistrationsFor(Object converter) {
+
+ Assert.notNull(converter, "Converter must not be null!");
+
+ Class> type = converter.getClass();
+ boolean isWriting = type.isAnnotationPresent(WritingConverter.class);
+ boolean isReading = type.isAnnotationPresent(ReadingConverter.class);
+
+ if (converter instanceof GenericConverter) {
+
+ GenericConverter genericConverter = (GenericConverter) converter;
+ return Streamable.of(genericConverter.getConvertibleTypes()).map(it -> register(it, isReading, isWriting));
+
+ } else if (converter instanceof ConverterFactory) {
+
+ return getRegistrationFor(converter, ConverterFactory.class, isReading, isWriting);
+
+ } else if (converter instanceof Converter) {
+
+ return getRegistrationFor(converter, Converter.class, isReading, isWriting);
+
+ } else {
+ throw new IllegalArgumentException("Unsupported converter type!");
+ }
+ }
+
+ private Streamable getRegistrationFor(Object converter, Class> type, boolean isReading,
+ boolean isWriting) {
+
+ Class>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), type);
+ return Streamable.of(register(arguments[0], arguments[1], isReading, isWriting));
+ }
+
+ private ConverterRegistration register(Class> source, Class> target, boolean isReading, boolean isWriting) {
+ return register(new ConvertiblePair(source, target), isReading, isWriting);
+ }
+
+ private ConverterRegistration register(ConvertiblePair pair, boolean isReading, boolean isWriting) {
+ return new ConverterRegistration(pair, this, isReading, isWriting);
+ }
+
+ private boolean isStoreSimpleType(Class> type) {
+ return storeTypeHolder.isSimpleType(type);
+ }
+ }
+}
diff --git a/src/main/java/org/springframework/data/domain/Chunk.java b/src/main/java/org/springframework/data/domain/Chunk.java
index 00267e349a..5a0c0aaa7d 100644
--- a/src/main/java/org/springframework/data/domain/Chunk.java
+++ b/src/main/java/org/springframework/data/domain/Chunk.java
@@ -15,6 +15,8 @@
*/
package org.springframework.data.domain;
+import lombok.Getter;
+
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
@@ -38,7 +40,7 @@ abstract class Chunk implements Slice, Serializable {
private static final long serialVersionUID = 867755909294344406L;
private final List content = new ArrayList<>();
- private final Pageable pageable;
+ private final @Getter Pageable pageable;
/**
* Creates a new {@link Chunk} with the given content and the given governing {@link Pageable}.
diff --git a/src/main/java/org/springframework/data/domain/Slice.java b/src/main/java/org/springframework/data/domain/Slice.java
index 8ad73582e4..e642a535de 100644
--- a/src/main/java/org/springframework/data/domain/Slice.java
+++ b/src/main/java/org/springframework/data/domain/Slice.java
@@ -100,6 +100,16 @@ public interface Slice extends Streamable {
*/
boolean hasPrevious();
+ /**
+ * Returns the {@link Pageable} that's been used to request the current {@link Slice}.
+ *
+ * @return
+ * @since 2.0
+ */
+ default Pageable getPageable() {
+ return PageRequest.of(getNumber(), getSize(), getSort());
+ }
+
/**
* Returns the {@link Pageable} to request the next {@link Slice}. Can be {@literal null} in case the current
* {@link Slice} is already the last one. Clients should check {@link #hasNext()} before calling this method to make
diff --git a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java
index a962e86ec0..749b80af5b 100644
--- a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java
+++ b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java
@@ -89,7 +89,7 @@ public abstract class AbstractMappingContext> initialEntitySet = new HashSet<>();
private boolean strict = false;
- private SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder();
+ private SimpleTypeHolder simpleTypeHolder = SimpleTypeHolder.DEFAULT;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock read = lock.readLock();
diff --git a/src/main/java/org/springframework/data/mapping/model/SimpleTypeHolder.java b/src/main/java/org/springframework/data/mapping/model/SimpleTypeHolder.java
index 3437d28d5d..5a27b00248 100644
--- a/src/main/java/org/springframework/data/mapping/model/SimpleTypeHolder.java
+++ b/src/main/java/org/springframework/data/mapping/model/SimpleTypeHolder.java
@@ -32,39 +32,43 @@
*/
public class SimpleTypeHolder {
- private static final Set> DEFAULTS = new HashSet<>();
-
- static {
- DEFAULTS.add(boolean.class);
- DEFAULTS.add(boolean[].class);
- DEFAULTS.add(long.class);
- DEFAULTS.add(long[].class);
- DEFAULTS.add(short.class);
- DEFAULTS.add(short[].class);
- DEFAULTS.add(int.class);
- DEFAULTS.add(int[].class);
- DEFAULTS.add(byte.class);
- DEFAULTS.add(byte[].class);
- DEFAULTS.add(float.class);
- DEFAULTS.add(float[].class);
- DEFAULTS.add(double.class);
- DEFAULTS.add(double[].class);
- DEFAULTS.add(char.class);
- DEFAULTS.add(char[].class);
- DEFAULTS.add(Boolean.class);
- DEFAULTS.add(Long.class);
- DEFAULTS.add(Short.class);
- DEFAULTS.add(Integer.class);
- DEFAULTS.add(Byte.class);
- DEFAULTS.add(Float.class);
- DEFAULTS.add(Double.class);
- DEFAULTS.add(Character.class);
- DEFAULTS.add(String.class);
- DEFAULTS.add(Date.class);
- DEFAULTS.add(Locale.class);
- DEFAULTS.add(Class.class);
- DEFAULTS.add(Enum.class);
- }
+ private static final Set> DEFAULTS = new HashSet>() {
+
+ private static final long serialVersionUID = -1738594126505221500L;
+
+ {
+ add(boolean.class);
+ add(boolean[].class);
+ add(long.class);
+ add(long[].class);
+ add(short.class);
+ add(short[].class);
+ add(int.class);
+ add(int[].class);
+ add(byte.class);
+ add(byte[].class);
+ add(float.class);
+ add(float[].class);
+ add(double.class);
+ add(double[].class);
+ add(char.class);
+ add(char[].class);
+ add(Boolean.class);
+ add(Long.class);
+ add(Short.class);
+ add(Integer.class);
+ add(Byte.class);
+ add(Float.class);
+ add(Double.class);
+ add(Character.class);
+ add(String.class);
+ add(Date.class);
+ add(Locale.class);
+ add(Class.class);
+ add(Enum.class);
+ }
+ };
+ public static final SimpleTypeHolder DEFAULT = new SimpleTypeHolder();
private final Set> simpleTypes;
@@ -73,8 +77,7 @@ public class SimpleTypeHolder {
*
* @see #SimpleTypeHolder(Set, boolean)
*/
- @SuppressWarnings("unchecked")
- public SimpleTypeHolder() {
+ protected SimpleTypeHolder() {
this(Collections.emptySet(), true);
}
@@ -128,13 +131,8 @@ public boolean isSimpleType(Class> type) {
return true;
}
- for (Class> clazz : simpleTypes) {
- if (clazz.isAssignableFrom(type)) {
- simpleTypes.add(type);
- return true;
- }
- }
-
- return false;
+ return simpleTypes.stream()//
+ .filter(it -> it.isAssignableFrom(type))//
+ .peek(it -> simpleTypes.add(type)).findFirst().isPresent();
}
}
diff --git a/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java b/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java
index 25c4642fe7..3ac0b833ba 100644
--- a/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java
+++ b/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Page;
@@ -49,7 +50,7 @@
public class PagedResourcesAssembler implements ResourceAssembler, PagedResources>> {
private final HateoasPageableHandlerMethodArgumentResolver pageableResolver;
- private final UriComponents baseUri;
+ private final Optional baseUri;
private final EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
private boolean forceFirstAndLastRels = false;
@@ -65,7 +66,7 @@ public class PagedResourcesAssembler implements ResourceAssembler, Pa
public PagedResourcesAssembler(HateoasPageableHandlerMethodArgumentResolver resolver, UriComponents baseUri) {
this.pageableResolver = resolver == null ? new HateoasPageableHandlerMethodArgumentResolver() : resolver;
- this.baseUri = baseUri;
+ this.baseUri = Optional.ofNullable(baseUri);
}
/**
@@ -87,7 +88,7 @@ public void setForceFirstAndLastRels(boolean forceFirstAndLastRels) {
*/
@Override
public PagedResources> toResource(Page entity) {
- return toResource(entity, new SimplePagedResourceAssembler<>());
+ return toResource(entity, it -> new Resource<>(it));
}
/**
@@ -100,7 +101,7 @@ public PagedResources> toResource(Page entity) {
* @return
*/
public PagedResources> toResource(Page page, Link selfLink) {
- return toResource(page, new SimplePagedResourceAssembler<>(), selfLink);
+ return toResource(page, it -> new Resource<>(it), selfLink);
}
/**
@@ -112,7 +113,7 @@ public PagedResources> toResource(Page page, Link selfLink) {
* @return
*/
public PagedResources toResource(Page page, ResourceAssembler assembler) {
- return createResource(page, assembler, null);
+ return createResource(page, assembler, Optional.empty());
}
/**
@@ -129,7 +130,20 @@ public PagedResources toResource(Page page, Re
Link link) {
Assert.notNull(link, "Link must not be null!");
- return createResource(page, assembler, link);
+
+ return createResource(page, assembler, Optional.of(link));
+ }
+
+ /**
+ * Creates a {@link PagedResources} with an empt collection {@link EmbeddedWrapper} for the given domain type.
+ *
+ * @param page must not be {@literal null}, content must be empty.
+ * @param type must not be {@literal null}.
+ * @return
+ * @since 2.0
+ */
+ public PagedResources> toEmptyResource(Page> page, Class> type) {
+ return toEmptyResource(page, type, Optional.empty());
}
/**
@@ -137,15 +151,20 @@ public PagedResources toResource(Page page, Re
*
* @param page must not be {@literal null}, content must be empty.
* @param type must not be {@literal null}.
- * @param link can be {@literal null}.
+ * @param link must not be {@literal null}.
* @return
* @since 1.11
*/
public PagedResources> toEmptyResource(Page> page, Class> type, Link link) {
+ return toEmptyResource(page, type, Optional.of(link));
+ }
+
+ private PagedResources> toEmptyResource(Page> page, Class> type, Optional link) {
Assert.notNull(page, "Page must must not be null!");
Assert.isTrue(!page.hasContent(), "Page must not have any content!");
Assert.notNull(type, "Type must not be null!");
+ Assert.notNull(link, "Link must not be null!");
PageMetadata metadata = asPageMetadata(page);
@@ -155,21 +174,6 @@ public PagedResources> toEmptyResource(Page> page, Class> type, Link link)
return addPaginationLinks(new PagedResources<>(embedded, metadata), page, link);
}
- /**
- * Adds the pagination parameters for all parameters not already present in the given {@link Link}.
- *
- * @param link must not be {@literal null}.
- * @return
- * @deprecated this method will be removed in 1.11 as no Spring Data module actually calls it. Other clients calling
- * it should stop doing so as {@link Link}s used for pagination shouldn't be templated in the first place.
- */
- @Deprecated
- public Link appendPaginationParameterTemplates(Link link) {
-
- Assert.notNull(link, "Link must not be null!");
- return createLink(new UriTemplate(link.getHref()), null, link.getRel());
- }
-
/**
* Creates the {@link PagedResources} to be equipped with pagination links downstream.
*
@@ -189,7 +193,7 @@ protected PagedResources createPagedResource(L
}
private PagedResources createResource(Page page,
- ResourceAssembler assembler, Link link) {
+ ResourceAssembler assembler, Optional link) {
Assert.notNull(page, "Page must not be null!");
Assert.notNull(assembler, "ResourceAssembler must not be null!");
@@ -205,7 +209,7 @@ private PagedResources createResource(Page
return addPaginationLinks(resource, page, link);
}
- private PagedResources addPaginationLinks(PagedResources resources, Page> page, Link link) {
+ private PagedResources addPaginationLinks(PagedResources resources, Page> page, Optional link) {
UriTemplate base = getUriTemplate(link);
@@ -219,7 +223,10 @@ private PagedResources addPaginationLinks(PagedResources resources, Pa
resources.add(createLink(base, page.previousPageable(), Link.REL_PREVIOUS));
}
- resources.add(createLink(base, null, Link.REL_SELF));
+ Link selfLink = link.map(it -> it.withSelfRel())//
+ .orElseGet(() -> createLink(base, page.getPageable(), Link.REL_SELF));
+
+ resources.add(selfLink);
if (page.hasNext()) {
resources.add(createLink(base, page.nextPageable(), Link.REL_NEXT));
@@ -241,12 +248,8 @@ private PagedResources addPaginationLinks(PagedResources resources, Pa
*
* @return
*/
- private UriTemplate getUriTemplate(Link baseLink) {
-
- String href = baseLink != null ? baseLink.getHref()
- : baseUri == null ? ServletUriComponentsBuilder.fromCurrentRequest().build().toString() : baseUri.toString();
-
- return new UriTemplate(href);
+ private UriTemplate getUriTemplate(Optional baseLink) {
+ return new UriTemplate(baseLink.map(Link::getHref).orElseGet(this::baseUriOrCurrentRequest));
}
/**
@@ -289,11 +292,11 @@ private static PageMetadata asPageMetadata(Page page) {
return new PageMetadata(page.getSize(), page.getNumber(), page.getTotalElements(), page.getTotalPages());
}
- private static class SimplePagedResourceAssembler implements ResourceAssembler> {
+ private String baseUriOrCurrentRequest() {
+ return baseUri.map(Object::toString).orElseGet(PagedResourcesAssembler::currentRequest);
+ }
- @Override
- public Resource toResource(T entity) {
- return new Resource<>(entity);
- }
+ private static String currentRequest() {
+ return ServletUriComponentsBuilder.fromCurrentRequest().build().toString();
}
}
diff --git a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java
new file mode 100644
index 0000000000..371824cb06
--- /dev/null
+++ b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2011-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.convert;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Locale;
+
+import org.joda.time.DateTime;
+import org.junit.Test;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+import org.springframework.core.convert.support.DefaultConversionService;
+import org.springframework.core.convert.support.GenericConversionService;
+import org.springframework.data.convert.CustomConversions.StoreConversions;
+import org.threeten.bp.LocalDateTime;
+
+/**
+ * Unit tests for {@link MongoCustomConversions}.
+ *
+ * @author Oliver Gierke
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public class CustomConversionsUnitTests {
+
+ @Test // DATACMNS-1035
+ public void findsBasicReadAndWriteConversions() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(FormatToStringConverter.INSTANCE, StringToFormatConverter.INSTANCE));
+
+ assertThat(conversions.getCustomWriteTarget(Format.class)).hasValue(String.class);
+ assertThat(conversions.getCustomWriteTarget(String.class)).isNotPresent();
+
+ assertThat(conversions.hasCustomReadTarget(String.class, Format.class)).isTrue();
+ assertThat(conversions.hasCustomReadTarget(String.class, Locale.class)).isFalse();
+ }
+
+ @Test // DATACMNS-1035
+ public void considersSubtypesCorrectly() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(NumberToStringConverter.INSTANCE, StringToNumberConverter.INSTANCE));
+
+ assertThat(conversions.getCustomWriteTarget(Long.class)).hasValue(String.class);
+ assertThat(conversions.hasCustomReadTarget(String.class, Long.class)).isTrue();
+ }
+
+ @Test // DATACMNS-1035
+ public void populatesConversionServiceCorrectly() {
+
+ GenericConversionService conversionService = new DefaultConversionService();
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(StringToFormatConverter.INSTANCE));
+ conversions.registerConvertersIn(conversionService);
+
+ assertThat(conversionService.canConvert(String.class, Format.class), is(true));
+ }
+
+ @Test // DATAMONGO-259, DATACMNS-1035
+ public void doesNotConsiderTypeSimpleIfOnlyReadConverterIsRegistered() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(StringToFormatConverter.INSTANCE));
+ assertThat(conversions.isSimpleType(Format.class), is(false));
+ }
+
+ @Test // DATAMONGO-298, DATACMNS-1035
+ public void discoversConvertersForSubtypesOfMongoTypes() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(StringToIntegerConverter.INSTANCE));
+ assertThat(conversions.hasCustomReadTarget(String.class, Integer.class), is(true));
+ assertThat(conversions.hasCustomWriteTarget(String.class, Integer.class), is(true));
+ }
+
+ @Test // DATAMONGO-795, DATACMNS-1035
+ public void favorsCustomConverterForIndeterminedTargetType() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(DateTimeToStringConverter.INSTANCE));
+ assertThat(conversions.getCustomWriteTarget(DateTime.class)).hasValue(String.class);
+ }
+
+ @Test // DATAMONGO-881, DATACMNS-1035
+ public void customConverterOverridesDefault() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(CustomDateTimeConverter.INSTANCE));
+ GenericConversionService conversionService = new DefaultConversionService();
+ conversions.registerConvertersIn(conversionService);
+
+ assertThat(conversionService.convert(new DateTime(), Date.class)).isEqualTo(new Date(0));
+ }
+
+ @Test // DATAMONGO-1001, DATACMNS-1035
+ public void shouldSelectPropertCustomWriteTargetForCglibProxiedType() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(FormatToStringConverter.INSTANCE));
+ assertThat(conversions.getCustomWriteTarget(createProxyTypeFor(Format.class))).hasValue(String.class);
+ }
+
+ @Test // DATAMONGO-1001, DATACMNS-1035
+ public void shouldSelectPropertCustomReadTargetForCglibProxiedType() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE,
+ Arrays.asList(CustomObjectToStringConverter.INSTANCE));
+ assertThat(conversions.hasCustomReadTarget(createProxyTypeFor(Object.class), String.class)).isTrue();
+ }
+
+ @Test // DATAMONGO-1131, DATACMNS-1035
+ public void registersConvertersForJsr310() {
+
+ CustomConversions customConversions = new CustomConversions(StoreConversions.NONE, Collections.emptyList());
+
+ assertThat(customConversions.hasCustomWriteTarget(java.time.LocalDateTime.class)).isTrue();
+ }
+
+ @Test // DATAMONGO-1131, DATACMNS-1035
+ public void registersConvertersForThreeTenBackPort() {
+
+ CustomConversions customConversions = new CustomConversions(StoreConversions.NONE, Collections.emptyList());
+
+ assertThat(customConversions.hasCustomWriteTarget(LocalDateTime.class)).isTrue();
+ }
+
+ @Test // DATAMONGO-1302, DATACMNS-1035
+ public void registersConverterFactoryCorrectly() {
+
+ CustomConversions customConversions = new CustomConversions(StoreConversions.NONE,
+ Collections.singletonList(new FormatConverterFactory()));
+
+ assertThat(customConversions.getCustomWriteTarget(String.class, SimpleDateFormat.class)).isPresent();
+ }
+
+ private static Class> createProxyTypeFor(Class> type) {
+
+ ProxyFactory factory = new ProxyFactory();
+ factory.setProxyTargetClass(true);
+ factory.setTargetClass(type);
+
+ return factory.getProxy().getClass();
+ }
+
+ enum FormatToStringConverter implements Converter {
+
+ INSTANCE;
+
+ public String convert(Format source) {
+ return source.toString();
+ }
+ }
+
+ enum StringToFormatConverter implements Converter {
+
+ INSTANCE;
+
+ public Format convert(String source) {
+ return DateFormat.getInstance();
+ }
+ }
+
+ enum NumberToStringConverter implements Converter {
+
+ INSTANCE;
+
+ public String convert(Number source) {
+ return source.toString();
+ }
+ }
+
+ enum StringToNumberConverter implements Converter {
+
+ INSTANCE;
+
+ public Number convert(String source) {
+ return 0L;
+ }
+ }
+
+ enum StringToIntegerConverter implements Converter {
+
+ INSTANCE;
+
+ public Integer convert(String source) {
+ return 0;
+ }
+ }
+
+ enum DateTimeToStringConverter implements Converter {
+
+ INSTANCE;
+
+ @Override
+ public String convert(DateTime source) {
+ return "";
+ }
+ }
+
+ enum CustomDateTimeConverter implements Converter {
+
+ INSTANCE;
+
+ @Override
+ public Date convert(DateTime source) {
+ return new Date(0);
+ }
+ }
+
+ enum CustomObjectToStringConverter implements Converter {
+
+ INSTANCE;
+
+ @Override
+ public String convert(Object source) {
+ return source != null ? source.toString() : null;
+ }
+
+ }
+
+ @WritingConverter
+ static class FormatConverterFactory implements ConverterFactory {
+
+ @Override
+ public Converter getConverter(Class targetType) {
+ return new StringToFormat(targetType);
+ }
+
+ private static final class StringToFormat implements Converter {
+
+ private final Class targetType;
+
+ public StringToFormat(Class targetType) {
+ this.targetType = targetType;
+ }
+
+ @Override
+ public T convert(String source) {
+
+ if (source.length() == 0) {
+ return null;
+ }
+
+ try {
+ return targetType.newInstance();
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/springframework/data/mapping/SimpleTypeHolderUnitTests.java b/src/test/java/org/springframework/data/mapping/SimpleTypeHolderUnitTests.java
index 0c0cdaefa9..90d1ed770c 100755
--- a/src/test/java/org/springframework/data/mapping/SimpleTypeHolderUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/SimpleTypeHolderUnitTests.java
@@ -44,14 +44,14 @@ public void rejectsNullOriginal() {
@Test(expected = IllegalArgumentException.class) // DATACMNS-31
public void rejectsNullTypeForIsSimpleTypeCall() {
- SimpleTypeHolder holder = new SimpleTypeHolder();
+ SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
holder.isSimpleType(null);
}
@Test
public void addsDefaultTypes() {
- SimpleTypeHolder holder = new SimpleTypeHolder();
+ SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
assertThat(holder.isSimpleType(String.class)).isTrue();
}
@@ -87,28 +87,28 @@ public void createsHolderFromAnotherOneCorrectly() {
@Test
public void considersObjectToBeSimpleType() {
- SimpleTypeHolder holder = new SimpleTypeHolder();
+ SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
assertThat(holder.isSimpleType(Object.class)).isTrue();
}
@Test
public void considersSimpleEnumAsSimple() {
- SimpleTypeHolder holder = new SimpleTypeHolder();
+ SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
assertThat(holder.isSimpleType(SimpleEnum.FOO.getClass())).isTrue();
}
@Test
public void considersComplexEnumAsSimple() {
- SimpleTypeHolder holder = new SimpleTypeHolder();
+ SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
assertThat(holder.isSimpleType(ComplexEnum.FOO.getClass())).isTrue();
}
@Test // DATACMNS-1006
public void considersJavaLangTypesSimple() {
- SimpleTypeHolder holder = new SimpleTypeHolder();
+ SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
assertThat(holder.isSimpleType(Type.class)).isTrue();
}
diff --git a/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java b/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java
index 504eadb344..57014caa33 100755
--- a/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java
@@ -48,7 +48,7 @@
*/
public class AbstractMappingContextUnitTests {
- final SimpleTypeHolder holder = new SimpleTypeHolder();
+ final SimpleTypeHolder holder = SimpleTypeHolder.DEFAULT;
SampleMappingContext context;
@Before
@@ -153,7 +153,9 @@ public void returnsEntityForComponentType() {
PersistentEntity entity = mappingContext
.getRequiredPersistentEntity(Sample.class);
- assertThat(entity.getPersistentProperty("persons")).hasValueSatisfying(it -> assertThat(mappingContext.getPersistentEntity(it)).hasValueSatisfying(inner -> assertThat(inner.getType()).isEqualTo(Person.class)));
+ assertThat(entity.getPersistentProperty("persons"))
+ .hasValueSatisfying(it -> assertThat(mappingContext.getPersistentEntity(it))
+ .hasValueSatisfying(inner -> assertThat(inner.getType()).isEqualTo(Person.class)));
}
@Test // DATACMNS-380
diff --git a/src/test/java/org/springframework/data/web/PagedResourcesAssemblerArgumentResolverUnitTests.java b/src/test/java/org/springframework/data/web/PagedResourcesAssemblerArgumentResolverUnitTests.java
index b443d22a6d..65d26c4899 100755
--- a/src/test/java/org/springframework/data/web/PagedResourcesAssemblerArgumentResolverUnitTests.java
+++ b/src/test/java/org/springframework/data/web/PagedResourcesAssemblerArgumentResolverUnitTests.java
@@ -18,6 +18,7 @@
import static org.assertj.core.api.Assertions.*;
import java.lang.reflect.Method;
+import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
@@ -130,9 +131,12 @@ public java.lang.Class> getDeclaringClass() {
Object result = resolver.resolveArgument(methodParameter, null, null, null);
assertThat(result).isInstanceOf(PagedResourcesAssembler.class);
- UriComponents uriComponents = (UriComponents) ReflectionTestUtils.getField(result, "baseUri");
- assertThat(uriComponents.getPath()).isEqualTo("/foo/mapping");
+ Optional uriComponents = (Optional) ReflectionTestUtils.getField(result, "baseUri");
+
+ assertThat(uriComponents).hasValueSatisfying(it -> {
+ assertThat(it.getPath()).isEqualTo("/foo/mapping");
+ });
}
private void assertSelectsParameter(Method method, int expectedIndex) throws Exception {
diff --git a/src/test/java/org/springframework/data/web/PagedResourcesAssemblerUnitTests.java b/src/test/java/org/springframework/data/web/PagedResourcesAssemblerUnitTests.java
index 9274ce45bf..b91a1349c4 100755
--- a/src/test/java/org/springframework/data/web/PagedResourcesAssemblerUnitTests.java
+++ b/src/test/java/org/springframework/data/web/PagedResourcesAssemblerUnitTests.java
@@ -18,7 +18,6 @@
import static org.assertj.core.api.Assertions.*;
import java.net.URI;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -111,7 +110,7 @@ public void usesCustomLinkProvided() {
PagedResources> resources = assembler.toResource(createPage(1), link);
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref()).startsWith(link.getHref());
- assertThat(resources.getLink(Link.REL_SELF)).isNotNull();
+ assertThat(resources.getLink(Link.REL_SELF)).isEqualTo(link.withSelfRel());
assertThat(resources.getLink(Link.REL_NEXT).getHref()).startsWith(link.getHref());
}
@@ -131,8 +130,7 @@ public void createsACanonicalLinkWithoutTemplateParameters() {
PagedResources> resources = assembler.toResource(createPage(1));
- Link selfLink = resources.getLink(Link.REL_SELF);
- assertThat(selfLink.getHref()).endsWith("localhost");
+ assertThat(resources.getLink(Link.REL_SELF).getHref()).doesNotContain("{").doesNotContain("}");
}
@Test // DATACMNS-418
@@ -170,7 +168,7 @@ public void generatedLinksShouldNotBeTemplated() {
PagedResources> resources = assembler.toResource(createPage(1));
- assertThat(resources.getLink(Link.REL_SELF).getHref()).endsWith("localhost");
+ assertThat(resources.getLink(Link.REL_SELF).getHref()).doesNotContain("{").doesNotContain("}");
assertThat(resources.getLink(Link.REL_NEXT).getHref()).endsWith("?page=2&size=1");
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref()).endsWith("?page=0&size=1");
}
@@ -178,7 +176,7 @@ public void generatedLinksShouldNotBeTemplated() {
@Test // DATACMNS-699
public void generatesEmptyPagedResourceWithEmbeddedWrapper() {
- PagedResources> result = assembler.toEmptyResource(EMPTY_PAGE, Person.class, null);
+ PagedResources> result = assembler.toEmptyResource(EMPTY_PAGE, Person.class);
Collection> content = result.getContent();
assertThat(content).hasSize(1);
@@ -190,12 +188,12 @@ public void generatesEmptyPagedResourceWithEmbeddedWrapper() {
@Test(expected = IllegalArgumentException.class) // DATACMNS-699
public void emptyPageCreatorRejectsPageWithContent() {
- assembler.toEmptyResource(createPage(1), Person.class, null);
+ assembler.toEmptyResource(createPage(1), Person.class);
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-699
public void emptyPageCreatorRejectsNullType() {
- assembler.toEmptyResource(EMPTY_PAGE, null, null);
+ assembler.toEmptyResource(EMPTY_PAGE, null);
}
@Test // DATACMNS-701
@@ -246,6 +244,14 @@ public void usesCustomPagedResources() {
assertThat(assembler.toResource(EMPTY_PAGE)).isInstanceOf(CustomPagedResources.class);
}
+ @Test // DATACMNS-1042
+ public void selfLinkContainsCoordinatesForCurrentPage() {
+
+ PagedResources> resource = assembler.toResource(createPage(0));
+
+ assertThat(resource.getLink(Link.REL_SELF).getHref()).endsWith("?page=0&size=1");
+ }
+
private static Page createPage(int index) {
Pageable request = PageRequest.of(index, 1);