diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java index 84d814bbe..810e542c6 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java @@ -22,4 +22,14 @@ public class StrolchRestfulConstants { 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 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"; + } diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java index d4bea27db..383ff7c0c 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java @@ -225,23 +225,25 @@ public class ModelQuery { StrolchElementVisitor toJsonVisitor) { // paging - Paging paging = Paging.asPage(elements, queryData.getPageSize(), queryData.getPage()); + Paging paging = Paging.asPage(elements, queryData.getOffset(), queryData.getLimit()); // get page List page = paging.getPage(); JsonObject root = new JsonObject(); 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("nrOfElements", paging.getNrOfElements()); if (StringHelper.isNotEmpty(queryData.getOrderBy())) root.addProperty("sortBy", queryData.getOrderBy()); root.addProperty("ascending", queryData.isAscending()); - root.addProperty("nrOfPages", paging.getNrOfPages()); - root.addProperty("pageSize", paging.getPageSize()); - root.addProperty("page", paging.getPageToReturn()); // add items JsonArray data = new JsonArray(); diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/QueryData.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/QueryData.java index d18b3b219..f236cf753 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/QueryData.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/QueryData.java @@ -7,14 +7,11 @@ public class QueryData { @QueryParam("realmName") private String realmName; - @QueryParam("draw") - private int draw; + @QueryParam("offset") + private int offset; - @QueryParam("pageSize") - private int pageSize; - - @QueryParam("page") - private int page; + @QueryParam("limit") + private int limit; @QueryParam("query") private String query; @@ -33,14 +30,6 @@ public class QueryData { this.realmName = realmName; } - public int getDraw() { - return this.draw; - } - - public void setDraw(int draw) { - this.draw = draw; - } - public String getOrderBy() { return this.orderBy; } @@ -57,20 +46,20 @@ public class QueryData { this.ascending = ascending; } - public int getPageSize() { - return this.pageSize; + public int getOffset() { + return this.offset; } - public void setPageSize(int pageSize) { - this.pageSize = pageSize; + public void setOffset(int offset) { + this.offset = offset; } - public int getPage() { - return this.page; + public int getLimit() { + return this.limit; } - public void setPage(int page) { - this.page = page; + public void setLimit(int limit) { + this.limit = limit; } public String getQuery() { diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/helper/ResponseUtil.java b/li.strolch.rest/src/main/java/li/strolch/rest/helper/ResponseUtil.java index 5bd5ea871..3d451aae6 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/helper/ResponseUtil.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/helper/ResponseUtil.java @@ -1,12 +1,25 @@ 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.Response; import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import li.strolch.utils.collections.Paging; import li.strolch.utils.helper.ExceptionHelper; import li.strolch.utils.helper.StringHelper; @@ -15,8 +28,6 @@ import li.strolch.utils.helper.StringHelper; */ public class ResponseUtil { - public static final String MSG = "msg"; - public static Response toResponse() { JsonObject response = new JsonObject(); response.addProperty(MSG, StringHelper.DASH); @@ -64,4 +75,30 @@ public class ResponseUtil { return Response.serverError().entity(json).type(MediaType.APPLICATION_JSON).build(); } + + public static Response toResponse(Paging 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 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(); + } + } diff --git a/li.strolch.utils/src/main/java/li/strolch/utils/collections/Paging.java b/li.strolch.utils/src/main/java/li/strolch/utils/collections/Paging.java index 1f45d06b0..7b6a4af34 100644 --- a/li.strolch.utils/src/main/java/li/strolch/utils/collections/Paging.java +++ b/li.strolch.utils/src/main/java/li/strolch/utils/collections/Paging.java @@ -18,6 +18,9 @@ package li.strolch.utils.collections; 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 <eitch@eitchnet.ch> * * @param @@ -25,108 +28,138 @@ import java.util.List; */ public class Paging { - private int pageSize; - private int pageToReturn; - private int nrOfPages; - private int nrOfElements; + private int limit; + private int offset; + private int size; private List input; private List page; - private Paging(int pageSize, int indexOfPageToReturn) { - this.pageSize = pageSize; - this.pageToReturn = indexOfPageToReturn; + private int nextOffset; + private int previousOffset; + 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; - } - - public void setNrOfElements(int nrOfElements) { - this.nrOfElements = nrOfElements; + /** + * @return the size of the input list + */ + public int getSize() { + return this.size; } + /** + * @return the input + */ public List getInput() { return this.input; } + /** + * @return the current page/window + */ public List getPage() { 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 - * the list to paginate - * @param pageSize - * The number of items to return in each page - * @param page - * the page to return - start index is 1 - * + * the list to paginate / create a window for + * @param offset + * where to start the sub list + * @param limit + * The number of items to return in each page/window * @return a {@link Paging} instance from which the selected page (list) can be retrieved * * @param * the type of element in the list */ - public static Paging asPage(List list, int pageSize, int page) { + public static Paging asPage(List list, int offset, int limit) { - Paging paging = new Paging<>(pageSize, page); - paging.nrOfElements = list.size(); + Paging paging = new Paging<>(); + 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.nextOffset = 0; + paging.lastOffset = 0; + paging.previousOffset = 0; + return paging; } - int size = list.size(); + paging.page = list.subList(offset, Math.min(paging.size, offset + limit)); - // calculate maximum number of pages - paging.nrOfPages = size / paging.pageSize; - if (size % paging.pageSize != 0) - paging.nrOfPages++; + if (limit == 1) { - // and from this validate requested page - paging.pageToReturn = Math.min(paging.pageToReturn, paging.nrOfPages); + paging.nextOffset = Math.min(paging.size - 1, offset + 1); + paging.previousOffset = Math.max(0, offset - 1); + paging.lastOffset = paging.size - 1; - // now we can calculate the start and end of the page - int start = Math.max(0, paging.pageSize * paging.pageToReturn - paging.pageSize); - int end = Math.min(size, paging.pageSize * paging.pageToReturn); + } else { - // and return the list - paging.page = list.subList(start, end); - - // fix page size - if (paging.page.size() < paging.pageSize) - paging.pageSize = paging.page.size(); + paging.nextOffset = Math.min(paging.size - 1, limit + offset); + paging.previousOffset = Math.max(0, offset - limit); + paging.lastOffset = offset + ((paging.size - offset) - ((paging.size - offset) % limit)); + } return paging; } diff --git a/li.strolch.utils/src/test/java/li/strolch/utils/collections/PagingTest.java b/li.strolch.utils/src/test/java/li/strolch/utils/collections/PagingTest.java index b4ec9db51..10e52eaf2 100644 --- a/li.strolch.utils/src/test/java/li/strolch/utils/collections/PagingTest.java +++ b/li.strolch.utils/src/test/java/li/strolch/utils/collections/PagingTest.java @@ -22,59 +22,153 @@ import java.util.List; import org.junit.Test; -import li.strolch.utils.collections.Paging; - /** * @author Robert von Burg <eitch@eitchnet.ch> */ public class PagingTest { + private List getInputList() { + List list = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); + return list; + } + @Test public void shouldReturnAll() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); + List list = getInputList(); List page = Paging.asPage(list, -1, -1).getPage(); assertEquals(list, page); + page = Paging.asPage(list, 0, 0).getPage(); + assertEquals(list, page); } @Test public void shouldReturnFirstPage1() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); - List page = Paging.asPage(list, 1, 1).getPage(); + List list = getInputList(); + List page = Paging.asPage(list, 0, 1).getPage(); assertEquals(Arrays.asList("a"), page); } @Test public void shouldReturnFirstPage2() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); - List page = Paging.asPage(list, 2, 1).getPage(); + List list = getInputList(); + List page = Paging.asPage(list, 0, 2).getPage(); assertEquals(Arrays.asList("a", "b"), page); } @Test public void shouldReturnSecondPage1() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); - List page = Paging.asPage(list, 1, 2).getPage(); + List list = getInputList(); + List page = Paging.asPage(list, 1, 1).getPage(); assertEquals(Arrays.asList("b"), page); } @Test public void shouldReturnSecondPage2() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); + List list = getInputList(); List page = Paging.asPage(list, 2, 2).getPage(); assertEquals(Arrays.asList("c", "d"), page); } @Test public void shouldReturnLastPage1() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); - List page = Paging.asPage(list, 1, 6).getPage(); + List list = getInputList(); + List page = Paging.asPage(list, 5, 1).getPage(); assertEquals(Arrays.asList("f"), page); } @Test public void shouldReturnLastPage2() { - List list = Arrays.asList("a", "b", "c", "d", "e", "f"); - List page = Paging.asPage(list, 2, 3).getPage(); + List list = getInputList(); + List page = Paging.asPage(list, 4, 2).getPage(); assertEquals(Arrays.asList("e", "f"), page); } + + @Test + public void shouldReturnLastPage3() { + List list = getInputList(); + + Paging paging = Paging.asPage(list, 0, 2); + List 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 list = getInputList(); + + Paging paging = Paging.asPage(list, 0, 1); + List 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 list = getInputList(); + + Paging paging = Paging.asPage(list, 0, 3); + List 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 list = getInputList(); + + Paging paging = Paging.asPage(list, 1, 1); + List 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 list = getInputList(); + + Paging paging = Paging.asPage(list, 2, 2); + List 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); + } }