/sitebricks/src/main/java/com/google/sitebricks/rendering/control/DecorateWidget.java

http://github.com/dhanji/sitebricks · Java · 96 lines · 67 code · 16 blank · 13 comment · 14 complexity · fcb072571f42e65c700066a90b398f8b MD5 · raw file

  1. package com.google.sitebricks.rendering.control;
  2. import com.google.inject.Inject;
  3. import com.google.sitebricks.Evaluator;
  4. import com.google.sitebricks.Renderable;
  5. import com.google.sitebricks.Respond;
  6. import com.google.sitebricks.StringBuilderRespond;
  7. import com.google.sitebricks.rendering.Decorated;
  8. import com.google.sitebricks.routing.PageBook;
  9. import java.util.Collections;
  10. import java.util.Set;
  11. /**
  12. * @author John Patterson (jdpatterson@gmail.com)
  13. */
  14. public class DecorateWidget implements Renderable {
  15. @Inject
  16. private PageBook book;
  17. private ThreadLocal<Class<?>> templateClassLocal = new ThreadLocal<Class<?>>();
  18. public static String embedNameFor(Class<?> pageClass) {
  19. return pageClass.getName().toLowerCase() + "-extend";
  20. }
  21. public DecorateWidget(WidgetChain chain, String expression, Evaluator evaluator) {
  22. // do not need any of the compulsory constructor args
  23. }
  24. @Override
  25. public void render(Object bound, Respond respond) {
  26. Class<?> templateClass;
  27. Class<?> previousTemplateClass = templateClassLocal.get();
  28. try {
  29. if (previousTemplateClass == null) {
  30. templateClass = nextDecoratedClassInHierarchy(null, bound.getClass());
  31. } else {
  32. // get the extension subclass above the last
  33. templateClass = nextDecoratedClassInHierarchy(previousTemplateClass, bound.getClass());
  34. if (templateClass == null) {
  35. throw new IllegalStateException(
  36. "Could not find subclass of " + previousTemplateClass.getName() +
  37. " with @Decorated annotation.");
  38. }
  39. }
  40. templateClassLocal.set(templateClass);
  41. // get the extension page by name
  42. PageBook.Page page = book.forName(DecorateWidget.embedNameFor(templateClass));
  43. // create a dummy respond to collect the output of the embedded page
  44. StringBuilderRespond sbrespond = new StringBuilderRespond(bound);
  45. EmbeddedRespond embedded = new EmbeddedRespond(null, sbrespond);
  46. page.widget().render(bound, embedded);
  47. // write the head and content to the real respond
  48. respond.writeToHead(embedded.toHeadString());
  49. respond.write(embedded.toString());
  50. // free some memory
  51. embedded.clear();
  52. } finally {
  53. // we are finished with this extension
  54. if (previousTemplateClass == null) {
  55. templateClassLocal.set(null);
  56. }
  57. }
  58. }
  59. // recursively find the next superclass with an @Decorated annotation
  60. private Class<?> nextDecoratedClassInHierarchy(Class<?> previousTemplateClass,
  61. Class<?> candidate) {
  62. if (candidate == previousTemplateClass) {
  63. // terminate the recursion
  64. return null;
  65. } else if (candidate == Object.class) {
  66. // this should never happen - we should terminate recursion first
  67. throw new IllegalStateException("Did not find previous extension");
  68. } else {
  69. boolean isDecorated = candidate.isAnnotationPresent(Decorated.class);
  70. if (isDecorated)
  71. return candidate;
  72. else
  73. return nextDecoratedClassInHierarchy(previousTemplateClass, candidate.getSuperclass());
  74. }
  75. }
  76. @Override
  77. public <T extends Renderable> Set<T> collect(Class<T> clazz) {
  78. return Collections.emptySet();
  79. }
  80. }