From ac6576e72155b3691ce6e5c56ee0c57eb9ac08c6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 5 May 2017 22:46:34 +0200 Subject: [PATCH] Add pagination container --- .../net/pterodactylus/sone/utils/Pagination.kt | 42 +++++ .../net/pterodactylus/sone/utils/PaginationTest.kt | 180 +++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 src/main/kotlin/net/pterodactylus/sone/utils/Pagination.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/utils/PaginationTest.kt diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Pagination.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Pagination.kt new file mode 100644 index 0000000..130fe2e --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Pagination.kt @@ -0,0 +1,42 @@ +package net.pterodactylus.sone.utils + +/** + * Helper class for lists that need pagination. Setting the page or the page + * size will automatically recalculate all other parameters, and the next call + * to [Pagination.items] retrieves all items on the current page. + *

+ * A pagination object can be used as an [Iterable]. When the [Iterator] + * from [Pagination.iterator] is requested, the iterator over + * [Pagination.items] is returned. + * + * @param + * The type of the list elements + */ +class Pagination(private val originalItems: List, pageSize: Int): Iterable { + + var page: Int = 0 + set(value) { + field = maxOf(0, minOf(value, lastPage)) + } + + var pageSize = pageSize + set(value) { + val oldFirstIndex = page * field + field = maxOf(1, value) + page = oldFirstIndex / field + } + + val pageNumber get() = page + 1 + val pageCount get() = maxOf((originalItems.size - 1) / pageSize + 1, 1) + val itemCount get() = minOf(originalItems.size - page * pageSize, pageSize) + val items get() = originalItems.subList(page * pageSize, minOf(originalItems.size, (page + 1) * pageSize)) + val isFirst get() = page == 0 + val isLast get() = page == lastPage + val isNecessary get() = pageCount > 1 + val previousPage get() = page - 1 + val nextPage get() = page + 1 + val lastPage get() = pageCount - 1 + + override fun iterator() = items.iterator() + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/PaginationTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/PaginationTest.kt new file mode 100644 index 0000000..9a1153c --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/utils/PaginationTest.kt @@ -0,0 +1,180 @@ +package net.pterodactylus.sone.utils + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.contains +import org.hamcrest.Matchers.equalTo +import org.junit.Test + +/** + * Unit test for [Pagination]. + */ +class PaginationTest { + + private val items = listOf(1, 2, 3, 4, 5) + private val pagination = Pagination(items, 2) + + @Test + fun `new pagination is at page 0`() { + assertThat(pagination.page, equalTo(0)) + } + + @Test + fun `new pagination is at page number 1`() { + assertThat(pagination.pageNumber, equalTo(1)) + } + + @Test + fun `setting a page to less than 0 sets page to 0`() { + pagination.page = -1 + assertThat(pagination.page, equalTo(0)) + } + + @Test + fun `setting page to a valid page sets page`() { + pagination.page = 1 + assertThat(pagination.page, equalTo(1)) + } + + @Test + fun `setting a too large page will cap the page`() { + pagination.page = 100 + assertThat(pagination.page, equalTo(2)) + } + + @Test + fun `the page count is returned correctly`() { + assertThat(pagination.pageCount, equalTo(3)) + } + + @Test + fun `page size is returned correctly`() { + assertThat(pagination.pageSize, equalTo(2)) + } + + @Test + fun `a page size of less than 1 is set to 1`() { + pagination.pageSize = 0 + assertThat(pagination.pageSize, equalTo(1)) + } + + @Test + fun `changing page size sets new page correctly`() { + pagination.page = 1 + pagination.pageSize = 1 + assertThat(pagination.page, equalTo(2)) + } + + @Test + fun `changing page size to very large returns to page 0`() { + pagination.page = 1 + pagination.pageSize = 20 + assertThat(pagination.page, equalTo(0)) + } + + @Test + fun `item count for current page is page size of first page`() { + assertThat(pagination.itemCount, equalTo(2)) + } + + @Test + fun `item count for last page is 1`() { + pagination.page = 2 + assertThat(pagination.itemCount, equalTo(1)) + } + + @Test + fun `items on first page are returned correctly`() { + assertThat(pagination.items, contains(1, 2)) + } + + @Test + fun `items on last page are returned correctly`() { + pagination.page = 2 + assertThat(pagination.items, contains(5)) + } + + @Test + fun `pagination is first on first page`() { + assertThat(pagination.isFirst, equalTo(true)) + } + + @Test + fun `pagination is not first on second page`() { + pagination.page = 1 + assertThat(pagination.isFirst, equalTo(false)) + } + + @Test + fun `pagination is not first on last page`() { + pagination.page = 2 + assertThat(pagination.isFirst, equalTo(false)) + } + + @Test + fun `pagination is not last on first page`() { + assertThat(pagination.isLast, equalTo(false)) + } + + @Test + fun `pagination is not last on second page`() { + pagination.page = 1 + assertThat(pagination.isLast, equalTo(false)) + } + + @Test + fun `pagination is last on last page`() { + pagination.page = 2 + assertThat(pagination.isLast, equalTo(true)) + } + + @Test + fun `pagination is necessary for three pages`() { + assertThat(pagination.isNecessary, equalTo(true)) + } + + @Test + fun `pagination is necessary for two pages`() { + pagination.pageSize = 4 + assertThat(pagination.isNecessary, equalTo(true)) + } + + @Test + fun `pagination is not necessary for one page`() { + pagination.pageSize = 20 + assertThat(pagination.isNecessary, equalTo(false)) + } + + @Test + fun `previous page is returned correctly for second page`() { + pagination.page = 1 + assertThat(pagination.previousPage, equalTo(0)) + } + + @Test + fun `previous page is returned correctly for last page`() { + pagination.page = 2 + assertThat(pagination.previousPage, equalTo(1)) + } + + @Test + fun `next page is returned correctly for first page`() { + assertThat(pagination.nextPage, equalTo(1)) + } + + @Test + fun `next page is returned correctly for second page`() { + pagination.page = 1 + assertThat(pagination.nextPage, equalTo(2)) + } + + @Test + fun `last page is returned correctly`() { + assertThat(pagination.lastPage, equalTo(2)) + } + + @Test + fun `iterator returns items on the current page`() { + assertThat(pagination.iterator().asSequence().toList(), contains(1, 2)) + } + +} -- 2.7.4