11package it .aboutbits .springboot .toolbox .parameter ;
22
3+ import lombok .EqualsAndHashCode ;
4+ import lombok .Getter ;
35import lombok .NonNull ;
6+ import lombok .experimental .Accessors ;
47import org .springframework .data .domain .Sort ;
58
6- import java .util .Collections ;
9+ import java .util .ArrayList ;
710import java .util .List ;
811import java .util .Map ;
912import java .util .stream .Collectors ;
1013import java .util .stream .Stream ;
1114
12- public record SortParameter <T extends Enum <?> & SortParameter .Definition >(List <SortField > sortFields ) {
15+ /**
16+ * Represents sorting parameters for data retrieval and manipulation.
17+ * This class provides methods for creating, customizing, and applying sorting criteria
18+ * based on enum constants and associated sort properties. Sorting criteria can be defined
19+ * with various configurations, including direction and null-handling behavior.
20+ */
21+ @ EqualsAndHashCode
22+ public final class SortParameter <T extends Enum <?> & SortParameter .Definition > {
1323 private static final String DEFAULT_SORT_PROPERTY = "id" ;
14- private static final Sort DEFAULT_SORT = Sort .by (
15- Sort .Direction .ASC ,
16- DEFAULT_SORT_PROPERTY
17- );
24+ private static final Sort .Direction DEFAULT_SORT_DIRETION = Sort .Direction .ASC ;
1825
26+ @ Accessors (fluent = true )
27+ @ Getter
28+ private final List <SortField > sortFields = new ArrayList <>();
29+
30+ public SortParameter (List <SortField > sortFields ) {
31+ if (sortFields == null ) {
32+ return ;
33+ }
34+ this .sortFields .addAll (sortFields );
35+ }
36+
37+ private SortParameter () {
38+ }
39+
40+ /**
41+ * Creates a {@link SortParameter} that represents an unsorted state.
42+ *
43+ * @param <T> a type that extends both {@link Enum} and {@link Definition}.
44+ * @return an instance of {@link SortParameter} configured with no sorting fields.
45+ */
1946 public static <T extends Enum <?> & Definition > SortParameter <T > unsorted () {
20- return new SortParameter <>(Collections . emptyList () );
47+ return new SortParameter <>();
2148 }
2249
50+ /**
51+ * Creates a {@link SortParameter} initialized with the provided sort definitions.
52+ * Each provided enum constant is converted into a {@link SortField} with ascending
53+ * order direction and default null-handling behavior. This method allows the
54+ * specification of multiple sorting criteria.
55+ *
56+ * @param <T> a type parameter representing an enum that implements the {@link Definition} interface.
57+ * @param sortDefinitions an array of enum constants defining the sort properties. Must not be null.
58+ * @return a {@link SortParameter} instance configured with the given sort definitions.
59+ */
2360 @ SafeVarargs
2461 public static <T extends Enum <?> & Definition > SortParameter <T > by (
2562 @ NonNull T ... sortDefinitions
@@ -36,6 +73,42 @@ public static <T extends Enum<?> & Definition> SortParameter<T> by(
3673 );
3774 }
3875
76+ /**
77+ * Creates a {@link SortParameter} initialized with a single sort definition.
78+ * This method allows specifying the property to sort by, the direction of sorting,
79+ * and uses the default null-handling behavior ({@link Sort.NullHandling#NATIVE}).
80+ *
81+ * @param <T> a type that extends both {@link Enum} and {@link Definition}.
82+ * @param sortDefinition an enum constant defining the property to sort by. Must not be null.
83+ * @param direction the direction of sorting, either {@link Sort.Direction#ASC} or {@link Sort.Direction#DESC}.
84+ * Must not be null.
85+ * @return an instance of {@link SortParameter} configured with the given sort definition and direction.
86+ */
87+ public static <T extends Enum <?> & Definition > SortParameter <T > by (
88+ @ NonNull T sortDefinition ,
89+ @ NonNull Sort .Direction direction
90+ ) {
91+ return new SortParameter <>(
92+ List .of (new SortField (
93+ sortDefinition .name (),
94+ direction ,
95+ Sort .NullHandling .NATIVE
96+ )
97+ )
98+ );
99+ }
100+
101+ /**
102+ * Creates a {@link SortParameter} instance configured with a single sort definition.
103+ * This method allows specifying the property to sort by, the direction of sorting,
104+ * and null-handling behavior.
105+ *
106+ * @param <T> the type parameter extending both {@link Enum} and {@link Definition}.
107+ * @param sortDefinition an enum constant defining the property to sort by. Must not be null.
108+ * @param direction the direction of sorting, either {@link Sort.Direction#ASC} or {@link Sort.Direction#DESC}. Must not be null.
109+ * @param nullHandling the strategy for handling null values during sorting, specified by {@link Sort.NullHandling}. Must not be null.
110+ * @return an instance of {@link SortParameter} configured with the given sort definition, direction, and null-handling behavior.
111+ */
39112 public static <T extends Enum <?> & Definition > SortParameter <T > by (
40113 @ NonNull T sortDefinition ,
41114 @ NonNull Sort .Direction direction ,
@@ -51,10 +124,105 @@ public static <T extends Enum<?> & Definition> SortParameter<T> by(
51124 );
52125 }
53126
127+ /**
128+ * Adds additional sorting criteria to the existing {@link SortParameter}.
129+ * Each provided enum constant is converted into a {@link SortField} with ascending
130+ * order direction and default null-handling behavior.
131+ *
132+ * @param sortDefinitions an array of enum constants defining the additional sort properties. Must not be null.
133+ * @return the updated {@link SortParameter} instance containing the new sort definitions.
134+ */
135+ @ SafeVarargs
136+ public final SortParameter <T > and (
137+ @ NonNull T ... sortDefinitions
138+ ) {
139+ sortFields .addAll (
140+ Stream .of (sortDefinitions )
141+ .map (sortDefinition -> new SortField (
142+ sortDefinition .name (),
143+ Sort .Direction .ASC ,
144+ Sort .NullHandling .NATIVE
145+ )
146+ )
147+ .toList ()
148+ );
149+
150+ return this ;
151+ }
152+
153+ /**
154+ * Adds a sorting criterion to the current {@link SortParameter} instance.
155+ * The provided sort definition and direction are converted into a {@link SortField}
156+ * with default null-handling behavior and appended to the existing sort fields.
157+ *
158+ * @param sortDefinition the enum constant defining the property to sort by. Must not be null.
159+ * @param direction the direction of sorting, either {@link Sort.Direction#ASC} or {@link Sort.Direction#DESC}. Must not be null.
160+ * @return the updated {@link SortParameter} instance containing the new sorting criterion.
161+ */
162+ public SortParameter <T > and (
163+ @ NonNull T sortDefinition ,
164+ @ NonNull Sort .Direction direction
165+ ) {
166+ sortFields .add (
167+ new SortField (
168+ sortDefinition .name (),
169+ direction ,
170+ Sort .NullHandling .NATIVE
171+ )
172+ );
173+
174+ return this ;
175+ }
176+
177+ /**
178+ * Adds a sorting criterion to the current {@link SortParameter} instance. The provided sort definition,
179+ * direction, and null-handling behavior are converted into a {@link SortField} and appended to the
180+ * existing sort fields.
181+ *
182+ * @param sortDefinition the enum constant defining the property to sort by. Must not be null.
183+ * @param direction the direction of sorting, either {@link Sort.Direction#ASC} or {@link Sort.Direction#DESC}. Must not be null.
184+ * @param nullHandling the strategy for handling null values during sorting, specified by {@link Sort.NullHandling}. Must not be null.
185+ * @return the updated {@link SortParameter} instance containing the new sorting criterion.
186+ */
187+ public SortParameter <T > and (
188+ @ NonNull T sortDefinition ,
189+ @ NonNull Sort .Direction direction ,
190+ @ NonNull Sort .NullHandling nullHandling
191+ ) {
192+ sortFields .add (
193+ new SortField (
194+ sortDefinition .name (),
195+ direction ,
196+ nullHandling
197+ )
198+ );
199+
200+ return this ;
201+ }
202+
203+ /**
204+ * Returns the current {@link SortParameter} instance if it has defined sorting fields.
205+ * Otherwise, returns the provided fallback {@link SortParameter}.
206+ *
207+ * @param fallback the {@link SortParameter} to use as a fallback in case the current instance
208+ * has no defined sorting fields. Must not be null.
209+ * @return the current {@link SortParameter} if it has defined sorting fields,
210+ * or the provided fallback if it does not.
211+ */
54212 public SortParameter <T > or (@ NonNull SortParameter <T > fallback ) {
55- return sortFields == null || sortFields .isEmpty () ? fallback : this ;
213+ return sortFields .isEmpty () ? fallback : this ;
56214 }
57215
216+ /**
217+ * Builds a {@link Sort} object without applying any default sorting parameters. Converts the enum keys
218+ * of the provided map to their string names and generates the sort object.
219+ *
220+ * @param mapping a non-null map where the keys are enumeration values representing sort properties
221+ * and the values are their associated sort directions. The enumeration keys must
222+ * have a `name()` method for string conversion. Must not be null.
223+ * @return an instance of {@link Sort} created using the transformed key-value mapping,
224+ * excluding default sorting behavior.
225+ */
58226 public Sort buildSortWithoutDefault (@ NonNull Map <T , String > mapping ) {
59227 var stringMapping = mapping .entrySet ().stream ()
60228 .collect (Collectors .toMap (
@@ -65,6 +233,16 @@ public Sort buildSortWithoutDefault(@NonNull Map<T, String> mapping) {
65233 return buildSort (stringMapping , false );
66234 }
67235
236+ /**
237+ * Builds a {@link Sort} object based on the provided mapping of enumeration values to string properties.
238+ * Converts enum keys to their respective string names and generates the sort object.
239+ * <p>
240+ * If a sort mapping for "id" is provided, it will be used as the default sort property. Unless specified, this sort will be applied last.
241+ *
242+ * @param mapping a non-null map where the keys represent enumeration values and the values represent sort property names.
243+ * The enumeration keys must implement the `name()` method to retrieve their string representation.
244+ * @return an instance of {@link Sort} created using the transformed key-value mapping with default sorting behavior.
245+ */
68246 public Sort buildSort (@ NonNull Map <T , String > mapping ) {
69247 var stringMapping = mapping .entrySet ().stream ()
70248 .collect (Collectors .toMap (
@@ -75,41 +253,47 @@ public Sort buildSort(@NonNull Map<T, String> mapping) {
75253 return buildSort (stringMapping , true );
76254 }
77255
78- // SonarLint: Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.
79- @ SuppressWarnings ("java:S6204" )
80256 private Sort buildSort (@ NonNull Map <String , String > mapping , boolean withDefault ) {
81- if (sortFields == null || sortFields .isEmpty ()) {
82- return withDefault ? DEFAULT_SORT : Sort .unsorted ();
257+ if (sortFields .isEmpty ()) {
258+ return withDefault ? getMappedDefaultSort ( mapping ) : Sort .unsorted ();
83259 }
84260
85261 var additionalSort = Sort .by (
86- sortFields .stream ()
87- .filter (sortField -> mapping .containsKey (sortField .property ()))
88- .map (sortField -> new Sort .Order (
89- sortField .direction (),
90- mapping .get (sortField .property ()),
91- sortField .nullHandling ()
92- ))
93- // We do not use .toList() here as we potentially want to modify the sort list later in the StoreImpl
94- .collect (Collectors .toList ())
262+ new ArrayList <>(
263+ sortFields .stream ()
264+ .filter (sortField -> mapping .containsKey (sortField .property ()))
265+ .map (sortField -> new Sort .Order (
266+ sortField .direction (),
267+ mapping .get (sortField .property ()),
268+ sortField .nullHandling ()
269+ ))
270+ .toList ()
271+ )
95272 );
96273
97274 if (withDefault ) {
98275 var includesDefault = additionalSort .getOrderFor (DEFAULT_SORT_PROPERTY ) != null ;
99276 if (!includesDefault ) {
100- return additionalSort .and (DEFAULT_SORT );
277+ return additionalSort .and (getMappedDefaultSort ( mapping ) );
101278 }
102279 }
103280 return additionalSort ;
104281 }
105282
283+ private static Sort getMappedDefaultSort (Map <String , String > mapping ) {
284+ return Sort .by (DEFAULT_SORT_DIRETION , mapping .getOrDefault (DEFAULT_SORT_PROPERTY , DEFAULT_SORT_PROPERTY ));
285+ }
286+
106287 public record SortField (
107288 @ NonNull String property ,
108289 @ NonNull Sort .Direction direction ,
109290 @ NonNull Sort .NullHandling nullHandling
110291 ) {
111292 }
112293
294+ /**
295+ * Interface to give enums the purpose of listing sortable keys.
296+ */
113297 public interface Definition {
114298 }
115299}
0 commit comments