DATACMNS-1551 - Polishing.

Method order in SortHandlerMethodArgumentResolver.
This commit is contained in:
Oliver Drotbohm
2019-07-01 14:21:22 +02:00
parent 4c1aed27d6
commit ed0d30b4d6

View File

@@ -95,6 +95,19 @@ public class SortHandlerMethodArgumentResolver implements SortArgumentResolver {
this.qualifierDelimiter = qualifierDelimiter == null ? DEFAULT_QUALIFIER_DELIMITER : qualifierDelimiter;
}
/**
* Configures the {@link Sort} to be used as fallback in case no {@link SortDefault} or {@link SortDefaults} (the
* latter only supported in legacy mode) can be found at the method parameter to be resolved.
* <p>
* If you set this to {@literal null}, be aware that you controller methods will get {@literal null} handed into them
* in case no {@link Sort} data can be found in the request.
*
* @param fallbackSort the {@link Sort} to be used as general fallback.
*/
public void setFallbackSort(Sort fallbackSort) {
this.fallbackSort = fallbackSort;
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
@@ -127,63 +140,6 @@ public class SortHandlerMethodArgumentResolver implements SortArgumentResolver {
return parseParameterIntoSort(directionParameter, propertyDelimiter);
}
/**
* Reads the default {@link Sort} to be used from the given {@link MethodParameter}. Rejects the parameter if both an
* {@link SortDefaults} and {@link SortDefault} annotation is found as we cannot build a reliable {@link Sort}
* instance then (property ordering).
*
* @param parameter will never be {@literal null}.
* @return the default {@link Sort} instance derived from the parameter annotations or the configured fallback-sort
* {@link #setFallbackSort(Sort)}.
*/
private Sort getDefaultFromAnnotationOrFallback(MethodParameter parameter) {
SortDefaults annotatedDefaults = parameter.getParameterAnnotation(SortDefaults.class);
SortDefault annotatedDefault = parameter.getParameterAnnotation(SortDefault.class);
if (annotatedDefault != null && annotatedDefaults != null) {
throw new IllegalArgumentException(
String.format("Cannot use both @%s and @%s on parameter %s! Move %s into %s to define sorting order!",
SORT_DEFAULTS_NAME, SORT_DEFAULT_NAME, parameter.toString(), SORT_DEFAULT_NAME, SORT_DEFAULTS_NAME));
}
if (annotatedDefault != null) {
return appendOrCreateSortTo(annotatedDefault, Sort.unsorted());
}
if (annotatedDefaults != null) {
Sort sort = Sort.unsorted();
for (SortDefault currentAnnotatedDefault : annotatedDefaults.value()) {
sort = appendOrCreateSortTo(currentAnnotatedDefault, sort);
}
return sort;
}
return fallbackSort;
}
/**
* Creates a new {@link Sort} instance from the given {@link SortDefault} or appends it to the given {@link Sort}
* instance if it's not {@literal null}.
*
* @param sortDefault
* @param sortOrNull
* @return
*/
private Sort appendOrCreateSortTo(SortDefault sortDefault, Sort sortOrNull) {
String[] fields = SpringDataAnnotationUtils.getSpecificPropertyOrDefaultFromValue(sortDefault, "sort");
if (fields.length == 0) {
return Sort.unsorted();
}
return sortOrNull.and(Sort.by(sortDefault.direction(), fields));
}
/**
* Returns the sort parameter to be looked up from the request. Potentially applies qualifiers to it.
*
@@ -203,61 +159,6 @@ public class SortHandlerMethodArgumentResolver implements SortArgumentResolver {
return builder.append(sortParameter).toString();
}
/**
* Parses the given sort expressions into a {@link Sort} instance. The implementation expects the sources to be a
* concatenation of Strings using the given delimiter. If the last element can be parsed into a {@link Direction} it's
* considered a {@link Direction} and a simple property otherwise.
*
* @param source will never be {@literal null}.
* @param delimiter the delimiter to be used to split up the source elements, will never be {@literal null}.
* @return
*/
Sort parseParameterIntoSort(String[] source, String delimiter) {
List<Order> allOrders = new ArrayList<>();
for (String part : source) {
if (part == null) {
continue;
}
String[] elements = Arrays.stream(part.split(delimiter)) //
.filter(SortHandlerMethodArgumentResolver::notOnlyDots) //
.toArray(String[]::new);
Optional<Direction> direction = elements.length == 0 ? Optional.empty()
: Direction.fromOptionalString(elements[elements.length - 1]);
int lastIndex = direction.map(it -> elements.length - 1).orElseGet(() -> elements.length);
for (int i = 0; i < lastIndex; i++) {
toOrder(elements[i], direction).ifPresent(allOrders::add);
}
}
return allOrders.isEmpty() ? Sort.unsorted() : Sort.by(allOrders);
}
/**
* Returns whether the given source {@link String} consists of dots only.
*
* @param source must not be {@literal null}.
* @return
*/
private static boolean notOnlyDots(String source) {
return StringUtils.hasText(source.replace(".", ""));
}
private static Optional<Order> toOrder(String property, Optional<Direction> direction) {
if (!StringUtils.hasText(property)) {
return Optional.empty();
}
return Optional.of(direction.map(it -> new Order(it, property)).orElseGet(() -> Order.by(property)));
}
/**
* Folds the given {@link Sort} instance into a {@link List} of sort expressions, accumulating {@link Order} instances
* of the same direction into a single expression if they are in order.
@@ -317,6 +218,117 @@ public class SortHandlerMethodArgumentResolver implements SortArgumentResolver {
return builder == null ? Collections.emptyList() : builder.dumpExpressionIfPresentInto(expressions);
}
/**
* Parses the given sort expressions into a {@link Sort} instance. The implementation expects the sources to be a
* concatenation of Strings using the given delimiter. If the last element can be parsed into a {@link Direction} it's
* considered a {@link Direction} and a simple property otherwise.
*
* @param source will never be {@literal null}.
* @param delimiter the delimiter to be used to split up the source elements, will never be {@literal null}.
* @return
*/
Sort parseParameterIntoSort(String[] source, String delimiter) {
List<Order> allOrders = new ArrayList<>();
for (String part : source) {
if (part == null) {
continue;
}
String[] elements = Arrays.stream(part.split(delimiter)) //
.filter(SortHandlerMethodArgumentResolver::notOnlyDots) //
.toArray(String[]::new);
Optional<Direction> direction = elements.length == 0 //
? Optional.empty() //
: Direction.fromOptionalString(elements[elements.length - 1]);
int lastIndex = direction.map(it -> elements.length - 1) //
.orElseGet(() -> elements.length);
for (int i = 0; i < lastIndex; i++) {
toOrder(elements[i], direction).ifPresent(allOrders::add);
}
}
return allOrders.isEmpty() ? Sort.unsorted() : Sort.by(allOrders);
}
/**
* Reads the default {@link Sort} to be used from the given {@link MethodParameter}. Rejects the parameter if both an
* {@link SortDefaults} and {@link SortDefault} annotation is found as we cannot build a reliable {@link Sort}
* instance then (property ordering).
*
* @param parameter will never be {@literal null}.
* @return the default {@link Sort} instance derived from the parameter annotations or the configured fallback-sort
* {@link #setFallbackSort(Sort)}.
*/
private Sort getDefaultFromAnnotationOrFallback(MethodParameter parameter) {
SortDefaults annotatedDefaults = parameter.getParameterAnnotation(SortDefaults.class);
SortDefault annotatedDefault = parameter.getParameterAnnotation(SortDefault.class);
if (annotatedDefault != null && annotatedDefaults != null) {
throw new IllegalArgumentException(
String.format("Cannot use both @%s and @%s on parameter %s! Move %s into %s to define sorting order!",
SORT_DEFAULTS_NAME, SORT_DEFAULT_NAME, parameter.toString(), SORT_DEFAULT_NAME, SORT_DEFAULTS_NAME));
}
if (annotatedDefault != null) {
return appendOrCreateSortTo(annotatedDefault, Sort.unsorted());
}
if (annotatedDefaults != null) {
Sort sort = Sort.unsorted();
for (SortDefault currentAnnotatedDefault : annotatedDefaults.value()) {
sort = appendOrCreateSortTo(currentAnnotatedDefault, sort);
}
return sort;
}
return fallbackSort;
}
/**
* Creates a new {@link Sort} instance from the given {@link SortDefault} or appends it to the given {@link Sort}
* instance if it's not {@literal null}.
*
* @param sortDefault
* @param sortOrNull
* @return
*/
private Sort appendOrCreateSortTo(SortDefault sortDefault, Sort sortOrNull) {
String[] fields = SpringDataAnnotationUtils.getSpecificPropertyOrDefaultFromValue(sortDefault, "sort");
return fields.length == 0 //
? Sort.unsorted()
: sortOrNull.and(Sort.by(sortDefault.direction(), fields));
}
/**
* Returns whether the given source {@link String} consists of dots only.
*
* @param source must not be {@literal null}.
* @return
*/
private static boolean notOnlyDots(String source) {
return StringUtils.hasText(source.replace(".", ""));
}
private static Optional<Order> toOrder(String property, Optional<Direction> direction) {
return !StringUtils.hasText(property) //
? Optional.empty() //
: Optional.of(direction.map(it -> new Order(it, property)) //
.orElseGet(() -> Order.by(property)));
}
/**
* Helper to easily build request parameter expressions for {@link Sort} instances.
*
@@ -376,17 +388,4 @@ public class SortHandlerMethodArgumentResolver implements SortArgumentResolver {
return expressions;
}
}
/**
* Configures the {@link Sort} to be used as fallback in case no {@link SortDefault} or {@link SortDefaults} (the
* latter only supported in legacy mode) can be found at the method parameter to be resolved.
* <p>
* If you set this to {@literal null}, be aware that you controller methods will get {@literal null} handed into them
* in case no {@link Sort} data can be found in the request.
*
* @param fallbackSort the {@link Sort} to be used as general fallback.
*/
public void setFallbackSort(Sort fallbackSort) {
this.fallbackSort = fallbackSort;
}
}