28
28
import org .springframework .core .annotation .RepeatableContainers ;
29
29
import org .springframework .data .domain .Sort ;
30
30
import org .springframework .data .domain .Sort .Direction ;
31
+ import org .springframework .data .domain .Sort .NullHandling ;
31
32
import org .springframework .data .domain .Sort .Order ;
32
33
import org .springframework .data .web .SortDefault .SortDefaults ;
33
34
import org .springframework .lang .Nullable ;
41
42
* @author Mark Paluch
42
43
* @author Vedran Pavic
43
44
* @author Johannes Englmeier
45
+ * @author Petar Heyken
44
46
* @see SortHandlerMethodArgumentResolver
45
47
* @see ReactiveSortHandlerMethodArgumentResolver
46
48
* @since 2.2
@@ -165,7 +167,14 @@ private Sort appendOrCreateSortTo(MergedAnnotation<SortDefault> sortDefault, Sor
165
167
List <Order > orders = new ArrayList <>(fields .length );
166
168
for (String field : fields ) {
167
169
168
- Order order = new Order (sortDefault .getEnum ("direction" , Sort .Direction .class ), field );
170
+ Order order = new Order (sortDefault .getEnum ("direction" , Direction .class ), field );
171
+
172
+ order = switch (sortDefault .getEnum ("nullHandling" , NullHandling .class )) {
173
+ case NATIVE -> order .nullsNative ();
174
+ case NULLS_FIRST -> order .nullsFirst ();
175
+ case NULLS_LAST -> order .nullsLast ();
176
+ };
177
+
169
178
orders .add (sortDefault .getBoolean ("caseSensitive" ) ? order : order .ignoreCase ());
170
179
}
171
180
@@ -214,6 +223,7 @@ Sort parseParameterIntoSort(List<String> source, String delimiter) {
214
223
}
215
224
216
225
SortOrderParser .parse (part , delimiter ) //
226
+ .parseNullHandling () //
217
227
.parseIgnoreCase () //
218
228
.parseDirection () //
219
229
.forEachOrder (allOrders ::add );
@@ -360,22 +370,28 @@ List<String> dumpExpressionIfPresentInto(List<String> expressions) {
360
370
static class SortOrderParser {
361
371
362
372
private static final String IGNORECASE = "ignorecase" ;
373
+ private static final String NULLSNATIVE = "nullsnative" ;
374
+ private static final String NULLSFIRST = "nullsfirst" ;
375
+ private static final String NULLSLAST = "nullslast" ;
363
376
364
377
private final String [] elements ;
365
378
private final int lastIndex ;
366
379
private final Optional <Direction > direction ;
367
380
private final Optional <Boolean > ignoreCase ;
381
+ private final Optional <NullHandling > nullHandling ;
368
382
369
383
private SortOrderParser (String [] elements ) {
370
- this (elements , elements .length , Optional .empty (), Optional .empty ());
384
+ this (elements , elements .length , Optional .empty (), Optional .empty (), Optional . empty () );
371
385
}
372
386
373
387
private SortOrderParser (String [] elements , int lastIndex , Optional <Direction > direction ,
374
- Optional <Boolean > ignoreCase ) {
388
+ Optional <Boolean > ignoreCase , Optional <NullHandling > nullHandling ) {
389
+
375
390
this .elements = elements ;
376
391
this .lastIndex = Math .max (0 , lastIndex );
377
392
this .direction = direction ;
378
393
this .ignoreCase = ignoreCase ;
394
+ this .nullHandling = nullHandling ;
379
395
}
380
396
381
397
/**
@@ -394,16 +410,34 @@ public static SortOrderParser parse(String part, String delimiter) {
394
410
return new SortOrderParser (elements );
395
411
}
396
412
413
+ /**
414
+ * Parse the {@link NullHandling} portion of the sort specification.
415
+ *
416
+ * @return a new parsing state object.
417
+ */
418
+ public SortOrderParser parseNullHandling () {
419
+
420
+ Optional <NullHandling > nullHandling = lastIndex > 0 ?
421
+ fromOptionalNullHandlingString (elements [lastIndex - 1 ]) :
422
+ Optional .empty ();
423
+
424
+ return new SortOrderParser (elements , lastIndex - (nullHandling .isPresent () ? 1 : 0 ), direction , ignoreCase ,
425
+ nullHandling );
426
+ }
427
+
397
428
/**
398
429
* Parse the {@code ignoreCase} portion of the sort specification.
399
430
*
400
431
* @return a new parsing state object.
401
432
*/
402
433
public SortOrderParser parseIgnoreCase () {
403
434
404
- Optional <Boolean > ignoreCase = lastIndex > 0 ? fromOptionalString (elements [lastIndex - 1 ]) : Optional .empty ();
435
+ Optional <Boolean > ignoreCase = lastIndex > 0 ?
436
+ fromOptionalIgnoreCaseString (elements [lastIndex - 1 ]) :
437
+ Optional .empty ();
405
438
406
- return new SortOrderParser (elements , lastIndex - (ignoreCase .isPresent () ? 1 : 0 ), direction , ignoreCase );
439
+ return new SortOrderParser (elements , lastIndex - (ignoreCase .isPresent () ? 1 : 0 ), direction , ignoreCase ,
440
+ nullHandling );
407
441
}
408
442
409
443
/**
@@ -416,7 +450,8 @@ public SortOrderParser parseDirection() {
416
450
Optional <Direction > direction = lastIndex > 0 ? Direction .fromOptionalString (elements [lastIndex - 1 ])
417
451
: Optional .empty ();
418
452
419
- return new SortOrderParser (elements , lastIndex - (direction .isPresent () ? 1 : 0 ), direction , ignoreCase );
453
+ return new SortOrderParser (elements , lastIndex - (direction .isPresent () ? 1 : 0 ), direction , ignoreCase ,
454
+ nullHandling );
420
455
}
421
456
422
457
/**
@@ -431,7 +466,24 @@ public void forEachOrder(Consumer<? super Order> callback) {
431
466
}
432
467
}
433
468
434
- private Optional <Boolean > fromOptionalString (String value ) {
469
+ private Optional <NullHandling > fromOptionalNullHandlingString (String value ) {
470
+
471
+ if (NULLSNATIVE .equalsIgnoreCase (value )) {
472
+ return Optional .of (NullHandling .NATIVE );
473
+ }
474
+
475
+ if (NULLSFIRST .equalsIgnoreCase (value )) {
476
+ return Optional .of (NullHandling .NULLS_FIRST );
477
+ }
478
+
479
+ if (NULLSLAST .equalsIgnoreCase (value )) {
480
+ return Optional .of (NullHandling .NULLS_LAST );
481
+ }
482
+
483
+ return Optional .empty ();
484
+ }
485
+
486
+ private Optional <Boolean > fromOptionalIgnoreCaseString (String value ) {
435
487
return IGNORECASE .equalsIgnoreCase (value ) ? Optional .of (true ) : Optional .empty ();
436
488
}
437
489
@@ -443,6 +495,14 @@ private Optional<Order> toOrder(String property) {
443
495
444
496
Order order = direction .map (it -> new Order (it , property )).orElseGet (() -> Order .by (property ));
445
497
498
+ if (nullHandling .isPresent ()) {
499
+ order = switch (nullHandling .get ()) {
500
+ case NATIVE -> order .nullsNative ();
501
+ case NULLS_FIRST -> order .nullsFirst ();
502
+ case NULLS_LAST -> order .nullsLast ();
503
+ };
504
+ }
505
+
446
506
if (ignoreCase .isPresent ()) {
447
507
return Optional .of (order .ignoreCase ());
448
508
}
0 commit comments