package com.google.sitebricks.compiler; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Map; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.sitebricks.Bricks; import com.google.sitebricks.MissingTemplateException; import com.google.sitebricks.Renderable; import com.google.sitebricks.Show; import com.google.sitebricks.TemplateLoader; import com.google.sitebricks.headless.Reply; import com.google.sitebricks.rendering.Decorated; import com.google.sitebricks.routing.PageBook; /** * A factory for internal template compilers. * * @author Dhanji R. Prasanna (dhanji@gmail com) */ @Singleton class StandardCompilers implements Compilers { private final PageBook pageBook; private final Map<String, Class<? extends Annotation>> httpMethods; private final TemplateLoader loader; @Inject public StandardCompilers(PageBook pageBook, @Bricks Map<String, Class<? extends Annotation>> httpMethods, TemplateLoader loader) { this.pageBook = pageBook; this.httpMethods = httpMethods; this.loader = loader; } // TODO(dhanji): Feedback errors as return rather than throwing. public void analyze(Class<?> page) { // // May move this into a separate class if it starts getting too big. // analyzeMethods(page.getDeclaredMethods()); analyzeMethods(page.getMethods()); } private void analyzeMethods(Method[] methods) { for (Method method : methods) { for (Annotation annotation : method.getDeclaredAnnotations()) { // // if this is a http method annotation, do some checking on the args and return types. // if (httpMethods.containsValue(annotation.annotationType())) { Class<?> returnType = method.getReturnType(); PageBook.Page page = pageBook.forClass(returnType); if (null == page) { // // throw an error. // } else { // // do further analysis on this sucka // if (page.getUri().contains(":")) { // // throw an error coz we cant redir to dynamic URLs // } // // If this is headless, it MUST return an instance of reply. // if (page.isHeadless()) { if (!Reply.class.isAssignableFrom(method.getReturnType())) { // throw error } } } } } } } public void compilePage(PageBook.Page page) { // // find the template page class // Class<?> templateClass = page.pageClass(); // // root page uses the last template, extension uses its own embedded template // if (!page.isDecorated() && templateClass.isAnnotationPresent(Decorated.class)) { // // the first superclass with a @Show and no @Extension is the template // while (!templateClass.isAnnotationPresent(Show.class) || templateClass.isAnnotationPresent(Decorated.class)) { templateClass = templateClass.getSuperclass(); if (templateClass == Object.class) { throw new MissingTemplateException("Could not find tempate for " + page.pageClass() + ". You must use @Show on a superclass of an @Extension page"); } } } // TODO(eric) this must be reviewed as we don't support @Show on methods combined with @Decorated pages. Renderable widget = null; if (templateClass.equals(page.pageClass())) { widget = loader.compile(page); } else { widget = compile(templateClass); } // //apply the compiled widget chain to the page (completing compile step) // page.apply(widget); } @Override public Renderable compile(Class<?> templateClass) { return loader.compile(templateClass); } }