Hi James, Thanks for getting back so quickly. I see two major areas of extensibility required: 1) Allow plugging in of custom themes 2) Allow for easy extensibility/re-use of existing themes (currently SimpleTheme or DefaultTheme)
1) We need a way of specifying custom themes to the JavaTemplateEngine via configuration. For now, the best I can come up with is a new struts configuration constant that takes a comma-delimited list of theme names as its value. So lets add something like the following constant to StrutsConstants: /** Custom theme classes for the javatemplates rendering engine */ public static final String STRUTS_JAVATEMPLATES_CUSTOM_THEMES = "struts.javatemplates.customThemes"; We can then specify in our struts.xml <constant name="struts.javatemplates.customThemes" value="com.example.ui.struts2.MyCustomJavaTheme" /> and add to the JavaTemplateEngine class /** * Allows for providing custom theme classes (implementations of the org.apache.struts2.views.java.Theme) interface * for custom rendering of tags using the javatemplates engine * @param themeClasses a comma delimited list of custom theme class names */ @Inject(StrutsConstants.STRUTS_JAVATEMPLATES_CUSTOM_THEMES) public void setThemeClasses(String themeClasses) { StringTokenizer customThemes = new StringTokenizer(themeClasses, ","); while (customThemes.hasMoreTokens()) { String themeClass = customThemes.nextToken().trim(); try { LOG.info("Registering custom theme '" + themeClass + "' to javatemplates engine"); //FIXME: This means Themes must have no-arg constructor - should use object factory here //ObjectFactory.getObjectFactory().buildBean(ClassLoaderUtil.loadClass(themeClass, getClass()), null); themes.add((Theme)ClassLoaderUtil.loadClass(themeClass, getClass()).newInstance()); } catch (ClassCastException cce) { LOG.error("Invalid java them class '" + themeClass + "'. Class does not implement 'org.apache.struts2.views.java.Theme' interface"); } catch (ClassNotFoundException cnf) { LOG.error("Invalid java theme class '" + themeClass + "'. Class not found"); } catch (Exception e) { LOG.error("Could not find messages file " + themeClass + ".properties. Skipping"); } } } For the custom theme implemetations, most will probably extend SimpleTheme/DefaultTheme and simply insert/remove component handlers in their desired sequence and perhaps provide a different (or additional) serializer. so it would be nice to: - lower the protection level on some of the methods/members to protected (eg. make DefaultTheme.handlerFactories protected, make FactoryList inner-class protected or a separate public class) - provide some utility methods. For example: Add to DefaultTheme class: /** * Set (replace if exists) the tag handler factories for specific tag * @param tagName * @param handlers */ protected void setTagHandlerFactories(String tagName, List<TagHandlerFactory> handlers) { if(tagName != null && handlers != null && this.handlerFactories != null) { handlerFactories.put(tagName,handlers); } } /** * Insert a new tag handler into a sequence of tag handlers for a specific tag * TODO: Need to take care of serializers, if handler specified is not a TagSerializer it should never * be placed after the serializer, but if it is not a TagSerializer, it should never * @param tagName * @param sequence * @param factory */ protected void insertTagHandlerFactory(String tagName, int sequence, TagHandlerFactory factory) { if(tagName != null && factory != null && this.handlerFactories != null) { List<TagHandlerFactory> tagHandlerFactories = handlerFactories.get(tagName); if(tagHandlerFactories == null) { tagHandlerFactories = new ArrayList<TagHandlerFactory>(); //TODO: Could use public FactoryList here } if(sequence > tagHandlerFactories.size()) { sequence = tagHandlerFactories.size(); } //TODO, need to account for TagHandlers vs. TagSerializers here tagHandlerFactories.add(sequence, factory); } } - Allow multiple TagSerializers (make sure framework supports chaining serizliers). For example, in my application I'd like to add a serializer which writes some javascript (think Ajax event binding) after - The way the serializers are registered simply as the last handler is neat, but it may cause some trouble when developers extend the themes and iject their own handlers, perhaps I like the chaining, so perhaps this should simply be accounted for carefully in the utiolity methods (such as the insertTagHandlerFactory() method i suggested above) and in the renderTag() method of the DefaultTheme. - DefaultTheme could probably be an abstract class and maybe called somehting like AbstractTheme or AbstractThemeSupport Incidentally, the reason I am looking into this is because I am starting to write a jQuery Ajax plugin (given the imminent deprecation of dojo, which I never really liked so much anyway - too large and clunky) I'm not sure how far I'll get on this, but I'm currently optimistic. Best, Eric On Wed, Feb 4, 2009 at 4:26 PM, James Holmes <ja...@jamesholmes.com> wrote: > I don't know if any plugging mechanism for themes has been discussed. > Please > send us your thoughts. > > On Wed, Feb 4, 2009 at 2:24 AM, Obinna <obi...@gmail.com> wrote: > > > Hi, > > > > I'd like to know if there's a 'recommended' way of extending the > > javatemplates theme. > > I've been using the default freemarker templates and have developed a few > > custom tag templates for some custom tags. I'm eager to port to > > javatemplates. > > > > I noticed that the SimpleTheme is 'hard-coded' in the JavaTemplateEngine > > like: > > > > private Themes themes = new Themes() {{ > > add(new SimpleTheme()); > > }}; > > > > which doesn't really allow for registering new themes. I can suggest a > > theme > > plugin/registry but wondered if it's already been considered. There > should > > also be a pluggable way of adding Handlers to a theme. > > > > > > - Eric > > >