Add filter that shortens a number of parts
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 11 Nov 2016 14:02:24 +0000 (15:02 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 11 Nov 2016 17:10:28 +0000 (18:10 +0100)
src/main/kotlin/net/pterodactylus/sone/template/ShortenFilter.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/template/ShortenFilterTest.kt [new file with mode: 0644]

diff --git a/src/main/kotlin/net/pterodactylus/sone/template/ShortenFilter.kt b/src/main/kotlin/net/pterodactylus/sone/template/ShortenFilter.kt
new file mode 100644 (file)
index 0000000..de7a930
--- /dev/null
@@ -0,0 +1,54 @@
+package net.pterodactylus.sone.template
+
+import net.pterodactylus.sone.text.LinkPart
+import net.pterodactylus.sone.text.Part
+import net.pterodactylus.sone.text.PlainTextPart
+import net.pterodactylus.util.template.Filter
+import net.pterodactylus.util.template.TemplateContext
+import java.util.*
+
+/**
+ * [Filter] that shortens a number of [Part]s in order to restrict the maximum visible text length.
+ */
+class ShortenFilter : Filter {
+
+       override fun format(templateContext: TemplateContext?, data: Any?, parameters: Map<String, Any?>?): Any? {
+               @Suppress("UNCHECKED_CAST")
+               val parts = data as? Iterable<Part> ?: return null
+               val length = parameters?.parseInt("length") ?: -1
+               val cutOffLength = parameters?.parseInt("cut-off-length") ?: length
+               if (length > -1) {
+                       var allPartsLength = 0
+                       val shortenedParts = ArrayList<Part>()
+                       for (part in parts) {
+                               if (part is PlainTextPart) {
+                                       val longText = part.text
+                                       if (allPartsLength < cutOffLength) {
+                                               if (allPartsLength + longText.length > cutOffLength) {
+                                                       shortenedParts.add(PlainTextPart(longText.substring(0, cutOffLength - allPartsLength) + "…"))
+                                               } else {
+                                                       shortenedParts.add(part)
+                                               }
+                                       }
+                                       allPartsLength += longText.length
+                               } else if (part is LinkPart) {
+                                       if (allPartsLength < cutOffLength) {
+                                               shortenedParts.add(part)
+                                       }
+                                       allPartsLength += part.text.length
+                               } else {
+                                       if (allPartsLength < cutOffLength) {
+                                               shortenedParts.add(part)
+                                       }
+                               }
+                       }
+                       if (allPartsLength > length) {
+                               return shortenedParts
+                       }
+               }
+               return parts
+       }
+
+       private fun Map<String, Any?>.parseInt(key: String) = this[key]?.toString()?.toInt()
+
+}
diff --git a/src/test/kotlin/net/pterodactylus/sone/template/ShortenFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/ShortenFilterTest.kt
new file mode 100644 (file)
index 0000000..c6990a7
--- /dev/null
@@ -0,0 +1,96 @@
+package net.pterodactylus.sone.template
+
+import net.pterodactylus.sone.data.Profile
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.text.FreenetLinkPart
+import net.pterodactylus.sone.text.Part
+import net.pterodactylus.sone.text.PlainTextPart
+import net.pterodactylus.sone.text.SonePart
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.contains
+import org.junit.Test
+import org.mockito.Mockito.`when`
+
+/**
+ * Unit test for [ShortenFilter].
+ */
+class ShortenFilterTest {
+
+       private val filter = ShortenFilter()
+
+       @Suppress("UNCHECKED_CAST")
+       private fun shortenParts(length: Int, cutOffLength: Int, vararg parts: Part) =
+                       filter.format(null, listOf(*parts), mapOf("cut-off-length" to cutOffLength, "length" to length)) as Iterable<Part>
+
+       @Test
+       fun `plain text part is shortened if length exceeds maxl ength`() {
+               assertThat(shortenParts(15, 10, PlainTextPart("This is a long text.")), contains<Part>(
+                               PlainTextPart("This is a …")
+               ))
+       }
+
+       @Test
+       fun `plain text part is not shortened if length does not exceed max length`() {
+               assertThat(shortenParts(20, 10, PlainTextPart("This is a long text.")), contains<Part>(
+                               PlainTextPart("This is a long text.")
+               ))
+       }
+
+       @Test
+       fun `short parts are not shortened`() {
+               assertThat(shortenParts(15, 10, PlainTextPart("This.")), contains<Part>(
+                               PlainTextPart("This.")
+               ))
+       }
+
+       @Test
+       fun `multiple plain text parts are shortened`() {
+               assertThat(shortenParts(15, 10, PlainTextPart("This "), PlainTextPart("is a long text.")), contains<Part>(
+                               PlainTextPart("This "),
+                               PlainTextPart("is a …")
+               ))
+       }
+
+       @Test
+       fun `parts after length has been reached are ignored`() {
+               assertThat(shortenParts(15, 10, PlainTextPart("This is a long text."), PlainTextPart(" And even more.")), contains<Part>(
+                               PlainTextPart("This is a …")
+               ))
+       }
+
+       @Test
+       fun `link parts are not shortened`() {
+               assertThat(shortenParts(15, 10, FreenetLinkPart("KSK@gpl.txt", "This is a long text.", false)), contains<Part>(
+                               FreenetLinkPart("KSK@gpl.txt", "This is a long text.", false)
+               ))
+       }
+
+       @Test
+       fun `additional link parts are ignored`() {
+               assertThat(shortenParts(15, 10, PlainTextPart("This is a long text."), FreenetLinkPart("KSK@gpl.txt", "This is a long text.", false)), contains<Part>(
+                               PlainTextPart("This is a …")
+               ))
+       }
+
+       @Test
+       fun `sone parts are added but their length is ignored`() {
+               val sone = mock<Sone>()
+               `when`(sone.profile).thenReturn(Profile(sone))
+               assertThat(shortenParts(15, 10, SonePart(sone), PlainTextPart("This is a long text.")), contains<Part>(
+                               SonePart(sone),
+                               PlainTextPart("This is a …")
+               ))
+       }
+
+       @Test
+       fun `additional sone parts are ignored`() {
+               val sone = mock<Sone>()
+               `when`(sone.profile).thenReturn(Profile(sone))
+               assertThat(shortenParts(15, 10, PlainTextPart("This is a long text."), SonePart(sone)), contains<Part>(
+                               PlainTextPart("This is a …")
+               ))
+       }
+
+}
+