Fix a FindBugs warning by making the non-nullable type explicit
[Sone.git] / src / main / kotlin / net / pterodactylus / sone / template / RenderFilter.kt
1 package net.pterodactylus.sone.template
2
3 import net.pterodactylus.sone.core.Core
4 import net.pterodactylus.sone.text.FreemailPart
5 import net.pterodactylus.sone.text.FreenetLinkPart
6 import net.pterodactylus.sone.text.LinkPart
7 import net.pterodactylus.sone.text.Part
8 import net.pterodactylus.sone.text.PlainTextPart
9 import net.pterodactylus.sone.text.PostPart
10 import net.pterodactylus.sone.text.SonePart
11 import net.pterodactylus.sone.text.SoneTextParser
12 import net.pterodactylus.sone.text.SoneTextParserContext
13 import net.pterodactylus.util.template.Filter
14 import net.pterodactylus.util.template.TemplateContext
15 import net.pterodactylus.util.template.TemplateContextFactory
16 import net.pterodactylus.util.template.TemplateParser
17 import java.io.StringReader
18 import java.io.StringWriter
19 import java.io.Writer
20 import java.net.URLEncoder
21
22 /**
23  * Renders a number of pre-parsed [Part] into a [String].
24  *
25  * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
26  */
27 class RenderFilter(private val core: Core, private val templateContextFactory: TemplateContextFactory) : Filter {
28
29         companion object {
30                 private val plainTextTemplate = TemplateParser.parse(StringReader("<%text|html>"))
31                 private val linkTemplate = TemplateParser.parse(StringReader("<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>"))
32         }
33
34         override fun format(templateContext: TemplateContext?, data: Any?, parameters: MutableMap<String, Any?>?): Any? {
35                 @Suppress("UNCHECKED_CAST")
36                 val parts = data as? Iterable<Part> ?: return null
37                 val parsedTextWriter = StringWriter()
38                 render(parsedTextWriter, parts)
39                 return parsedTextWriter.toString()
40         }
41
42         private fun render(writer: Writer, parts: Iterable<Part>) {
43                 parts.forEach { render(writer, it) }
44         }
45
46         private fun render(writer: Writer, part: Part) {
47                 @Suppress("UNCHECKED_CAST")
48                 when (part) {
49                         is PlainTextPart -> render(writer, part)
50                         is FreenetLinkPart -> render(writer, part)
51                         is LinkPart -> render(writer, part)
52                         is SonePart -> render(writer, part)
53                         is PostPart -> render(writer, part)
54                         is FreemailPart -> render(writer, part)
55                 }
56         }
57
58         private fun render(writer: Writer, plainTextPart: PlainTextPart) {
59                 val templateContext = templateContextFactory.createTemplateContext()
60                 templateContext.set("text", plainTextPart.text)
61                 plainTextTemplate.render(templateContext, writer)
62         }
63
64         private fun render(writer: Writer, freenetLinkPart: FreenetLinkPart) {
65                 renderLink(writer, "/${freenetLinkPart.link}", freenetLinkPart.text, freenetLinkPart.title, if (freenetLinkPart.trusted) "freenet-trusted" else "freenet")
66         }
67
68         private fun render(writer: Writer, linkPart: LinkPart) {
69                 renderLink(writer, "/external-link/?_CHECKED_HTTP_=${linkPart.link.urlEncode()}", linkPart.text, linkPart.title, "internet")
70         }
71
72         private fun String.urlEncode(): String = URLEncoder.encode(this, "UTF-8")
73
74         private fun render(writer: Writer, sonePart: SonePart) {
75                 if (sonePart.sone.name != null) {
76                         renderLink(writer, "viewSone.html?sone=${sonePart.sone.id}", SoneAccessor.getNiceName(sonePart.sone), SoneAccessor.getNiceName(sonePart.sone), "in-sone")
77                 } else {
78                         renderLink(writer, "/WebOfTrust/ShowIdentity?id=${sonePart.sone.id}", sonePart.sone.id, sonePart.sone.id, "in-sone")
79                 }
80         }
81
82         private fun render(writer: Writer, postPart: PostPart) {
83                 val parser = SoneTextParser(core, core)
84                 val parserContext = SoneTextParserContext(postPart.post.sone)
85                 val parts = parser.parse(postPart.post.text, parserContext)
86                 val excerpt = StringBuilder()
87                 for (part in parts) {
88                         excerpt.append(part.text)
89                         if (excerpt.length > 20) {
90                                 val lastSpace = excerpt.lastIndexOf(" ", 20)
91                                 if (lastSpace > -1) {
92                                         excerpt.setLength(lastSpace)
93                                 } else {
94                                         excerpt.setLength(20)
95                                 }
96                                 excerpt.append("…")
97                                 break
98                         }
99                 }
100                 renderLink(writer, "viewPost.html?post=${postPart.post.id}", excerpt.toString(), SoneAccessor.getNiceName(postPart.post.sone), "in-sone")
101         }
102
103         private fun render(writer: Writer, freemailPart: FreemailPart) {
104                 val sone = core.getSone(freemailPart.identityId)
105                 val soneName = sone.transform(SoneAccessor::getNiceName).or(freemailPart.identityId)
106                 renderLink(writer,
107                                 "/Freemail/NewMessage?to=${freemailPart.identityId}",
108                                 "${freemailPart.emailLocalPart}@$soneName.freemail",
109                                 "$soneName\n${freemailPart.emailLocalPart}@${freemailPart.freemailId}.freemail",
110                                 "in-sone")
111         }
112
113         private fun renderLink(writer: Writer, link: String, text: String, title: String, cssClass: String) {
114                 val templateContext = templateContextFactory.createTemplateContext()
115                 templateContext["cssClass"] = cssClass
116                 templateContext["link"] = link
117                 templateContext["text"] = text
118                 templateContext["title"] = title
119                 linkTemplate.render(templateContext, writer)
120         }
121
122 }