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 }