Skip to content

Commit 34507ec

Browse files
committed
DATACMNS-1450 - Introduced type-safe Sort API.
We now expose a TypedSort that can use method handles to define properties to sort by. Sort.sort(Person.class).by(Person::getName).ascending(); Related tickets: DATACMNS-1449.
1 parent 34d95fb commit 34507ec

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

src/main/java/org/springframework/data/domain/Sort.java

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
import java.util.List;
2424
import java.util.Locale;
2525
import java.util.Optional;
26+
import java.util.function.Function;
2627
import java.util.stream.Collectors;
2728

29+
import org.springframework.data.util.MethodInvocationRecorder;
30+
import org.springframework.data.util.MethodInvocationRecorder.Recorded;
2831
import org.springframework.data.util.Streamable;
2932
import org.springframework.lang.Nullable;
3033
import org.springframework.util.Assert;
@@ -169,6 +172,17 @@ public static Sort by(Direction direction, String... properties) {
169172
.collect(Collectors.toList()));
170173
}
171174

175+
/**
176+
* Creates a new {@link TypedSort} for the given type.
177+
*
178+
* @param type must not be {@literal null}.
179+
* @return
180+
* @since 2.2
181+
*/
182+
public static <T> TypedSort<T> sort(Class<T> type) {
183+
return new TypedSort<>(type);
184+
}
185+
172186
/**
173187
* Returns a {@link Sort} instances representing no sorting setup at all.
174188
*
@@ -692,4 +706,83 @@ public String toString() {
692706
return result;
693707
}
694708
}
709+
710+
/**
711+
* Extension of Sort to use method handles to define properties to sort by.
712+
*
713+
* @author Oliver Gierke
714+
* @since 2.2
715+
* @soundtrack The Intersphere - Linger (The Grand Delusion)
716+
*/
717+
public static class TypedSort<T> extends Sort {
718+
719+
private static final long serialVersionUID = -3550403511206745880L;
720+
721+
private final Recorded<T> recorded;
722+
723+
private TypedSort(Class<T> type) {
724+
this(MethodInvocationRecorder.forProxyOf(type));
725+
}
726+
727+
private TypedSort(Recorded<T> recorded) {
728+
729+
super(new Order[0]);
730+
this.recorded = recorded;
731+
}
732+
733+
public <S> TypedSort<S> by(Function<T, S> property) {
734+
return new TypedSort<>(recorded.record(property));
735+
}
736+
737+
public <S> TypedSort<S> by(Recorded.ToCollectionConverter<T, S> collectionProperty) {
738+
return new TypedSort<>(recorded.record(collectionProperty));
739+
}
740+
741+
public <S> TypedSort<S> by(Recorded.ToMapConverter<T, S> mapProperty) {
742+
return new TypedSort<>(recorded.record(mapProperty));
743+
}
744+
745+
public Sort ascending() {
746+
return withDirection(Sort::ascending);
747+
}
748+
749+
public Sort descending() {
750+
return withDirection(Sort::descending);
751+
}
752+
753+
private Sort withDirection(Function<Sort, Sort> direction) {
754+
755+
return recorded.getPropertyPath() //
756+
.map(Sort::by) //
757+
.map(direction) //
758+
.orElseGet(Sort::unsorted);
759+
}
760+
761+
/*
762+
* (non-Javadoc)
763+
* @see org.springframework.data.domain.Sort#iterator()
764+
*/
765+
@Override
766+
public Iterator<Order> iterator() {
767+
768+
return recorded.getPropertyPath() //
769+
.map(Order::by) //
770+
.map(Collections::singleton) //
771+
.orElseGet(Collections::emptySet).iterator();
772+
773+
}
774+
775+
/*
776+
* (non-Javadoc)
777+
* @see org.springframework.data.domain.Sort#toString()
778+
*/
779+
@Override
780+
public String toString() {
781+
782+
return recorded.getPropertyPath() //
783+
.map(Sort::by) //
784+
.orElseGet(Sort::unsorted) //
785+
.toString();
786+
}
787+
}
695788
}

src/test/java/org/springframework/data/domain/SortUnitTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
import static org.assertj.core.api.Assertions.*;
1919
import static org.springframework.data.domain.Sort.NullHandling.*;
2020

21+
import lombok.Getter;
22+
23+
import java.util.Collection;
24+
2125
import org.junit.Test;
2226
import org.springframework.data.domain.Sort.Direction;
2327
import org.springframework.data.domain.Sort.Order;
@@ -172,4 +176,28 @@ public void preventsNullDirection() {
172176
.isThrownBy(() -> Sort.by((Direction) null, "foo"))//
173177
.withMessageContaining("Direction");
174178
}
179+
180+
@Test // DATACMNS-1450
181+
public void translatesTypedSortCorrectly() {
182+
183+
assertThat(Sort.sort(Sample.class).by(Sample::getNested).by(Nested::getFirstname)) //
184+
.containsExactly(Order.by("nested.firstname"));
185+
186+
assertThat(Sort.sort(Sample.class).by((Sample it) -> it.getNested().getFirstname())) //
187+
.containsExactly(Order.by("nested.firstname"));
188+
189+
assertThat(Sort.sort(Sample.class).by(Sample::getNesteds).by(Nested::getFirstname)) //
190+
.containsExactly(Order.by("nesteds.firstname"));
191+
}
192+
193+
@Getter
194+
static class Sample {
195+
Nested nested;
196+
Collection<Nested> nesteds;
197+
}
198+
199+
@Getter
200+
static class Nested {
201+
String firstname;
202+
}
175203
}

0 commit comments

Comments
 (0)