package com.google.sitebricks; import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import com.google.common.collect.Lists; import net.jcip.annotations.NotThreadSafe; /** * @author Dhanji R. Prasanna (dhanji@gmail.com) */ @NotThreadSafe public class StringBuilderRespond implements Respond { private static final String TEXT_TAG_TEMPLATE = "sitebricks.template.textfield"; private static final String TEXTAREA_TAG_TEMPLATE = "sitebricks.template.textarea"; //TODO Improve performance by using an insertion index rather than a placeholder string private static final String HEADER_PLACEHOLDER = "__sb:PLACEhOlDeR:__"; private static final AtomicReference<Map<String, String>> templates = new AtomicReference<Map<String, String>>(); private static final String TEXT_HTML = "text/html;charset=utf-8"; private Object page; private List<String> errors; @SuppressWarnings("unchecked") public StringBuilderRespond(Object context) { this.page = context; if (null == templates.get()) { final Properties properties = new Properties(); try { properties.load(StringBuilderRespond.class.getResourceAsStream("templates.properties")); } catch (IOException e) { throw new NoSuchResourceException("Can't find templates.properties", e); } //Concurrent/idempotent templates.compareAndSet(null, (Map) properties); } } private final StringBuilder out = new StringBuilder(); private final StringBuilder head = new StringBuilder(); private final Set<String> requires = new LinkedHashSet<String>(); private String redirect; public String getHead() { return head.toString(); } public void write(String text) { out.append(text); } public HtmlTagBuilder withHtml() { return new HtmlBuilder(); } public void write(char c) { out.append(c); } public void require(String require) { requires.add(require); } public void redirect(String to) { this.redirect = to; } public void writeToHead(String text) { head.append(text); } public void chew() { out.deleteCharAt(out.length() - 1); } public String getRedirect() { return redirect; } public Renderable include(String argument) { return null; } public String getContentType() { return TEXT_HTML; } public void clear() { if (null != out) { out.delete(0, out.length()); } if (null != head) { head.delete(0, head.length()); } } @Override public Object pageObject() { return page; } @Override public List<String> getErrors() { if (this.errors == null) { this.errors = Lists.newArrayList(); } return this.errors; } @Override public void setErrors(List<String> errors) { this.errors = errors; } @Override public String toString() { //write requires to header first... for (String require : requires) { writeToHead(require); } //write header to placeholder... //TODO optimize by scanning upto <body> only (if no head) int index = out.indexOf(HEADER_PLACEHOLDER); String output = out.toString(); if (index > 0) { output = output.replaceFirst(HEADER_PLACEHOLDER, head.toString()); } return output; } //do NOT make this a static inner class! private class HtmlBuilder implements HtmlTagBuilder { public void textField(String bind, String value) { write(String.format(templates.get().get(TEXT_TAG_TEMPLATE), bind, value)); } public void headerPlaceholder() { write(HEADER_PLACEHOLDER); } public void textArea(String bind, String value) { write(String.format(templates.get().get(TEXTAREA_TAG_TEMPLATE), bind, value)); } } }