1 package net.pterodactylus.sone.template;
3 import static java.lang.String.valueOf;
4 import static net.pterodactylus.sone.utils.NumberParsers.parseInt;
6 import java.io.IOException;
7 import java.io.StringReader;
8 import java.io.StringWriter;
9 import java.io.UnsupportedEncodingException;
10 import java.io.Writer;
11 import java.net.URLEncoder;
12 import java.util.ArrayList;
13 import java.util.List;
16 import net.pterodactylus.sone.core.Core;
17 import net.pterodactylus.sone.data.Album;
18 import net.pterodactylus.sone.data.Sone;
19 import net.pterodactylus.sone.text.AlbumPart;
20 import net.pterodactylus.sone.text.FreenetLinkPart;
21 import net.pterodactylus.sone.text.LinkPart;
22 import net.pterodactylus.sone.text.Part;
23 import net.pterodactylus.sone.text.PartContainer;
24 import net.pterodactylus.sone.text.PlainTextPart;
25 import net.pterodactylus.sone.text.PostPart;
26 import net.pterodactylus.sone.text.SonePart;
27 import net.pterodactylus.sone.text.SoneTextParser;
28 import net.pterodactylus.sone.text.SoneTextParserContext;
29 import net.pterodactylus.util.template.Filter;
30 import net.pterodactylus.util.template.Template;
31 import net.pterodactylus.util.template.TemplateContext;
32 import net.pterodactylus.util.template.TemplateContextFactory;
33 import net.pterodactylus.util.template.TemplateParser;
36 * {@link Filter} implementation that renders an {@link Iterable} (such as a {@link PartContainer}) of {@link Part}s to HTML.
38 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
40 public class RenderFilter implements Filter {
42 private static final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>"));
43 private static final Template linkTemplate =
44 TemplateParser.parse(new StringReader("<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>"));
45 private final Core core;
46 private final TemplateContextFactory templateContextFactory;
48 public RenderFilter(Core core, TemplateContextFactory templateContextFactory) {
50 this.templateContextFactory = templateContextFactory;
54 public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
55 if (!(data instanceof Iterable<?>)) {
58 Iterable<Part> parts = (Iterable<Part>) data;
59 int length = parseInt(valueOf(parameters.get("length")), -1);
60 int cutOffLength = parseInt(valueOf(parameters.get("cut-off-length")), length);
61 StringWriter parsedTextWriter = new StringWriter();
63 int allPartsLength = 0;
64 List<Part> shortenedParts = new ArrayList<Part>();
65 for (Part part : parts) {
66 if (part instanceof PlainTextPart) {
67 String longText = part.getText();
68 if (allPartsLength < cutOffLength) {
69 if ((allPartsLength + longText.length()) > cutOffLength) {
70 shortenedParts.add(new PlainTextPart(longText.substring(0, cutOffLength - allPartsLength) + "…"));
72 shortenedParts.add(part);
75 allPartsLength += longText.length();
76 } else if (part instanceof LinkPart) {
77 if (allPartsLength < cutOffLength) {
78 shortenedParts.add(part);
80 allPartsLength += part.getText().length();
82 if (allPartsLength < cutOffLength) {
83 shortenedParts.add(part);
87 if (allPartsLength >= length) {
88 parts = shortenedParts;
91 render(parsedTextWriter, parts);
92 return parsedTextWriter.toString();
95 private void render(Writer writer, Iterable<Part> parts) {
96 for (Part part : parts) {
101 private void render(Writer writer, Part part) {
102 if (part instanceof PlainTextPart) {
103 render(writer, (PlainTextPart) part);
104 } else if (part instanceof FreenetLinkPart) {
105 render(writer, (FreenetLinkPart) part);
106 } else if (part instanceof LinkPart) {
107 render(writer, (LinkPart) part);
108 } else if (part instanceof SonePart) {
109 render(writer, (SonePart) part);
110 } else if (part instanceof PostPart) {
111 render(writer, (PostPart) part);
112 } else if (part instanceof AlbumPart) {
113 render(writer, (AlbumPart) part);
114 } else if (part instanceof Iterable<?>) {
115 render(writer, (Iterable<Part>) part);
119 private void render(Writer writer, PlainTextPart plainTextPart) {
120 TemplateContext templateContext = templateContextFactory.createTemplateContext();
121 templateContext.set("text", plainTextPart.getText());
122 plainTextTemplate.render(templateContext, writer);
125 private void render(Writer writer, FreenetLinkPart freenetLinkPart) {
126 renderLink(writer, "/" + freenetLinkPart.getLink(), freenetLinkPart.getText(), freenetLinkPart.getTitle(),
127 freenetLinkPart.isTrusted() ? "freenet-trusted" : "freenet");
130 private void render(Writer writer, LinkPart linkPart) {
132 renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(),
133 linkPart.getTitle(), "internet");
134 } catch (UnsupportedEncodingException uee1) {
135 /* not possible for UTF-8. */
136 throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1);
140 private void render(Writer writer, SonePart sonePart) {
141 Sone sone = sonePart.getSone();
142 if ((sone != null) && (sone.getName() != null)) {
143 String niceName = SoneAccessor.getNiceName(sone);
144 renderLink(writer, "viewSone.html?sone=" + sone.getId(), niceName, niceName, "in-sone");
146 renderLink(writer, "/WebOfTrust/ShowIdentity?id=" + sone.getId(), sone.getId(), sone.getId(), "in-sone");
150 private void render(Writer writer, PostPart postPart) {
151 SoneTextParser parser = new SoneTextParser(core, core, core);
152 SoneTextParserContext parserContext = new SoneTextParserContext(null, postPart.getPost().getSone());
154 Iterable<Part> parts = parser.parse(parserContext, new StringReader(postPart.getPost().getText()));
155 StringBuilder excerpt = new StringBuilder();
156 for (Part part : parts) {
157 excerpt.append(part.getText());
158 if (excerpt.length() > 20) {
159 int lastSpace = excerpt.lastIndexOf(" ", 20);
160 if (lastSpace > -1) {
161 excerpt.setLength(lastSpace);
163 excerpt.setLength(20);
169 renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), excerpt.toString(),
170 SoneAccessor.getNiceName(postPart.getPost().getSone()), postPart.usesDeprecatedLink() ? "internet" : "in-sone");
171 } catch (IOException ioe1) {
172 /* StringReader shouldn’t throw. */
176 private void render(Writer writer, AlbumPart albumPart) {
177 Album album = albumPart.getAlbum();
178 renderLink(writer, String.format("imageBrowser.html?album=%s", album.getId()), album.getTitle(), album.getDescription(), "in-sone");
181 private void renderLink(Writer writer, String link, String text, String title, String cssClass) {
182 TemplateContext templateContext = templateContextFactory.createTemplateContext();
183 templateContext.set("cssClass", cssClass);
184 templateContext.set("link", link);
185 templateContext.set("text", text);
186 templateContext.set("title", title);
187 linkTemplate.render(templateContext, writer);