Add possibility to load templates from the filesystem
[Sone.git] / src / main / java / net / pterodactylus / sone / template / FilesystemTemplate.java
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);
+               }
+
+       }
+
+}