package com.google.sitebricks.rendering.control; import com.google.inject.Inject; import com.google.sitebricks.Evaluator; import com.google.sitebricks.Renderable; import com.google.sitebricks.Respond; import com.google.sitebricks.StringBuilderRespond; import com.google.sitebricks.rendering.Decorated; import com.google.sitebricks.routing.PageBook; import java.util.Collections; import java.util.Set; /** * @author John Patterson (jdpatterson@gmail.com) */ public class DecorateWidget implements Renderable { @Inject private PageBook book; private ThreadLocal<Class<?>> templateClassLocal = new ThreadLocal<Class<?>>(); public static String embedNameFor(Class<?> pageClass) { return pageClass.getName().toLowerCase() + "-extend"; } public DecorateWidget(WidgetChain chain, String expression, Evaluator evaluator) { // do not need any of the compulsory constructor args } @Override public void render(Object bound, Respond respond) { Class<?> templateClass; Class<?> previousTemplateClass = templateClassLocal.get(); try { if (previousTemplateClass == null) { templateClass = nextDecoratedClassInHierarchy(null, bound.getClass()); } else { // get the extension subclass above the last templateClass = nextDecoratedClassInHierarchy(previousTemplateClass, bound.getClass()); if (templateClass == null) { throw new IllegalStateException( "Could not find subclass of " + previousTemplateClass.getName() + " with @Decorated annotation."); } } templateClassLocal.set(templateClass); // get the extension page by name PageBook.Page page = book.forName(DecorateWidget.embedNameFor(templateClass)); // create a dummy respond to collect the output of the embedded page StringBuilderRespond sbrespond = new StringBuilderRespond(bound); EmbeddedRespond embedded = new EmbeddedRespond(null, sbrespond); page.widget().render(bound, embedded); // write the head and content to the real respond respond.writeToHead(embedded.toHeadString()); respond.write(embedded.toString()); // free some memory embedded.clear(); } finally { // we are finished with this extension if (previousTemplateClass == null) { templateClassLocal.set(null); } } } // recursively find the next superclass with an @Decorated annotation private Class<?> nextDecoratedClassInHierarchy(Class<?> previousTemplateClass, Class<?> candidate) { if (candidate == previousTemplateClass) { // terminate the recursion return null; } else if (candidate == Object.class) { // this should never happen - we should terminate recursion first throw new IllegalStateException("Did not find previous extension"); } else { boolean isDecorated = candidate.isAnnotationPresent(Decorated.class); if (isDecorated) return candidate; else return nextDecoratedClassInHierarchy(previousTemplateClass, candidate.getSuperclass()); } } @Override public <T extends Renderable> Set<T> collect(Class<T> clazz) { return Collections.emptySet(); } }