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 <[email protected]> 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 <[email protected]> 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
> >
>