Freitag, 23. September 2011

Adding Footnote Support to Wicket

During my current Wicket project I found that there is no built in support for adding footnotes to components. All you could do was adding the footnote and the corresponding description explicitly and as separate components. This is not very flexible or generic and when you start adding footnotes to panels but want to have the description show up in the containing page or in a different component of the same page.it gets very tedious to keep everything consistent by hand.

So once more I put my project work on hold and started some plumbing ;-). It took me about two days of trying and testing but finally I came up with a solution that serves all of my needs and should be rather easy to use. But I would also like to point out that I am aware this solution might not be what the wicket team would recommend (suggestions for improvement are as always welcome). But for me this is quite sufficient and should cover most real life use cases. For those interessted the source is available here.

Here a summary of the available features:

- programmatically add a footnote to a component
- programmatically process the descriptions for programmatically added components
- declaratively add labels with footnotes to a markup container
- declaratively add label with footnote descriptions for declaratively added footnotes
- declaratively add label with footnote descriptions for programmatically added footnotes
- use text or images as footnotes
- use separate style definitions for each footnote and description
- use freely defined wrapping tags for declaratively added footnote descriptions

As a first I took on the part of the solution concerning adding footnotes programmatically to later add support for declarative footnote creation on top of it.

For the programmatical solution I created two artifacts:

- a new behavior FootnoteBehavior.java, to be added to the component that is to be enriched with a footnote
- a new interface FootnoteAware.java, that is used to handle the descriptions for added footnotes

The behavior takes several argument:

/**
   * @param footNote
   *            The footnote text to be added to the component's model object
   *            as superscript and will additionally be wrapped with an anchor
   *            tag, e.g. 1 would result in 1
   * @param createImageTag
   *            If set to true the value of footNote
   *            is wrapped into a html img tag in which it is used as src
   *            attribute. This allows to use images for a footnote instead of
   *            regular text.
   * @param footNoteClass
   *            If this parameter is not empty it's value will be set as class
   *            attribute on the generated anchor tag.
   * @param footNoteText
   *            If this parameter is not empty a footnote description will be
   *            created.
   * @param fromResolver
   *            Indicates if this behavior was constructed by
   *            {@link FootnoteResolver}
   */

  public FootnoteBehavior(String footNote, boolean createImageTag, String footNoteClass, String footNoteText, boolean fromResolver)

The text representing the footNote is appended to the response after the component the behavior has been added to finished rendering. Additionally a footnote description is created as a Label and added to the first parent in the component's hierarchy that implements the FootnoteAware interface, the specifics of how this is done can be seen in the javadoc and source code of the classes. Then the FootnoteAware can render the descriptions as it needs to, e.g. as a PropertyListView.

This was already a nice effort but not yet sufficient for every day use as most components that use footnotes are Labels which are usually declaratively. So with the solution we have so far it would be necessary to add all those Labels by Java code which would be a drawback.

So we need support for declarativ footnotes which can be achieved by adding two resolvers:

- FootnoteResolver.java, to add a Label component with a FootnoteBehavior to the markup container
- FootnoteDescriptionResolver.java, to add a Label containing all footnote descriptions from the closest FootnoteAware parent

The first resolver simply takes it's the tag attributes and constructs a Label component as well as a FootnoteBehavior accordingly. The key attribute is used to determine the Label's content in the same way as a tag would, which means it is resolved via proeprty lookup. In addition all attributes are interpolated using the markup container's properties and/or nested tags. For further details please check javadoc and source code.

The second resolver looks for a FootnoteAware parent of it's container and if it finds one renders the contained footnote descriptions according to the tag attributes. Each description will be wrapped in a tag who's name can be defined via the tagname attribute and can optionally have a class attribute to allow futher styling flexibility.

I hope this summary is comprehensive enough as I tried to keep it rather short instead of copying the classes' source code. If you find any flaws or have suggestions I would be glad if you shared them. And of course everyone is welcome to copy and use the code provided here.