Add pagination container
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 5 May 2017 20:46:34 +0000 (22:46 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 5 May 2017 20:51:35 +0000 (22:51 +0200)
src/main/kotlin/net/pterodactylus/sone/utils/Pagination.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/utils/PaginationTest.kt [new file with mode: 0644]

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 (file)
index 0000000..130fe2e
--- /dev/null
@@ -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.
+ * <p>
+ * 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 <T>
+ *            The type of the list elements
+ */
+class Pagination<out T>(private val originalItems: List<T>, pageSize: Int): Iterable<T> {
+
+       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 (file)
index 0000000..9a1153c
--- /dev/null
@@ -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<Int>(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))
+       }
+
+}