Proposal: allow a short HTML displayer template name for parameterized types

Hello!

Context

See XWIKI-24872
The default HTMLDisplayer implementation, DefaultTemplateHTMLDisplayer, renders a value by looking for a template named after the displayed type, in this order:

  • html_displayer/[type]/[mode].vm
  • html_displayer/[type].vm
  • html_displayer/[mode].vm
  • html_displayer/default.vm

How [type] is computed depends on the kind of Type:

  • a plain Class is named by its simple name, lowercased, so org.xwiki.model.reference.SpaceReference resolves to html_displayer/spacereference.vm;
  • any other Type, and in particular a ParameterizedType, is named by its fully qualified serialization only, so java.util.List<org.xwiki.model.reference.SpaceReference> can only ever resolve to html_displayer/java.util.list(org.xwiki.model.reference.spacereference).vm.

The author of a displayer for a SpaceReference writes spacereference.vm, while the author of a displayer for a List<SpaceReference> has to spell out both fully qualified names. The asymmetry has no functional reason, it is just how getTypeNames grew: the simple-name branch was added for Class in 10.11RC1 (8fe09eda9de) and the parameterized case fell into the generic ReflectionUtils.serializeType fallback.

This naming convention is a public extension point, since any extension can ship an html_displayer/*.vm template and since the displayer is callable from wiki pages through $services.displayer.html.

Proposal

Add a short name for parameterized types, built from the simple names of the raw type and of its type arguments, and look it up after the fully qualified one. For java.util.List<org.xwiki.model.reference.SpaceReference> the candidate type names would become, in order:

  1. java.util.list(org.xwiki.model.reference.spacereference)
  2. list(spacereference)

which, combined with the mode, gives the full lookup order:

html_displayer/java.util.list(org.xwiki.model.reference.spacereference)/view.vm
html_displayer/java.util.list(org.xwiki.model.reference.spacereference).vm
html_displayer/list(spacereference)/view.vm
html_displayer/list(spacereference).vm
html_displayer/view.vm
html_displayer/default.vm

  • Priority. The fully qualified name keeps the highest priority on purpose. Short names can collide: List<org.xwiki.rendering.block.Block> and List<org.xwiki.contrib.myextension.Block> both shorten to list(block). Keeping the fully qualified name first means a template can always target one precise type, and the short name stays a convenience for the common case. That collision risk is not new, by the way: it already exists for plain classes since 10.11RC1, where every Class is named by its simple name only.
  • Nested type arguments. A type argument that is not a plain Class would still fall back to its serialized name, so List<List<Block>> would shorten to list(java.util.list(org.xwiki.rendering.block.block)): only the outer raw type could be shortened.
  • Impact on existing installations. No Java signature changes, the modified method is private and in an internal package, and Revapi reports no break. What changes is the resolution.

Opinion

+1 from me for the proposal as described, because:

  • It removes an inconsistency rather than adding a feature: the same rule, “name the type by its simple name”, would apply to both kinds of types.
  • Trying the fully qualified name first means that we don’t change how anything resolves today. We just add support for things that wouldn’t resolve before.
  • Doing nothing and keeping html_displayer/java.util.list(org.xwiki.model.reference.spacereference)/view.vm as the only possible file name for a common case looks off given we already have a system for simple class cases.

Conclusion

Two things I would like to settle before this is merged:

  • Behaviour change. Do you think this behaviour change is legitimate, should we avoid it?
  • Nested types. Should the shortening recurse into type arguments, so that List<List<Block>> shortens all the way to list(list(block)), or is stopping at the first level good enough given we don’t have a need for that deeper nesting yet?

I’d like to resolve this discussion quite quickly since it’s eventually a blocker for a roadmap ticket.
I am looking forward to your feedback :slight_smile:
Lucas C.

Sounds good to me in general, +1. If it doesn’t make the implementation too complex, I would suggest also supporting shortened nested types, but only either all shortened or all expanded, no mixed versions.

Thanks for the proposal @CharpentierLucas
Do you have a practical use case motivating you to work on this?

+1 as well (with Michael’s suggestion)

+1, but as I commented on the pull request (and as mentioned by @MichaelHamann here), it should be recursive, as a mixed name does not make much sense (it’s not like it was complex to support).

My main use case for now is to make templates for “multi” pickers in XS. Those would be used to pick multiple items for the same parameter that relies on a composite type (e.g. list(user)).

Thanks for all your feedback!
Implemented the recursive version in XWIKI-24872: Allow a short HTML displayer template name for parameterized types by Sereza5 · Pull Request #6383 · xwiki/xwiki-platform · GitHub : `List<List>` now resolves to `html_displayer/list(list(block)).vm`, and a name is either fully qualified or fully shortened, no mixed form.

Closing this topic with 4 +1 .