From ed0d30b4d6bba15f686bf554adda01d5b798ad78 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Mon, 1 Jul 2019 14:21:22 +0200 Subject: [PATCH] DATACMNS-1551 - Polishing. Method order in SortHandlerMethodArgumentResolver. --- .../SortHandlerMethodArgumentResolver.java | 249 +++++++++--------- 1 file changed, 124 insertions(+), 125 deletions(-) diff --git a/src/main/java/org/springframework/data/web/SortHandlerMethodArgumentResolver.java b/src/main/java/org/springframework/data/web/SortHandlerMethodArgumentResolver.java index 797fa1746..7bafca892 100644 --- a/src/main/java/org/springframework/data/web/SortHandlerMethodArgumentResolver.java +++ b/src/main/java/org/springframework/data/web/SortHandlerMethodArgumentResolver.java @@ -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. + *

+ * 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 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 = 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 toOrder(String property, Optional 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 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 = 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 toOrder(String property, Optional 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. - *

- * 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; - } }