1 /* 2 * Copyright (c) 2018 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you 5 * may not use this file except in compliance with the License. You may 6 * obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 * implied. See the License for the specific language governing 14 * permissions and limitations under the License. 15 */ 16 17 package com.android.vts.util; 18 19 import com.google.cloud.datastore.Cursor; 20 import com.google.cloud.datastore.QueryResults; 21 import com.googlecode.objectify.cmd.Query; 22 23 import java.util.ArrayList; 24 import java.util.Iterator; 25 import java.util.LinkedHashSet; 26 import java.util.List; 27 import java.util.Objects; 28 import java.util.stream.Collectors; 29 30 /** Helper class for pagination. */ 31 public class Pagination<T> implements Iterable<T> { 32 33 /** the default page size */ 34 public static final int DEFAULT_PAGE_SIZE = 10; 35 /** the default page window size */ 36 private static final int DEFAULT_PAGE_WINDOW = 10; 37 38 /** the current page */ 39 private int page; 40 41 /** the page window size */ 42 private int pageSize = DEFAULT_PAGE_SIZE; 43 44 /** the total number of found entities */ 45 private int totalCount; 46 47 /** the previous cursor string token where to start */ 48 private String previousPageCountToken = ""; 49 50 /** the previous cursor string token where to start */ 51 private String currentPageCountToken = ""; 52 53 /** the next cursor string token where to start */ 54 private String nextPageCountToken = ""; 55 56 /** the previous cursor string token list where to start */ 57 private LinkedHashSet<String> pageCountTokenSet; 58 59 /** the maximum number of pages */ 60 private int maxPages; 61 62 /** the list of object on the page */ 63 private List<T> list = new ArrayList<>(); 64 Pagination(List<T> list, int page, int pageSize, int totalCount)65 public Pagination(List<T> list, int page, int pageSize, int totalCount) { 66 this.list = list; 67 this.page = page; 68 this.pageSize = pageSize; 69 this.totalCount = totalCount; 70 } 71 Pagination( Query<T> query, int page, int pageSize, String startPageToken, LinkedHashSet<String> pageCountTokenSet)72 public Pagination( 73 Query<T> query, 74 int page, 75 int pageSize, 76 String startPageToken, 77 LinkedHashSet<String> pageCountTokenSet) { 78 this.page = page; 79 this.pageSize = pageSize; 80 this.pageCountTokenSet = pageCountTokenSet; 81 this.currentPageCountToken = startPageToken; 82 83 List<String> pageCountTokenList = 84 this.pageCountTokenSet.stream().collect(Collectors.toList()); 85 if (pageCountTokenList.size() > 0) { 86 int foundIndex = pageCountTokenList.indexOf(startPageToken); 87 if (foundIndex <= 0) { 88 this.previousPageCountToken = ""; 89 } else { 90 this.previousPageCountToken = pageCountTokenList.get(foundIndex - 1); 91 } 92 } 93 94 int limitValue = pageSize * DEFAULT_PAGE_WINDOW + pageSize / 2; 95 query = query.limit(limitValue); 96 if (startPageToken.equals("")) { 97 this.totalCount = query.count(); 98 } else { 99 query = query.startAt(Cursor.fromUrlSafe(startPageToken)); 100 this.totalCount = query.count(); 101 } 102 103 this.maxPages = 104 this.totalCount / this.pageSize + (this.totalCount % this.pageSize == 0 ? 0 : 1); 105 106 int iteratorIndex = 0; 107 int startIndex = (page % pageSize == 0 ? 9 : page % pageSize - 1) * pageSize; 108 109 QueryResults<T> resultIterator = query.iterator(); 110 while (resultIterator.hasNext()) { 111 if (startIndex <= iteratorIndex && iteratorIndex < startIndex + this.pageSize) 112 this.list.add(resultIterator.next()); 113 else resultIterator.next(); 114 if (iteratorIndex == DEFAULT_PAGE_WINDOW * this.pageSize) { 115 if ( Objects.nonNull(resultIterator) && Objects.nonNull(resultIterator.getCursorAfter()) ) { 116 this.nextPageCountToken = resultIterator.getCursorAfter().toUrlSafe(); 117 } 118 } 119 iteratorIndex++; 120 } 121 } 122 iterator()123 public Iterator<T> iterator() { 124 return list.iterator(); 125 } 126 127 /** 128 * Gets the total number of objects. 129 * 130 * @return the total number of objects as an int 131 */ getTotalCount()132 public int getTotalCount() { 133 return totalCount; 134 } 135 136 /** 137 * Gets the number of page window. 138 * 139 * @return the number of page window as an int 140 */ getPageSize()141 public int getPageSize() { 142 return pageSize; 143 } 144 145 /** 146 * Gets the maximum number of pages. 147 * 148 * @return the maximum number of pages as an int 149 */ getMaxPages()150 public int getMaxPages() { 151 return this.maxPages; 152 } 153 154 /** 155 * Gets the page. 156 * 157 * @return the page as an int 158 */ getPage()159 public int getPage() { 160 return page; 161 } 162 163 /** 164 * Gets the minimum page in the window. 165 * 166 * @return the page number 167 */ getMinPageRange()168 public int getMinPageRange() { 169 if (this.getPage() <= this.getPageSize()) { 170 return 1; 171 } else { 172 if ((this.getPage() % this.getPageSize()) == 0) { 173 return (this.getPage() / this.getPageSize() - 1) * this.getPageSize() + 1; 174 } else { 175 return this.getPage() / this.getPageSize() * this.getPageSize() + 1; 176 } 177 } 178 } 179 180 /** 181 * Gets the maximum page in the window. 182 * 183 * @return the page number 184 */ getMaxPageRange()185 public int getMaxPageRange() { 186 if (this.getMaxPages() > this.getPageSize()) { 187 return getMinPageRange() + this.getPageSize() - 1; 188 } else { 189 return this.getMinPageRange() + this.getMaxPages() - 1; 190 } 191 } 192 193 /** 194 * Gets the subset of the list for the current page. 195 * 196 * @return a List 197 */ getList()198 public List<T> getList() { 199 return this.list; 200 } 201 202 /** 203 * Gets the cursor token for the previous page starting point. 204 * 205 * @return a string of cursor of previous starting point 206 */ getPreviousPageCountToken()207 public String getPreviousPageCountToken() { 208 return this.previousPageCountToken; 209 } 210 211 /** 212 * Gets the cursor token for the current page starting point. 213 * 214 * @return a string of cursor of current starting point 215 */ getCurrentPageCountToken()216 public String getCurrentPageCountToken() { 217 return this.currentPageCountToken; 218 } 219 220 /** 221 * Gets the cursor token for the next page starting point. 222 * 223 * @return a string of cursor of next starting point 224 */ getNextPageCountToken()225 public String getNextPageCountToken() { 226 return this.nextPageCountToken; 227 } 228 } 229