View Javadoc
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 }