[New] Rewrote the Paging class to use offset/limit

Also returns next, previous and last offsets. Added helper method to
create a REST Response from a page in ResponseUtil
This commit is contained in:
Robert von Burg 2016-10-07 11:55:08 +02:00
parent ba476b2f68
commit 992f3966dc
6 changed files with 266 additions and 101 deletions

View File

@ -22,4 +22,14 @@ public class StrolchRestfulConstants {
public static final String STROLCH_CERTIFICATE = "strolch.certificate"; //$NON-NLS-1$ public static final String STROLCH_CERTIFICATE = "strolch.certificate"; //$NON-NLS-1$
public static final String STROLCH_AUTHORIZATION = "strolch.authorization"; //$NON-NLS-1$ public static final String STROLCH_AUTHORIZATION = "strolch.authorization"; //$NON-NLS-1$
public static final String MSG = "msg";
public static final String DATA = "data";
public static final String LAST_OFFSET = "lastOffset";
public static final String NEXT_OFFSET = "nextOffset";
public static final String PREVIOUS_OFFSET = "previousOffset";
public static final String SIZE = "size";
public static final String OFFSET = "offset";
public static final String LIMIT = "limit";
} }

View File

@ -225,23 +225,25 @@ public class ModelQuery {
StrolchElementVisitor<T, JsonObject> toJsonVisitor) { StrolchElementVisitor<T, JsonObject> toJsonVisitor) {
// paging // paging
Paging<T> paging = Paging.asPage(elements, queryData.getPageSize(), queryData.getPage()); Paging<T> paging = Paging.asPage(elements, queryData.getOffset(), queryData.getLimit());
// get page // get page
List<T> page = paging.getPage(); List<T> page = paging.getPage();
JsonObject root = new JsonObject(); JsonObject root = new JsonObject();
root.addProperty("msg", "-"); root.addProperty("msg", "-");
root.addProperty("draw", queryData.getDraw()); root.addProperty("limit", paging.getLimit());
root.addProperty("offset", paging.getOffset());
root.addProperty("size", paging.getSize());
root.addProperty("previousOffset", paging.getPreviousOffset());
root.addProperty("nextOffset", paging.getNextOffset());
root.addProperty("lastOffset", paging.getLastOffset());
root.addProperty("dataSetSize", dataSetSize); root.addProperty("dataSetSize", dataSetSize);
root.addProperty("nrOfElements", paging.getNrOfElements());
if (StringHelper.isNotEmpty(queryData.getOrderBy())) if (StringHelper.isNotEmpty(queryData.getOrderBy()))
root.addProperty("sortBy", queryData.getOrderBy()); root.addProperty("sortBy", queryData.getOrderBy());
root.addProperty("ascending", queryData.isAscending()); root.addProperty("ascending", queryData.isAscending());
root.addProperty("nrOfPages", paging.getNrOfPages());
root.addProperty("pageSize", paging.getPageSize());
root.addProperty("page", paging.getPageToReturn());
// add items // add items
JsonArray data = new JsonArray(); JsonArray data = new JsonArray();

View File

@ -7,14 +7,11 @@ public class QueryData {
@QueryParam("realmName") @QueryParam("realmName")
private String realmName; private String realmName;
@QueryParam("draw") @QueryParam("offset")
private int draw; private int offset;
@QueryParam("pageSize") @QueryParam("limit")
private int pageSize; private int limit;
@QueryParam("page")
private int page;
@QueryParam("query") @QueryParam("query")
private String query; private String query;
@ -33,14 +30,6 @@ public class QueryData {
this.realmName = realmName; this.realmName = realmName;
} }
public int getDraw() {
return this.draw;
}
public void setDraw(int draw) {
this.draw = draw;
}
public String getOrderBy() { public String getOrderBy() {
return this.orderBy; return this.orderBy;
} }
@ -57,20 +46,20 @@ public class QueryData {
this.ascending = ascending; this.ascending = ascending;
} }
public int getPageSize() { public int getOffset() {
return this.pageSize; return this.offset;
} }
public void setPageSize(int pageSize) { public void setOffset(int offset) {
this.pageSize = pageSize; this.offset = offset;
} }
public int getPage() { public int getLimit() {
return this.page; return this.limit;
} }
public void setPage(int page) { public void setLimit(int limit) {
this.page = page; this.limit = limit;
} }
public String getQuery() { public String getQuery() {

View File

@ -1,12 +1,25 @@
package li.strolch.rest.helper; package li.strolch.rest.helper;
import static li.strolch.rest.StrolchRestfulConstants.DATA;
import static li.strolch.rest.StrolchRestfulConstants.LAST_OFFSET;
import static li.strolch.rest.StrolchRestfulConstants.LIMIT;
import static li.strolch.rest.StrolchRestfulConstants.MSG;
import static li.strolch.rest.StrolchRestfulConstants.NEXT_OFFSET;
import static li.strolch.rest.StrolchRestfulConstants.OFFSET;
import static li.strolch.rest.StrolchRestfulConstants.PREVIOUS_OFFSET;
import static li.strolch.rest.StrolchRestfulConstants.SIZE;
import java.util.List;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import li.strolch.utils.collections.Paging;
import li.strolch.utils.helper.ExceptionHelper; import li.strolch.utils.helper.ExceptionHelper;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.helper.StringHelper;
@ -15,8 +28,6 @@ import li.strolch.utils.helper.StringHelper;
*/ */
public class ResponseUtil { public class ResponseUtil {
public static final String MSG = "msg";
public static Response toResponse() { public static Response toResponse() {
JsonObject response = new JsonObject(); JsonObject response = new JsonObject();
response.addProperty(MSG, StringHelper.DASH); response.addProperty(MSG, StringHelper.DASH);
@ -64,4 +75,30 @@ public class ResponseUtil {
return Response.serverError().entity(json).type(MediaType.APPLICATION_JSON).build(); return Response.serverError().entity(json).type(MediaType.APPLICATION_JSON).build();
} }
public static Response toResponse(Paging<JsonObject> paging) {
JsonObject response = new JsonObject();
response.addProperty(MSG, StringHelper.DASH);
response.addProperty(LIMIT, paging.getLimit());
response.addProperty(OFFSET, paging.getOffset());
response.addProperty(SIZE, paging.getSize());
response.addProperty(PREVIOUS_OFFSET, paging.getPreviousOffset());
response.addProperty(NEXT_OFFSET, paging.getNextOffset());
response.addProperty(LAST_OFFSET, paging.getLastOffset());
List<JsonObject> page = paging.getPage();
JsonArray data = new JsonArray();
for (JsonObject jsonObject : page) {
JsonObject element = jsonObject;
data.add(element);
}
response.add(DATA, data);
String json = new Gson().toJson(response);
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}
} }

View File

@ -18,6 +18,9 @@ package li.strolch.utils.collections;
import java.util.List; import java.util.List;
/** /**
* Paging helper creating windows on result list of queries. The input is the original complete list, and with the help
* of an offset and a limit, a page/window can be fetched.
*
* @author Robert von Burg &lt;eitch@eitchnet.ch&gt; * @author Robert von Burg &lt;eitch@eitchnet.ch&gt;
* *
* @param <T> * @param <T>
@ -25,108 +28,138 @@ import java.util.List;
*/ */
public class Paging<T> { public class Paging<T> {
private int pageSize; private int limit;
private int pageToReturn; private int offset;
private int nrOfPages; private int size;
private int nrOfElements;
private List<T> input; private List<T> input;
private List<T> page; private List<T> page;
private Paging(int pageSize, int indexOfPageToReturn) { private int nextOffset;
this.pageSize = pageSize; private int previousOffset;
this.pageToReturn = indexOfPageToReturn; private int firstOffset;
private int lastOffset;
private Paging() {
// empty constructor
} }
public int getPageSize() { /**
return this.pageSize; * @return the max number of elements to be returned in the page/window
*/
public int getLimit() {
return this.limit;
} }
public void setPageSize(int pageSize) { /**
this.pageSize = pageSize; * @return the offset in the input list for the page/window
*/
public int getOffset() {
return this.offset;
} }
public int getPageToReturn() { /**
return this.pageToReturn; * @return the next offset, i.e. offset + limit
*/
public int getNextOffset() {
return this.nextOffset;
} }
public void setPageToReturn(int pageToReturn) { /**
this.pageToReturn = pageToReturn; * @return the previous offset, i.e. offset - limit
*/
public int getPreviousOffset() {
return this.previousOffset;
} }
public int getNrOfPages() { /**
return this.nrOfPages; * @return 0, as the first offset is the first element in the input list
*/
public int getFirstOffset() {
return this.firstOffset;
} }
public void setNrOfPages(int nrOfPages) { /**
this.nrOfPages = nrOfPages; * @return the last possible offset given the offset, limit and input size
*/
public int getLastOffset() {
return this.lastOffset;
} }
public int getNrOfElements() { /**
return this.nrOfElements; * @return the size of the input list
} */
public int getSize() {
public void setNrOfElements(int nrOfElements) { return this.size;
this.nrOfElements = nrOfElements;
} }
/**
* @return the input
*/
public List<T> getInput() { public List<T> getInput() {
return this.input; return this.input;
} }
/**
* @return the current page/window
*/
public List<T> getPage() { public List<T> getPage() {
return this.page; return this.page;
} }
/** /**
* Creates a sub list of the given list by creating defining start and end from the requested page of the form * Creates a sub list of the input list with the given limit and offset
* *
* @param list * @param list
* the list to paginate * the list to paginate / create a window for
* @param pageSize * @param offset
* The number of items to return in each page * where to start the sub list
* @param page * @param limit
* the page to return - start index is 1 * The number of items to return in each page/window
*
* @return a {@link Paging} instance from which the selected page (list) can be retrieved * @return a {@link Paging} instance from which the selected page (list) can be retrieved
* *
* @param <T> * @param <T>
* the type of element in the list * the type of element in the list
*/ */
public static <T> Paging<T> asPage(List<T> list, int pageSize, int page) { public static <T> Paging<T> asPage(List<T> list, int offset, int limit) {
Paging<T> paging = new Paging<>(pageSize, page); Paging<T> paging = new Paging<>();
paging.nrOfElements = list.size(); paging.firstOffset = 0;
paging.limit = limit;
paging.offset = offset;
paging.size = list.size();
paging.input = list;
paging.firstOffset = 0;
if (paging.limit <= 0 || paging.offset < 0) {
paging.offset = 0;
paging.limit = list.size();
if (paging.pageSize <= 0 || paging.pageToReturn <= 0) {
paging.nrOfPages = 0;
paging.pageSize = list.size();
paging.pageToReturn = 0;
paging.input = list;
paging.page = list; paging.page = list;
paging.nextOffset = 0;
paging.lastOffset = 0;
paging.previousOffset = 0;
return paging; return paging;
} }
int size = list.size(); paging.page = list.subList(offset, Math.min(paging.size, offset + limit));
// calculate maximum number of pages if (limit == 1) {
paging.nrOfPages = size / paging.pageSize;
if (size % paging.pageSize != 0)
paging.nrOfPages++;
// and from this validate requested page paging.nextOffset = Math.min(paging.size - 1, offset + 1);
paging.pageToReturn = Math.min(paging.pageToReturn, paging.nrOfPages); paging.previousOffset = Math.max(0, offset - 1);
paging.lastOffset = paging.size - 1;
// now we can calculate the start and end of the page } else {
int start = Math.max(0, paging.pageSize * paging.pageToReturn - paging.pageSize);
int end = Math.min(size, paging.pageSize * paging.pageToReturn);
// and return the list paging.nextOffset = Math.min(paging.size - 1, limit + offset);
paging.page = list.subList(start, end); paging.previousOffset = Math.max(0, offset - limit);
paging.lastOffset = offset + ((paging.size - offset) - ((paging.size - offset) % limit));
// fix page size }
if (paging.page.size() < paging.pageSize)
paging.pageSize = paging.page.size();
return paging; return paging;
} }

View File

@ -22,59 +22,153 @@ import java.util.List;
import org.junit.Test; import org.junit.Test;
import li.strolch.utils.collections.Paging;
/** /**
* @author Robert von Burg &lt;eitch@eitchnet.ch&gt; * @author Robert von Burg &lt;eitch@eitchnet.ch&gt;
*/ */
public class PagingTest { public class PagingTest {
private List<String> getInputList() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g");
return list;
}
@Test @Test
public void shouldReturnAll() { public void shouldReturnAll() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, -1, -1).getPage(); List<String> page = Paging.asPage(list, -1, -1).getPage();
assertEquals(list, page); assertEquals(list, page);
page = Paging.asPage(list, 0, 0).getPage();
assertEquals(list, page);
} }
@Test @Test
public void shouldReturnFirstPage1() { public void shouldReturnFirstPage1() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, 1, 1).getPage(); List<String> page = Paging.asPage(list, 0, 1).getPage();
assertEquals(Arrays.asList("a"), page); assertEquals(Arrays.asList("a"), page);
} }
@Test @Test
public void shouldReturnFirstPage2() { public void shouldReturnFirstPage2() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, 2, 1).getPage(); List<String> page = Paging.asPage(list, 0, 2).getPage();
assertEquals(Arrays.asList("a", "b"), page); assertEquals(Arrays.asList("a", "b"), page);
} }
@Test @Test
public void shouldReturnSecondPage1() { public void shouldReturnSecondPage1() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, 1, 2).getPage(); List<String> page = Paging.asPage(list, 1, 1).getPage();
assertEquals(Arrays.asList("b"), page); assertEquals(Arrays.asList("b"), page);
} }
@Test @Test
public void shouldReturnSecondPage2() { public void shouldReturnSecondPage2() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, 2, 2).getPage(); List<String> page = Paging.asPage(list, 2, 2).getPage();
assertEquals(Arrays.asList("c", "d"), page); assertEquals(Arrays.asList("c", "d"), page);
} }
@Test @Test
public void shouldReturnLastPage1() { public void shouldReturnLastPage1() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, 1, 6).getPage(); List<String> page = Paging.asPage(list, 5, 1).getPage();
assertEquals(Arrays.asList("f"), page); assertEquals(Arrays.asList("f"), page);
} }
@Test @Test
public void shouldReturnLastPage2() { public void shouldReturnLastPage2() {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f"); List<String> list = getInputList();
List<String> page = Paging.asPage(list, 2, 3).getPage(); List<String> page = Paging.asPage(list, 4, 2).getPage();
assertEquals(Arrays.asList("e", "f"), page); assertEquals(Arrays.asList("e", "f"), page);
} }
@Test
public void shouldReturnLastPage3() {
List<String> list = getInputList();
Paging<String> paging = Paging.asPage(list, 0, 2);
List<String> page = paging.getPage();
assertEquals(Arrays.asList("a", "b"), page);
assertEquals(0, paging.getFirstOffset());
assertEquals(2, paging.getNextOffset());
assertEquals(6, paging.getLastOffset());
paging = Paging.asPage(list, paging.getLastOffset(), 2);
page = paging.getPage();
assertEquals(Arrays.asList("g"), page);
assertEquals(0, paging.getFirstOffset());
assertEquals(6, paging.getNextOffset());
assertEquals(6, paging.getLastOffset());
}
@Test
public void shouldReturnLastPage4() {
List<String> list = getInputList();
Paging<String> paging = Paging.asPage(list, 0, 1);
List<String> page = paging.getPage();
assertEquals(Arrays.asList("a"), page);
paging = Paging.asPage(list, paging.getLastOffset(), 1);
page = paging.getPage();
assertEquals(Arrays.asList("g"), page);
assertEquals(0, paging.getFirstOffset());
assertEquals(6, paging.getNextOffset());
assertEquals(6, paging.getLastOffset());
}
@Test
public void shouldReturnLastPage5() {
List<String> list = getInputList();
Paging<String> paging = Paging.asPage(list, 0, 3);
List<String> page = paging.getPage();
assertEquals(Arrays.asList("a", "b", "c"), page);
assertEquals(0, paging.getFirstOffset());
assertEquals(3, paging.getNextOffset());
assertEquals(6, paging.getLastOffset());
page = Paging.asPage(list, paging.getNextOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("d", "e", "f"), page);
page = Paging.asPage(list, paging.getLastOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("g"), page);
}
@Test
public void shouldReturnLastPage6() {
List<String> list = getInputList();
Paging<String> paging = Paging.asPage(list, 1, 1);
List<String> page = paging.getPage();
assertEquals(Arrays.asList("b"), page);
page = Paging.asPage(list, paging.getPreviousOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("a"), page);
page = Paging.asPage(list, paging.getNextOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("c"), page);
page = Paging.asPage(list, paging.getLastOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("g"), page);
}
@Test
public void shouldReturnLastPage7() {
List<String> list = getInputList();
Paging<String> paging = Paging.asPage(list, 2, 2);
List<String> page = paging.getPage();
assertEquals(Arrays.asList("c", "d"), page);
page = Paging.asPage(list, paging.getPreviousOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("a", "b"), page);
page = Paging.asPage(list, paging.getNextOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("e", "f"), page);
page = Paging.asPage(list, paging.getLastOffset(), paging.getLimit()).getPage();
assertEquals(Arrays.asList("g"), page);
}
} }