Add possibility to load templates from the filesystem
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 27 Jul 2015 17:14:39 +0000 (19:14 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 27 Jul 2015 17:14:39 +0000 (19:14 +0200)
src/main/java/net/pterodactylus/sone/main/DebugLoaders.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/main/Loaders.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/template/FilesystemTemplate.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/template/FilesystemTemplateTest.java [new file with mode: 0644]
src/test/resources/net/pterodactylus/sone/main/template.txt [new file with mode: 0644]

diff --git a/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java b/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java
new file mode 100644 (file)
index 0000000..1daa18a
--- /dev/null
@@ -0,0 +1,26 @@
+package net.pterodactylus.sone.main;
+
+import java.io.File;
+
+import net.pterodactylus.sone.template.FilesystemTemplate;
+import net.pterodactylus.util.template.Template;
+
+/**
+ * {@link Loaders} implementation that loads all resources from the filesystem.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DebugLoaders implements Loaders {
+
+       private final String filesystemPath;
+
+       public DebugLoaders(String filesystemPath) {
+               this.filesystemPath = filesystemPath;
+       }
+
+       @Override
+       public Template loadTemplate(String path) {
+               return new FilesystemTemplate(new File(filesystemPath, path).getAbsolutePath());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java
new file mode 100644 (file)
index 0000000..39a289d
--- /dev/null
@@ -0,0 +1,36 @@
+package net.pterodactylus.sone.main;
+
+import static net.pterodactylus.util.template.TemplateParser.parse;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.template.Template;
+
+/**
+ * Default {@link Loaders} implementation that loads resources from the classpath.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DefaultLoaders implements Loaders {
+
+       @Override
+       public Template loadTemplate(String path) {
+               InputStream templateInputStream = null;
+               Reader reader = null;
+               try {
+                       templateInputStream = getClass().getResourceAsStream(path);
+                       reader = new InputStreamReader(templateInputStream, "UTF-8");
+                       return parse(reader);
+               } catch (UnsupportedEncodingException uee1) {
+                       throw new RuntimeException("UTF-8 not supported.");
+               } finally {
+                       Closer.close(reader);
+                       Closer.close(templateInputStream);
+               }
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/main/Loaders.java b/src/main/java/net/pterodactylus/sone/main/Loaders.java
new file mode 100644 (file)
index 0000000..73f8ec6
--- /dev/null
@@ -0,0 +1,17 @@
+package net.pterodactylus.sone.main;
+
+import net.pterodactylus.util.template.Template;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Defines loaders for resources that can be loaded from various locations.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+@ImplementedBy(DefaultLoaders.class)
+public interface Loaders {
+
+       Template loadTemplate(String path);
+
+}
index ffaad9f..cbdca34 100644 (file)
@@ -250,6 +250,12 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                                bind(Context.class).toInstance(context);
                                bind(getOptionalContextTypeLiteral()).toInstance(of(context));
                                bind(SonePlugin.class).toInstance(SonePlugin.this);
+                               if (startConfiguration.getBooleanValue("Developer.LoadFromFilesystem").getValue(false)) {
+                                       String path = startConfiguration.getStringValue("Developer.FilesystemPath").getValue(null);
+                                       if (path != null) {
+                                               bind(Loaders.class).toInstance(new DebugLoaders(path));
+                                       }
+                               }
                                bindListener(Matchers.any(), new TypeListener() {
 
                                        @Override
diff --git a/src/main/java/net/pterodactylus/sone/template/FilesystemTemplate.java b/src/main/java/net/pterodactylus/sone/template/FilesystemTemplate.java
new file mode 100644 (file)
index 0000000..473c191
--- /dev/null
@@ -0,0 +1,145 @@
+package net.pterodactylus.sone.template;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import net.pterodactylus.util.template.Part;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateException;
+import net.pterodactylus.util.template.TemplateParser;
+
+import freenet.support.io.Closer;
+
+import com.google.common.base.Charsets;
+
+/**
+ * {@link Template} implementation that can be reloaded from the filesystem.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FilesystemTemplate extends Template {
+
+       private final String filename;
+       private final AtomicReference<LastLoadedTemplate> lastTemplate = new AtomicReference<LastLoadedTemplate>();
+       private final TemplateContext initialContext = new TemplateContext();
+       private final List<Part> parts = new ArrayList<Part>();
+
+       public FilesystemTemplate(String filename) {
+               this.filename = filename;
+       }
+
+       @Override
+       public TemplateContext getInitialContext() {
+               loadTemplate();
+               return initialContext;
+       }
+
+       private void loadTemplate() {
+               File templateFile = new File(filename);
+               if (!templateFile.exists()) {
+                       throw new TemplateFileNotFoundException(filename);
+               }
+               if (templateWasLoaded() && !templateFileHasBeenModifiedAfterLoading(templateFile)) {
+                       return;
+               }
+               InputStream templateInputStream = null;
+               Reader templateReader = null;
+               try {
+                       templateInputStream = new FileInputStream(templateFile);
+                       templateReader = new InputStreamReader(templateInputStream, Charsets.UTF_8);
+                       Template template = TemplateParser.parse(templateReader);
+                       lastTemplate.set(new LastLoadedTemplate(template));
+                       template.getInitialContext().mergeContext(initialContext);
+                       for (Part part : parts) {
+                               template.add(part);
+                       }
+               } catch (FileNotFoundException e) {
+                       throw new TemplateFileNotFoundException(filename);
+               } finally {
+                       Closer.close(templateReader);
+                       Closer.close(templateInputStream);
+               }
+       }
+
+       private boolean templateWasLoaded() {
+               return lastTemplate.get() != null;
+       }
+
+       private boolean templateFileHasBeenModifiedAfterLoading(File templateFile) {
+               return templateFile.lastModified() > lastTemplate.get().getLoadTime();
+       }
+
+       @Override
+       public void add(Part part) {
+               loadTemplate();
+               parts.add(part);
+               lastTemplate.get().getTemplate().add(part);
+       }
+
+       @Override
+       public void render(TemplateContext templateContext, Writer writer) throws TemplateException {
+               loadTemplate();
+               lastTemplate.get().getTemplate().render(templateContext, writer);
+       }
+
+       @Override
+       public Iterator<Part> iterator() {
+               loadTemplate();
+               return lastTemplate.get().getTemplate().iterator();
+       }
+
+       @Override
+       public int getLine() {
+               loadTemplate();
+               return lastTemplate.get().getTemplate().getLine();
+       }
+
+       @Override
+       public int getColumn() {
+               loadTemplate();
+               return lastTemplate.get().getTemplate().getColumn();
+       }
+
+       private static class LastLoadedTemplate {
+
+               private final Template template;
+               private final long loadTime = System.currentTimeMillis();
+
+               private LastLoadedTemplate(Template template) {
+                       this.template = template;
+               }
+
+               public Template getTemplate() {
+                       return template;
+               }
+
+               public long getLoadTime() {
+                       return loadTime;
+               }
+
+       }
+
+       /**
+        * Exception that signals that a template file could not be found.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public static class TemplateFileNotFoundException extends RuntimeException {
+
+               public TemplateFileNotFoundException(String filename) {
+                       super(filename);
+               }
+
+       }
+
+}
index fbba8e0..a3f3352 100644 (file)
@@ -21,11 +21,7 @@ import static java.util.logging.Logger.getLogger;
 import static net.pterodactylus.util.template.TemplateParser.parse;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
 import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -72,6 +68,7 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.freenet.L10nFilter;
 import net.pterodactylus.sone.freenet.wot.Identity;
 import net.pterodactylus.sone.freenet.wot.Trust;
+import net.pterodactylus.sone.main.Loaders;
 import net.pterodactylus.sone.main.ReparseFilter;
 import net.pterodactylus.sone.main.SonePlugin;
 import net.pterodactylus.sone.notify.ListNotification;
@@ -129,7 +126,6 @@ import net.pterodactylus.sone.web.ajax.UntrustAjaxPage;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.sone.web.page.PageToadlet;
 import net.pterodactylus.sone.web.page.PageToadletFactory;
-import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.notify.Notification;
 import net.pterodactylus.util.notify.NotificationManager;
 import net.pterodactylus.util.notify.TemplateNotification;
@@ -176,6 +172,9 @@ public class WebInterface {
        /** The logger. */
        private static final Logger logger = getLogger(WebInterface.class.getName());
 
+       /** The loaders for templates, pages, and classpath providers. */
+       private final Loaders loaders;
+
        /** The notification manager. */
        private final NotificationManager notificationManager = new NotificationManager();
 
@@ -246,8 +245,9 @@ public class WebInterface {
         *            The Sone plugin
         */
        @Inject
-       public WebInterface(SonePlugin sonePlugin) {
+       public WebInterface(SonePlugin sonePlugin, Loaders loaders) {
                this.sonePlugin = sonePlugin;
+               this.loaders = loaders;
                formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
                soneTextParser = new SoneTextParser(getCore(), getCore());
 
@@ -291,55 +291,40 @@ public class WebInterface {
                templateContextFactory.addTemplateObject("formPassword", formPassword);
 
                /* create notifications. */
-               Template newSoneNotificationTemplate = parseTemplate("/templates/notify/newSoneNotification.html");
+               Template newSoneNotificationTemplate = loaders.loadTemplate("/templates/notify/newSoneNotification.html");
                newSoneNotification = new ListNotification<Sone>("new-sone-notification", "sones", newSoneNotificationTemplate, false);
 
-               Template newPostNotificationTemplate = parseTemplate("/templates/notify/newPostNotification.html");
+               Template newPostNotificationTemplate = loaders.loadTemplate("/templates/notify/newPostNotification.html");
                newPostNotification = new ListNotification<Post>("new-post-notification", "posts", newPostNotificationTemplate, false);
 
-               Template localPostNotificationTemplate = parseTemplate("/templates/notify/newPostNotification.html");
+               Template localPostNotificationTemplate = loaders.loadTemplate("/templates/notify/newPostNotification.html");
                localPostNotification = new ListNotification<Post>("local-post-notification", "posts", localPostNotificationTemplate, false);
 
-               Template newReplyNotificationTemplate = parseTemplate("/templates/notify/newReplyNotification.html");
+               Template newReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
                newReplyNotification = new ListNotification<PostReply>("new-reply-notification", "replies", newReplyNotificationTemplate, false);
 
-               Template localReplyNotificationTemplate = parseTemplate("/templates/notify/newReplyNotification.html");
+               Template localReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html");
                localReplyNotification = new ListNotification<PostReply>("local-reply-notification", "replies", localReplyNotificationTemplate, false);
 
-               Template mentionNotificationTemplate = parseTemplate("/templates/notify/mentionNotification.html");
+               Template mentionNotificationTemplate = loaders.loadTemplate("/templates/notify/mentionNotification.html");
                mentionNotification = new ListNotification<Post>("mention-notification", "posts", mentionNotificationTemplate, false);
 
-               Template lockedSonesTemplate = parseTemplate("/templates/notify/lockedSonesNotification.html");
+               Template lockedSonesTemplate = loaders.loadTemplate("/templates/notify/lockedSonesNotification.html");
                lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
 
-               Template newVersionTemplate = parseTemplate("/templates/notify/newVersionNotification.html");
+               Template newVersionTemplate = loaders.loadTemplate("/templates/notify/newVersionNotification.html");
                newVersionNotification = new TemplateNotification("new-version-notification", newVersionTemplate);
 
-               Template insertingImagesTemplate = parseTemplate("/templates/notify/inserting-images-notification.html");
+               Template insertingImagesTemplate = loaders.loadTemplate("/templates/notify/inserting-images-notification.html");
                insertingImagesNotification = new ListNotification<Image>("inserting-images-notification", "images", insertingImagesTemplate);
 
-               Template insertedImagesTemplate = parseTemplate("/templates/notify/inserted-images-notification.html");
+               Template insertedImagesTemplate = loaders.loadTemplate("/templates/notify/inserted-images-notification.html");
                insertedImagesNotification = new ListNotification<Image>("inserted-images-notification", "images", insertedImagesTemplate);
 
-               Template imageInsertFailedTemplate = parseTemplate("/templates/notify/image-insert-failed-notification.html");
+               Template imageInsertFailedTemplate = loaders.loadTemplate("/templates/notify/image-insert-failed-notification.html");
                imageInsertFailedNotification = new ListNotification<Image>("image-insert-failed-notification", "images", imageInsertFailedTemplate);
        }
 
-       private Template parseTemplate(String resourceName) {
-               InputStream templateInputStream = null;
-               Reader reader = null;
-               try {
-                       templateInputStream = getClass().getResourceAsStream(resourceName);
-                       reader = new InputStreamReader(templateInputStream, "UTF-8");
-                       return parse(reader);
-               } catch (UnsupportedEncodingException uee1) {
-                       throw new RuntimeException("UTF-8 not supported.");
-               } finally {
-                       Closer.close(reader);
-                       Closer.close(templateInputStream);
-               }
-       }
-
        //
        // ACCESSORS
        //
@@ -528,7 +513,7 @@ public class WebInterface {
         */
        public void setFirstStart(boolean firstStart) {
                if (firstStart) {
-                       Template firstStartNotificationTemplate = parseTemplate("/templates/notify/firstStartNotification.html");
+                       Template firstStartNotificationTemplate = loaders.loadTemplate("/templates/notify/firstStartNotification.html");
                        Notification firstStartNotification = new TemplateNotification("first-start-notification", firstStartNotificationTemplate);
                        notificationManager.addNotification(firstStartNotification);
                }
@@ -543,7 +528,7 @@ public class WebInterface {
         */
        public void setNewConfig(boolean newConfig) {
                if (newConfig && !hasFirstStartNotification()) {
-                       Template configNotReadNotificationTemplate = parseTemplate("/templates/notify/configNotReadNotification.html");
+                       Template configNotReadNotificationTemplate = loaders.loadTemplate("/templates/notify/configNotReadNotification.html");
                        Notification configNotReadNotification = new TemplateNotification("config-not-read-notification", configNotReadNotificationTemplate);
                        notificationManager.addNotification(configNotReadNotification);
                }
@@ -574,7 +559,7 @@ public class WebInterface {
                registerToadlets();
 
                /* notification templates. */
-               Template startupNotificationTemplate = parseTemplate("/templates/notify/startupNotification.html");
+               Template startupNotificationTemplate = loaders.loadTemplate("/templates/notify/startupNotification.html");
 
                final TemplateNotification startupNotification = new TemplateNotification("startup-notification", startupNotificationTemplate);
                notificationManager.addNotification(startupNotification);
@@ -587,7 +572,7 @@ public class WebInterface {
                        }
                }, 2, TimeUnit.MINUTES);
 
-               Template wotMissingNotificationTemplate = parseTemplate("/templates/notify/wotMissingNotification.html");
+               Template wotMissingNotificationTemplate = loaders.loadTemplate("/templates/notify/wotMissingNotification.html");
                final TemplateNotification wotMissingNotification = new TemplateNotification("wot-missing-notification", wotMissingNotificationTemplate);
                ticker.scheduleAtFixedRate(new Runnable() {
 
@@ -621,37 +606,37 @@ public class WebInterface {
         */
        private void registerToadlets() {
                Template emptyTemplate = parse(new StringReader(""));
-               Template loginTemplate = parseTemplate("/templates/login.html");
-               Template indexTemplate = parseTemplate("/templates/index.html");
-               Template newTemplate = parseTemplate("/templates/new.html");
-               Template knownSonesTemplate = parseTemplate("/templates/knownSones.html");
-               Template createSoneTemplate = parseTemplate("/templates/createSone.html");
-               Template createPostTemplate = parseTemplate("/templates/createPost.html");
-               Template createReplyTemplate = parseTemplate("/templates/createReply.html");
-               Template bookmarksTemplate = parseTemplate("/templates/bookmarks.html");
-               Template searchTemplate = parseTemplate("/templates/search.html");
-               Template editProfileTemplate = parseTemplate("/templates/editProfile.html");
-               Template editProfileFieldTemplate = parseTemplate("/templates/editProfileField.html");
-               Template deleteProfileFieldTemplate = parseTemplate("/templates/deleteProfileField.html");
-               Template viewSoneTemplate = parseTemplate("/templates/viewSone.html");
-               Template viewPostTemplate = parseTemplate("/templates/viewPost.html");
-               Template deletePostTemplate = parseTemplate("/templates/deletePost.html");
-               Template deleteReplyTemplate = parseTemplate("/templates/deleteReply.html");
-               Template deleteSoneTemplate = parseTemplate("/templates/deleteSone.html");
-               Template imageBrowserTemplate = parseTemplate("/templates/imageBrowser.html");
-               Template createAlbumTemplate = parseTemplate("/templates/createAlbum.html");
-               Template deleteAlbumTemplate = parseTemplate("/templates/deleteAlbum.html");
-               Template deleteImageTemplate = parseTemplate("/templates/deleteImage.html");
-               Template noPermissionTemplate = parseTemplate("/templates/noPermission.html");
-               Template emptyImageTitleTemplate = parseTemplate("/templates/emptyImageTitle.html");
-               Template emptyAlbumTitleTemplate = parseTemplate("/templates/emptyAlbumTitle.html");
-               Template optionsTemplate = parseTemplate("/templates/options.html");
-               Template rescueTemplate = parseTemplate("/templates/rescue.html");
-               Template aboutTemplate = parseTemplate("/templates/about.html");
-               Template invalidTemplate = parseTemplate("/templates/invalid.html");
-               Template postTemplate = parseTemplate("/templates/include/viewPost.html");
-               Template replyTemplate = parseTemplate("/templates/include/viewReply.html");
-               Template openSearchTemplate = parseTemplate("/templates/xml/OpenSearch.xml");
+               Template loginTemplate = loaders.loadTemplate("/templates/login.html");
+               Template indexTemplate = loaders.loadTemplate("/templates/index.html");
+               Template newTemplate = loaders.loadTemplate("/templates/new.html");
+               Template knownSonesTemplate = loaders.loadTemplate("/templates/knownSones.html");
+               Template createSoneTemplate = loaders.loadTemplate("/templates/createSone.html");
+               Template createPostTemplate = loaders.loadTemplate("/templates/createPost.html");
+               Template createReplyTemplate = loaders.loadTemplate("/templates/createReply.html");
+               Template bookmarksTemplate = loaders.loadTemplate("/templates/bookmarks.html");
+               Template searchTemplate = loaders.loadTemplate("/templates/search.html");
+               Template editProfileTemplate = loaders.loadTemplate("/templates/editProfile.html");
+               Template editProfileFieldTemplate = loaders.loadTemplate("/templates/editProfileField.html");
+               Template deleteProfileFieldTemplate = loaders.loadTemplate("/templates/deleteProfileField.html");
+               Template viewSoneTemplate = loaders.loadTemplate("/templates/viewSone.html");
+               Template viewPostTemplate = loaders.loadTemplate("/templates/viewPost.html");
+               Template deletePostTemplate = loaders.loadTemplate("/templates/deletePost.html");
+               Template deleteReplyTemplate = loaders.loadTemplate("/templates/deleteReply.html");
+               Template deleteSoneTemplate = loaders.loadTemplate("/templates/deleteSone.html");
+               Template imageBrowserTemplate = loaders.loadTemplate("/templates/imageBrowser.html");
+               Template createAlbumTemplate = loaders.loadTemplate("/templates/createAlbum.html");
+               Template deleteAlbumTemplate = loaders.loadTemplate("/templates/deleteAlbum.html");
+               Template deleteImageTemplate = loaders.loadTemplate("/templates/deleteImage.html");
+               Template noPermissionTemplate = loaders.loadTemplate("/templates/noPermission.html");
+               Template emptyImageTitleTemplate = loaders.loadTemplate("/templates/emptyImageTitle.html");
+               Template emptyAlbumTitleTemplate = loaders.loadTemplate("/templates/emptyAlbumTitle.html");
+               Template optionsTemplate = loaders.loadTemplate("/templates/options.html");
+               Template rescueTemplate = loaders.loadTemplate("/templates/rescue.html");
+               Template aboutTemplate = loaders.loadTemplate("/templates/about.html");
+               Template invalidTemplate = loaders.loadTemplate("/templates/invalid.html");
+               Template postTemplate = loaders.loadTemplate("/templates/include/viewPost.html");
+               Template replyTemplate = loaders.loadTemplate("/templates/include/viewReply.html");
+               Template openSearchTemplate = loaders.loadTemplate("/templates/xml/OpenSearch.xml");
 
                PageToadletFactory pageToadletFactory = new PageToadletFactory(sonePlugin.pluginRespirator().getHLSimpleClient(), "/Sone/");
                pageToadlets.add(pageToadletFactory.createPageToadlet(new RedirectPage<FreenetRequest>("", "index.html")));
@@ -795,7 +780,7 @@ public class WebInterface {
                synchronized (soneInsertNotifications) {
                        TemplateNotification templateNotification = soneInsertNotifications.get(sone);
                        if (templateNotification == null) {
-                               templateNotification = new TemplateNotification(parseTemplate("/templates/notify/soneInsertNotification.html"));
+                               templateNotification = new TemplateNotification(loaders.loadTemplate("/templates/notify/soneInsertNotification.html"));
                                templateNotification.set("insertSone", sone);
                                soneInsertNotifications.put(sone, templateNotification);
                        }
diff --git a/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java b/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java
new file mode 100644 (file)
index 0000000..81a85a5
--- /dev/null
@@ -0,0 +1,50 @@
+package net.pterodactylus.sone.main;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Unit test for {@link DebugLoaders}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DebugLoadersTest {
+
+       @Rule
+       public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+       private final StringWriter stringWriter = new StringWriter();
+       private final TemplateContext templateContext = new TemplateContext();
+       private String templatePath;
+       private Loaders loaders;
+
+       @Before
+       public void setupLoader() throws IOException {
+               templatePath = temporaryFolder.newFolder("temps").getPath();
+               loaders = new DebugLoaders(templatePath);
+       }
+
+       @Test
+       public void debugLoaderCanLoadTemplatesFromFilesystem() throws IOException {
+               File templateFile = new File(templatePath, "template.txt");
+               Files.write("<%if foo>foo<%else>bar<%/if>", templateFile, Charsets.UTF_8);
+               Template template = loaders.loadTemplate("/template.txt");
+               template.render(templateContext, stringWriter);
+               assertThat(stringWriter.toString(), is("bar"));
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java b/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java
new file mode 100644 (file)
index 0000000..220996d
--- /dev/null
@@ -0,0 +1,31 @@
+package net.pterodactylus.sone.main;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.io.StringWriter;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link DefaultLoaders}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DefaultLoadersTest {
+
+       private final Loaders loaders = new DefaultLoaders();
+       private final StringWriter stringWriter = new StringWriter();
+       private final TemplateContext templateContext = new TemplateContext();
+
+       @Test
+       public void templateCanBeLoadedFromTheClasspath() {
+               Template template = loaders.loadTemplate("/net/pterodactylus/sone/main/template.txt");
+               template.render(templateContext, stringWriter);
+               assertThat(stringWriter.toString(), is("Template. bar\n"));
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/template/FilesystemTemplateTest.java b/src/test/java/net/pterodactylus/sone/template/FilesystemTemplateTest.java
new file mode 100644 (file)
index 0000000..cd989eb
--- /dev/null
@@ -0,0 +1,111 @@
+package net.pterodactylus.sone.template;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
+
+import net.pterodactylus.util.template.Part;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateException;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link FilesystemTemplate}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FilesystemTemplateTest {
+
+       private final File tempFile;
+       private final FilesystemTemplate filesystemTemplate;
+       private final AtomicReference<StringWriter> stringWriter = new AtomicReference<StringWriter>(new StringWriter());
+       private final TemplateContext templateContext = new TemplateContext();
+
+       public FilesystemTemplateTest() throws IOException {
+               tempFile = File.createTempFile("template-", ".dat");
+               writeTemplate("Text");
+               filesystemTemplate = new FilesystemTemplate(tempFile.getAbsolutePath());
+       }
+
+       private void writeTemplate(String text) throws IOException {
+               Files.write(text + ".<%foreach values value><% value><%/foreach>", tempFile, Charsets.UTF_8);
+       }
+
+       @Before
+       public void setupTemplateContext() {
+               templateContext.set("values", Arrays.asList("a", 1));
+       }
+
+       @Test(expected = FilesystemTemplate.TemplateFileNotFoundException.class)
+       public void loadingTemplateFromNonExistingFileThrowsException() throws IOException {
+               FilesystemTemplate filesystemTemplate = new FilesystemTemplate("/a/b/c.dat");
+               filesystemTemplate.getInitialContext();
+       }
+
+       @Test
+       public void templateCanBeLoadedFromTheFilesystem() {
+               filesystemTemplate.render(templateContext, stringWriter.get());
+               assertThat(getRenderedString(), is("Text.a1"));
+       }
+
+       @Test
+       public void templateCanBeReloaded() throws IOException, InterruptedException {
+               filesystemTemplate.render(templateContext, stringWriter.get());
+               assertThat(getRenderedString(), is("Text.a1"));
+               Thread.sleep(1000);
+               writeTemplate("New");
+               filesystemTemplate.render(templateContext, stringWriter.get());
+               assertThat(getRenderedString(), is("New.a1"));
+       }
+
+       @Test
+       public void templateIsNotReloadedIfNotChanged() {
+               filesystemTemplate.render(templateContext, stringWriter.get());
+               assertThat(getRenderedString(), is("Text.a1"));
+               filesystemTemplate.render(templateContext, stringWriter.get());
+               assertThat(getRenderedString(), is("Text.a1"));
+       }
+
+       private String getRenderedString() {
+               String renderedString = stringWriter.get().toString();
+               stringWriter.set(new StringWriter());
+               return renderedString;
+       }
+
+       @Test
+       public void initialContextIsCopiedToReloadedTemplates() throws IOException, InterruptedException {
+               filesystemTemplate.getInitialContext().set("values", "test");
+               Thread.sleep(1000);
+               writeTemplate("New");
+               assertThat(filesystemTemplate.getInitialContext().get("values"), is((Object) "test"));
+       }
+
+       @Test
+       public void partsAreCopiedToReloadedTemplates() throws InterruptedException, IOException {
+               filesystemTemplate.add(new Part() {
+                       @Override
+                       public void render(TemplateContext templateContext, Writer writer) throws TemplateException {
+                               try {
+                                       writer.write(".Test");
+                               } catch (IOException e) {
+                                       throw new TemplateException(e);
+                               }
+                       }
+               });
+               Thread.sleep(1000);
+               writeTemplate("New");
+               filesystemTemplate.render(templateContext, stringWriter.get());
+               assertThat(getRenderedString(), is("New.a1.Test"));
+       }
+
+}
diff --git a/src/test/resources/net/pterodactylus/sone/main/template.txt b/src/test/resources/net/pterodactylus/sone/main/template.txt
new file mode 100644 (file)
index 0000000..c2377d2
--- /dev/null
@@ -0,0 +1 @@
+Template. <%if foo>foo<%else>bar<%/if>