1 package org.nuiton.util.pagination;
2
3 /*
4 * #%L
5 * Nuiton Utils
6 * %%
7 * Copyright (C) 2004 - 2017 CodeLutin
8 * %%
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program. If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * #L%
23 */
24
25 import com.google.common.base.Preconditions;
26 import com.google.common.collect.Iterables;
27 import com.google.common.collect.Lists;
28
29 import java.io.Serializable;
30 import java.util.Collections;
31 import java.util.LinkedList;
32 import java.util.List;
33
34 /**
35 * This class represents the necessary information to do pagination (page number, size, ...).
36 *
37 * @author Arnaud Thimel (Code Lutin)
38 * @since 3.0
39 */
40 public class PaginationParameter implements Serializable {
41
42 private static final long serialVersionUID = 1L;
43
44 /**
45 * All results.
46 */
47 protected static final int ALL_PAGE_SIZE = -1;
48
49 /**
50 * An instance that represents a page of ALL elements
51 */
52 public static final PaginationParameter ALL = PaginationParameter.of(0, ALL_PAGE_SIZE);
53
54 /**
55 * 0-based page number
56 */
57 protected int pageNumber;
58
59 /**
60 * The size of each page. Value can be -1 (for infinite pageSize) or greater than 0
61 */
62 protected int pageSize;
63
64 /**
65 * The list of order clauses. This instance is unmodifiable and never null.
66 */
67 protected List<PaginationOrder> orderClauses;
68
69 protected PaginationParameter(int pageNumber, int pageSize) {
70 this(pageNumber, pageSize, new LinkedList<PaginationOrder>());
71 }
72
73 protected PaginationParameter(int pageNumber, int pageSize, List<PaginationOrder> orderClauses) {
74 Preconditions.checkArgument(pageNumber >= 0, "pageNumber cannot be lower than 0");
75 Preconditions.checkArgument(pageSize == -1 || pageSize > 0, "pageSize can only be -1 or greater than 0");
76 Preconditions.checkArgument(pageSize != -1 || pageNumber == 0, "This is non-sense to have pageNumber>1 if pageSize==-1");
77 this.pageNumber = pageNumber;
78 this.pageSize = pageSize;
79 this.orderClauses = Collections.unmodifiableList(orderClauses);
80 }
81
82 /**
83 * Method to create a PaginationParameter only based on pageNumber and pageSize (no order clauses).
84 *
85 * @param pageNumber the index (0-based) of the page
86 * @param pageSize the size of each page. Value can be -1 (for infinite pageSize) or greater than 0
87 * @return an immutable PaginationParameter instance
88 */
89 public static PaginationParameter of(int pageNumber, int pageSize) {
90 PaginationParameter result = new PaginationParameter(pageNumber, pageSize);
91 return result;
92 }
93
94 /**
95 * Method to create a PaginationParameter based on pageNumber, pageSize and a single order clause.
96 *
97 * If you have an unknown number of order clauses, you should use the {@link #builder(int, int)}
98 * method together with {@link PaginationParameterBuilder#addOrder(String, boolean)} and
99 * {@link PaginationParameterBuilder#build()} methods.
100 *
101 * @param pageNumber the index (0-based) of the page
102 * @param pageSize the size of each page. Value can be -1 (for infinite pageSize) or greater than 0
103 * @param orderClause1 an order clause attribute name. It comes together with {code}orderDesc1{/code}
104 * @param orderDesc1 the asc/desc property associated with {code}orderClause1{/code}
105 * @return an immutable PaginationParameter instance
106 * @see PaginationOrder
107 * @see PaginationParameterBuilder
108 */
109 public static PaginationParameter of(int pageNumber, int pageSize,
110 String orderClause1, boolean orderDesc1) {
111 return builder(pageNumber, pageSize)
112 .addOrder(orderClause1, orderDesc1)
113 .build();
114 }
115
116 /**
117 * Method to create a PaginationParameter based on pageNumber, pageSize and two order clauses.
118 *
119 * If you have an unknown number of order clauses, you should use the {@link #builder(int, int)}
120 * method together with {@link PaginationParameterBuilder#addOrder(String, boolean)} and
121 * {@link PaginationParameterBuilder#build()} methods.
122 *
123 * @param pageNumber the index (0-based) of the page
124 * @param pageSize the size of each page. Value can be -1 (for infinite pageSize) or greater than 0
125 * @param orderClause1 an order clause attribute name. It comes together with {code}orderDesc1{/code}
126 * @param orderDesc1 the asc/desc property associated with {code}orderClause1{/code}
127 * @param orderClause2 an order clause attribute name. It comes together with {code}orderDesc2{/code}
128 * @param orderDesc2 the asc/desc property associated with {code}orderClause2{/code}
129 * @return an immutable PaginationParameter instance
130 * @see PaginationOrder
131 * @see PaginationParameterBuilder
132 */
133 public static PaginationParameter of(int pageNumber, int pageSize,
134 String orderClause1, boolean orderDesc1,
135 String orderClause2, boolean orderDesc2) {
136 return builder(pageNumber, pageSize)
137 .addOrder(orderClause1, orderDesc1)
138 .addOrder(orderClause2, orderDesc2)
139 .build();
140 }
141
142 /**
143 * Method to create a PaginationParameter based on pageNumber, pageSize and three order clauses.
144 *
145 * If you have more order clauses, or an unknown number of clauses, you should use the {@link #builder(int, int)}
146 * method together with {@link PaginationParameterBuilder#addOrder(String, boolean)} and
147 * {@link PaginationParameterBuilder#build()} methods.
148 *
149 * @param pageNumber the index (0-based) of the page
150 * @param pageSize the size of each page. Value can be -1 (for infinite pageSize) or greater than 0
151 * @param orderClause1 an order clause attribute name. It comes together with {code}orderDesc1{/code}
152 * @param orderDesc1 the asc/desc property associated with {code}orderClause1{/code}
153 * @param orderClause2 an order clause attribute name. It comes together with {code}orderDesc2{/code}
154 * @param orderDesc2 the asc/desc property associated with {code}orderClause2{/code}
155 * @param orderClause3 an order clause attribute name. It comes together with {code}orderDesc3{/code}
156 * @param orderDesc3 the asc/desc property associated with {code}orderClause3{/code}
157 * @return an immutable PaginationParameter instance
158 * @see PaginationOrder
159 * @see PaginationParameterBuilder
160 */
161 public static PaginationParameter of(int pageNumber, int pageSize,
162 String orderClause1, boolean orderDesc1,
163 String orderClause2, boolean orderDesc2,
164 String orderClause3, boolean orderDesc3) {
165 return builder(pageNumber, pageSize)
166 .addOrder(orderClause1, orderDesc1)
167 .addOrder(orderClause2, orderDesc2)
168 .addOrder(orderClause3, orderDesc3)
169 .build();
170 }
171
172 /**
173 * Method to create a PaginationParameter using the {@link PaginationParameterBuilder}.
174 *
175 * @param pageNumber the index (0-based) of the page
176 * @param pageSize the size of each page. Value can be -1 (for infinite pageSize) or greater than 0
177 * @return an immutable PaginationParameter.Builder instance
178 */
179 public static PaginationParameterBuilder builder(int pageNumber, int pageSize) {
180 PaginationParameterBuilder result = new PaginationParameterBuilder(pageNumber, pageSize);
181 return result;
182 }
183
184 public int getPageNumber() {
185 return pageNumber;
186 }
187
188 public int getPageSize() {
189 return pageSize;
190 }
191
192 public List<PaginationOrder> getOrderClauses() {
193 return orderClauses;
194 }
195
196 /**
197 * Method that computes the start index of a page according to {@link #pageNumber} and {@link #pageSize}.
198 *
199 * @return the computed start index
200 */
201 public int getStartIndex() {
202 if (pageNumber != 0) {
203 Preconditions.checkState(pageSize != -1, "This is non-sense to have pageNumber>1 if pageSize==-1");
204 }
205 int startIndex = pageNumber * pageSize;
206 return startIndex;
207 }
208
209 /**
210 * Method that computes the end index of a page according to {@link #pageNumber} and {@link #pageSize}. If the
211 * pageSize is -1, the end index will be {@link Integer#MAX_VALUE}.
212 *
213 * @return the computed end index
214 */
215 public int getEndIndex() {
216 int endIndex = Integer.MAX_VALUE;
217 if (pageSize != -1) {
218 endIndex = getStartIndex() + pageSize - 1;
219 }
220 return endIndex;
221 }
222
223 @Override
224 public boolean equals(Object o) {
225 if (this == o) {
226 return true;
227 }
228 if (o == null || getClass() != o.getClass()) {
229 return false;
230 }
231
232 PaginationParameter that = (PaginationParameter) o;
233
234 if (pageNumber != that.pageNumber) {
235 return false;
236 }
237 if (pageSize != that.pageSize) {
238 return false;
239 }
240 boolean result = orderClauses.equals(that.orderClauses);
241 return result;
242 }
243
244 @Override
245 public int hashCode() {
246 int result = pageNumber;
247 result = 31 * result + pageSize;
248 result = 31 * result + orderClauses.hashCode();
249 return result;
250 }
251
252 /**
253 * Test if pagination parameter is about all results range.
254 *
255 * @return {@code true} if pagination parameter is about all results range
256 */
257 public boolean isAll() {
258 return pageSize == ALL_PAGE_SIZE;
259 }
260
261 /**
262 * Class used to build an instance of PaginationParameter. Use the {@link #build()} method to create the
263 * {@link org.nuiton.util.pagination.PaginationParameter}.
264 *
265 * @author Arnaud Thimel (Code Lutin)
266 * @since 3.0
267 */
268 public static class PaginationParameterBuilder {
269
270 protected int pageNumber;
271 protected int pageSize;
272 protected List<PaginationOrder> orderClauses;
273
274 /**
275 * Creates a Builder instance
276 *
277 * @param pageNumber the index (0-based) of the page
278 * @param pageSize the size of each page. Value can be -1 (for infinite pageSize) or greater than 0
279 */
280 public PaginationParameterBuilder(int pageNumber, int pageSize) {
281 this.pageNumber = pageNumber;
282 this.pageSize = pageSize;
283 }
284
285 /**
286 * Adds an order clause
287 *
288 * @param clause an order clause attribute name. It comes together with {code}desc{/code}
289 * @param desc the asc/desc property associated with {code}clause{/code}
290 * @return the current Builder for a Fluent usage
291 */
292 public PaginationParameterBuilder addOrder(String clause, boolean desc) {
293 if (orderClauses == null) {
294 orderClauses = Lists.newLinkedList();
295 }
296 PaginationOrder paginationOrder = new PaginationOrder(clause, desc);
297 orderClauses.add(paginationOrder);
298 return this;
299 }
300
301 /**
302 * Adds an ASC order clause
303 *
304 * @param clause an order clause attribute name
305 * @return the current Builder for a Fluent usage
306 */
307 public PaginationParameterBuilder addAscOrder(String clause) {
308 return addOrder(clause, false);
309 }
310
311 /**
312 * Adds an DESC order clause
313 *
314 * @param clause an order clause attribute name
315 * @return the current Builder for a Fluent usage
316 */
317 public PaginationParameterBuilder addDescOrder(String clause) {
318 return addOrder(clause, true);
319 }
320
321 /**
322 * Adds an order clause. The asc/desc value is guessed from the given {code}clause{/code}. The expected format
323 * is "column asc" or "column desc"
324 *
325 * @param clause an order clause attribute name. It comes together with {code}desc{/code}
326 * @return the current Builder for a Fluent usage
327 */
328 public PaginationParameterBuilder addOrder(String clause) {
329 boolean desc = false;
330 String cleanedClause = clause;
331 int spaceIndex = clause.indexOf(' ');
332 if (spaceIndex != -1) {
333 cleanedClause = clause.substring(0, spaceIndex).trim();
334 desc = "desc".equalsIgnoreCase(clause.substring(spaceIndex + 1).trim());
335 }
336 return addOrder(cleanedClause, desc);
337 }
338
339 /**
340 * Adds an the given order clauses
341 *
342 * @param clauses an list of order clauses
343 * @return the current Builder for a Fluent usage
344 */
345 public PaginationParameterBuilder addOrderClauses(Iterable<PaginationOrder> clauses) {
346 if (orderClauses == null) {
347 orderClauses = Lists.newLinkedList();
348 }
349 if (clauses != null) {
350 Iterables.addAll(orderClauses, clauses);
351 }
352 return this;
353 }
354
355 /**
356 * Final method that instantiates the immutable PaginationParameter
357 *
358 * @return the immutable PaginationParameter built
359 */
360 public PaginationParameter build() {
361 PaginationParameter result = new PaginationParameter(pageNumber, pageSize, orderClauses);
362 return result;
363 }
364 }
365 }