Skip to content

Commit ed0d30b

Browse files
committed
DATACMNS-1551 - Polishing.
Method order in SortHandlerMethodArgumentResolver.
1 parent 4c1aed2 commit ed0d30b

File tree

1 file changed

+110
-111
lines changed

1 file changed

+110
-111
lines changed

src/main/java/org/springframework/data/web/SortHandlerMethodArgumentResolver.java

Lines changed: 110 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ public void setQualifierDelimiter(String qualifierDelimiter) {
9595
this.qualifierDelimiter = qualifierDelimiter == null ? DEFAULT_QUALIFIER_DELIMITER : qualifierDelimiter;
9696
}
9797

98+
/**
99+
* Configures the {@link Sort} to be used as fallback in case no {@link SortDefault} or {@link SortDefaults} (the
100+
* latter only supported in legacy mode) can be found at the method parameter to be resolved.
101+
* <p>
102+
* If you set this to {@literal null}, be aware that you controller methods will get {@literal null} handed into them
103+
* in case no {@link Sort} data can be found in the request.
104+
*
105+
* @param fallbackSort the {@link Sort} to be used as general fallback.
106+
*/
107+
public void setFallbackSort(Sort fallbackSort) {
108+
this.fallbackSort = fallbackSort;
109+
}
110+
98111
/*
99112
* (non-Javadoc)
100113
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
@@ -128,79 +141,81 @@ public Sort resolveArgument(MethodParameter parameter, @Nullable ModelAndViewCon
128141
}
129142

130143
/**
131-
* Reads the default {@link Sort} to be used from the given {@link MethodParameter}. Rejects the parameter if both an
132-
* {@link SortDefaults} and {@link SortDefault} annotation is found as we cannot build a reliable {@link Sort}
133-
* instance then (property ordering).
144+
* Returns the sort parameter to be looked up from the request. Potentially applies qualifiers to it.
134145
*
135-
* @param parameter will never be {@literal null}.
136-
* @return the default {@link Sort} instance derived from the parameter annotations or the configured fallback-sort
137-
* {@link #setFallbackSort(Sort)}.
146+
* @param parameter can be {@literal null}.
147+
* @return
138148
*/
139-
private Sort getDefaultFromAnnotationOrFallback(MethodParameter parameter) {
140-
141-
SortDefaults annotatedDefaults = parameter.getParameterAnnotation(SortDefaults.class);
142-
SortDefault annotatedDefault = parameter.getParameterAnnotation(SortDefault.class);
143-
144-
if (annotatedDefault != null && annotatedDefaults != null) {
145-
throw new IllegalArgumentException(
146-
String.format("Cannot use both @%s and @%s on parameter %s! Move %s into %s to define sorting order!",
147-
SORT_DEFAULTS_NAME, SORT_DEFAULT_NAME, parameter.toString(), SORT_DEFAULT_NAME, SORT_DEFAULTS_NAME));
148-
}
149-
150-
if (annotatedDefault != null) {
151-
return appendOrCreateSortTo(annotatedDefault, Sort.unsorted());
152-
}
153-
154-
if (annotatedDefaults != null) {
149+
protected String getSortParameter(@Nullable MethodParameter parameter) {
155150

156-
Sort sort = Sort.unsorted();
151+
StringBuilder builder = new StringBuilder();
157152

158-
for (SortDefault currentAnnotatedDefault : annotatedDefaults.value()) {
159-
sort = appendOrCreateSortTo(currentAnnotatedDefault, sort);
160-
}
153+
Qualifier qualifier = parameter != null ? parameter.getParameterAnnotation(Qualifier.class) : null;
161154

162-
return sort;
155+
if (qualifier != null) {
156+
builder.append(qualifier.value()).append(qualifierDelimiter);
163157
}
164158

165-
return fallbackSort;
159+
return builder.append(sortParameter).toString();
166160
}
167161

168162
/**
169-
* Creates a new {@link Sort} instance from the given {@link SortDefault} or appends it to the given {@link Sort}
170-
* instance if it's not {@literal null}.
163+
* Folds the given {@link Sort} instance into a {@link List} of sort expressions, accumulating {@link Order} instances
164+
* of the same direction into a single expression if they are in order.
171165
*
172-
* @param sortDefault
173-
* @param sortOrNull
166+
* @param sort must not be {@literal null}.
174167
* @return
175168
*/
176-
private Sort appendOrCreateSortTo(SortDefault sortDefault, Sort sortOrNull) {
169+
protected List<String> foldIntoExpressions(Sort sort) {
177170

178-
String[] fields = SpringDataAnnotationUtils.getSpecificPropertyOrDefaultFromValue(sortDefault, "sort");
171+
List<String> expressions = new ArrayList<>();
172+
ExpressionBuilder builder = null;
173+
174+
for (Order order : sort) {
179175

180-
if (fields.length == 0) {
181-
return Sort.unsorted();
176+
Direction direction = order.getDirection();
177+
178+
if (builder == null) {
179+
builder = new ExpressionBuilder(direction);
180+
} else if (!builder.hasSameDirectionAs(order)) {
181+
builder.dumpExpressionIfPresentInto(expressions);
182+
builder = new ExpressionBuilder(direction);
183+
}
184+
185+
builder.add(order.getProperty());
182186
}
183187

184-
return sortOrNull.and(Sort.by(sortDefault.direction(), fields));
188+
return builder == null ? Collections.emptyList() : builder.dumpExpressionIfPresentInto(expressions);
185189
}
186190

187191
/**
188-
* Returns the sort parameter to be looked up from the request. Potentially applies qualifiers to it.
192+
* Folds the given {@link Sort} instance into two expressions. The first being the property list, the second being the
193+
* direction.
189194
*
190-
* @param parameter can be {@literal null}.
195+
* @throws IllegalArgumentException if a {@link Sort} with multiple {@link Direction}s has been handed in.
196+
* @param sort must not be {@literal null}.
191197
* @return
192198
*/
193-
protected String getSortParameter(@Nullable MethodParameter parameter) {
199+
protected List<String> legacyFoldExpressions(Sort sort) {
194200

195-
StringBuilder builder = new StringBuilder();
201+
List<String> expressions = new ArrayList<>();
202+
ExpressionBuilder builder = null;
196203

197-
Qualifier qualifier = parameter != null ? parameter.getParameterAnnotation(Qualifier.class) : null;
204+
for (Order order : sort) {
198205

199-
if (qualifier != null) {
200-
builder.append(qualifier.value()).append(qualifierDelimiter);
206+
Direction direction = order.getDirection();
207+
208+
if (builder == null) {
209+
builder = new ExpressionBuilder(direction);
210+
} else if (!builder.hasSameDirectionAs(order)) {
211+
throw new IllegalArgumentException(String.format(
212+
"%s in legacy configuration only supports a single direction to sort by!", getClass().getSimpleName()));
213+
}
214+
215+
builder.add(order.getProperty());
201216
}
202217

203-
return builder.append(sortParameter).toString();
218+
return builder == null ? Collections.emptyList() : builder.dumpExpressionIfPresentInto(expressions);
204219
}
205220

206221
/**
@@ -226,10 +241,12 @@ Sort parseParameterIntoSort(String[] source, String delimiter) {
226241
.filter(SortHandlerMethodArgumentResolver::notOnlyDots) //
227242
.toArray(String[]::new);
228243

229-
Optional<Direction> direction = elements.length == 0 ? Optional.empty()
244+
Optional<Direction> direction = elements.length == 0 //
245+
? Optional.empty() //
230246
: Direction.fromOptionalString(elements[elements.length - 1]);
231247

232-
int lastIndex = direction.map(it -> elements.length - 1).orElseGet(() -> elements.length);
248+
int lastIndex = direction.map(it -> elements.length - 1) //
249+
.orElseGet(() -> elements.length);
233250

234251
for (int i = 0; i < lastIndex; i++) {
235252
toOrder(elements[i], direction).ifPresent(allOrders::add);
@@ -240,81 +257,76 @@ Sort parseParameterIntoSort(String[] source, String delimiter) {
240257
}
241258

242259
/**
243-
* Returns whether the given source {@link String} consists of dots only.
260+
* Reads the default {@link Sort} to be used from the given {@link MethodParameter}. Rejects the parameter if both an
261+
* {@link SortDefaults} and {@link SortDefault} annotation is found as we cannot build a reliable {@link Sort}
262+
* instance then (property ordering).
244263
*
245-
* @param source must not be {@literal null}.
246-
* @return
264+
* @param parameter will never be {@literal null}.
265+
* @return the default {@link Sort} instance derived from the parameter annotations or the configured fallback-sort
266+
* {@link #setFallbackSort(Sort)}.
247267
*/
248-
private static boolean notOnlyDots(String source) {
249-
return StringUtils.hasText(source.replace(".", ""));
250-
}
268+
private Sort getDefaultFromAnnotationOrFallback(MethodParameter parameter) {
251269

252-
private static Optional<Order> toOrder(String property, Optional<Direction> direction) {
270+
SortDefaults annotatedDefaults = parameter.getParameterAnnotation(SortDefaults.class);
271+
SortDefault annotatedDefault = parameter.getParameterAnnotation(SortDefault.class);
253272

254-
if (!StringUtils.hasText(property)) {
255-
return Optional.empty();
273+
if (annotatedDefault != null && annotatedDefaults != null) {
274+
throw new IllegalArgumentException(
275+
String.format("Cannot use both @%s and @%s on parameter %s! Move %s into %s to define sorting order!",
276+
SORT_DEFAULTS_NAME, SORT_DEFAULT_NAME, parameter.toString(), SORT_DEFAULT_NAME, SORT_DEFAULTS_NAME));
256277
}
257278

258-
return Optional.of(direction.map(it -> new Order(it, property)).orElseGet(() -> Order.by(property)));
259-
}
260-
261-
/**
262-
* Folds the given {@link Sort} instance into a {@link List} of sort expressions, accumulating {@link Order} instances
263-
* of the same direction into a single expression if they are in order.
264-
*
265-
* @param sort must not be {@literal null}.
266-
* @return
267-
*/
268-
protected List<String> foldIntoExpressions(Sort sort) {
269-
270-
List<String> expressions = new ArrayList<>();
271-
ExpressionBuilder builder = null;
279+
if (annotatedDefault != null) {
280+
return appendOrCreateSortTo(annotatedDefault, Sort.unsorted());
281+
}
272282

273-
for (Order order : sort) {
283+
if (annotatedDefaults != null) {
274284

275-
Direction direction = order.getDirection();
285+
Sort sort = Sort.unsorted();
276286

277-
if (builder == null) {
278-
builder = new ExpressionBuilder(direction);
279-
} else if (!builder.hasSameDirectionAs(order)) {
280-
builder.dumpExpressionIfPresentInto(expressions);
281-
builder = new ExpressionBuilder(direction);
287+
for (SortDefault currentAnnotatedDefault : annotatedDefaults.value()) {
288+
sort = appendOrCreateSortTo(currentAnnotatedDefault, sort);
282289
}
283290

284-
builder.add(order.getProperty());
291+
return sort;
285292
}
286293

287-
return builder == null ? Collections.emptyList() : builder.dumpExpressionIfPresentInto(expressions);
294+
return fallbackSort;
288295
}
289296

290297
/**
291-
* Folds the given {@link Sort} instance into two expressions. The first being the property list, the second being the
292-
* direction.
298+
* Creates a new {@link Sort} instance from the given {@link SortDefault} or appends it to the given {@link Sort}
299+
* instance if it's not {@literal null}.
293300
*
294-
* @throws IllegalArgumentException if a {@link Sort} with multiple {@link Direction}s has been handed in.
295-
* @param sort must not be {@literal null}.
301+
* @param sortDefault
302+
* @param sortOrNull
296303
* @return
297304
*/
298-
protected List<String> legacyFoldExpressions(Sort sort) {
299-
300-
List<String> expressions = new ArrayList<>();
301-
ExpressionBuilder builder = null;
305+
private Sort appendOrCreateSortTo(SortDefault sortDefault, Sort sortOrNull) {
302306

303-
for (Order order : sort) {
307+
String[] fields = SpringDataAnnotationUtils.getSpecificPropertyOrDefaultFromValue(sortDefault, "sort");
304308

305-
Direction direction = order.getDirection();
309+
return fields.length == 0 //
310+
? Sort.unsorted()
311+
: sortOrNull.and(Sort.by(sortDefault.direction(), fields));
312+
}
306313

307-
if (builder == null) {
308-
builder = new ExpressionBuilder(direction);
309-
} else if (!builder.hasSameDirectionAs(order)) {
310-
throw new IllegalArgumentException(String.format(
311-
"%s in legacy configuration only supports a single direction to sort by!", getClass().getSimpleName()));
312-
}
314+
/**
315+
* Returns whether the given source {@link String} consists of dots only.
316+
*
317+
* @param source must not be {@literal null}.
318+
* @return
319+
*/
320+
private static boolean notOnlyDots(String source) {
321+
return StringUtils.hasText(source.replace(".", ""));
322+
}
313323

314-
builder.add(order.getProperty());
315-
}
324+
private static Optional<Order> toOrder(String property, Optional<Direction> direction) {
316325

317-
return builder == null ? Collections.emptyList() : builder.dumpExpressionIfPresentInto(expressions);
326+
return !StringUtils.hasText(property) //
327+
? Optional.empty() //
328+
: Optional.of(direction.map(it -> new Order(it, property)) //
329+
.orElseGet(() -> Order.by(property)));
318330
}
319331

320332
/**
@@ -376,17 +388,4 @@ public List<String> dumpExpressionIfPresentInto(List<String> expressions) {
376388
return expressions;
377389
}
378390
}
379-
380-
/**
381-
* Configures the {@link Sort} to be used as fallback in case no {@link SortDefault} or {@link SortDefaults} (the
382-
* latter only supported in legacy mode) can be found at the method parameter to be resolved.
383-
* <p>
384-
* If you set this to {@literal null}, be aware that you controller methods will get {@literal null} handed into them
385-
* in case no {@link Sort} data can be found in the request.
386-
*
387-
* @param fallbackSort the {@link Sort} to be used as general fallback.
388-
*/
389-
public void setFallbackSort(Sort fallbackSort) {
390-
this.fallbackSort = fallbackSort;
391-
}
392391
}

0 commit comments

Comments
 (0)