OK, it (see quoted mail, and
https://issues.apache.org/jira/browse/FREEMARKER-63) is partially
done, so insights are highly welcome! It's not yet merged, so see:
https://github.com/ddekany/incubator-freemarker/tree/FREEMARKER-63
https://github.com/apache/incubator-freemarker/pull/30
It works fully for directives (like macros), as per FREEMARKER-63.
(#function-s/methods is the next issue,
https://issues.apache.org/jira/browse/FREEMARKER-64)
The syntax is as it was described earlier, except "byName" and
"byPosition" was replaced with "named" and "positional":
<#macro message text{positional}, color id=someDefault>...
<#function message(text, color{named}, id{named}=someDefault)>...
and then you call it like this (as it was said much earlier):
<@message 'Hello World' color='red' id='test' />
(You can't yet call function with named arguments. You can already
define functions with named parameters though.)
More examples:
https://github.com/ddekany/incubator-freemarker/blob/FREEMARKER-63/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
As of the main API-s, the key interfaces are TemplateCallableModel and
its two sub-interfaces:
https://github.com/ddekany/incubator-freemarker/blob/FREEMARKER-63/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
https://github.com/ddekany/incubator-freemarker/blob/FREEMARKER-63/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
https://github.com/ddekany/incubator-freemarker/blob/FREEMARKER-63/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
One of the potentially controversial thing with them is that they use
a somewhat low-level way of argument passing, where all the arguments
are passed in a single TemplateModel array, and there's a
ArgumentArrayLayout that describes which parameters go to what indexes:
https://github.com/ddekany/incubator-freemarker/blob/FREEMARKER-63/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
This is for efficiency (we avoid building Map-s on runtime, and just
create an array, and can also do some of the grinding later without
HashMap lookups and string comparisons, with simply reading array
items from know (constant) indexes. It isn't the most user friendly
API to implement for sure (but the majority users don't implement
FreeMarker directives/function in Java anyway - it's power user thing
mostly), though I have implemented a few directives and functions with
this, and it's not that bad after doing the same with FM2,
particularly because with named arguments you don't have to deal with
unknown names anymore, or write those switch-by-String statements (or
worse, Map.Entry loops before Java 7). Anyway, later I plan to add
some utility for defining directives/function with annotated static
methods, so it can be much more friendly if it has to.
Calling a TemplateCallableModel from user Java code is a much more
inconvenient thing though, as then you are responsible for setting up
the argument array as per the provided ArgumentArrayLayout. Luckily,
that's done very rarely by users (and can also be supported with some
utility methods if really needed). After all, FreeMarker
directives/function are to be called from templates, otherwise you
just use plain Java methods.
See also some more smaller changes here:
https://github.com/ddekany/incubator-freemarker/blob/FREEMARKER-63/FM3-CHANGE-LOG.txt
Wednesday, July 5, 2017, 10:00:26 PM, Daniel Dekany wrote:
> Something from the first mail of this thread that I want to emphasize
> is that which parameter needs to be passed by position and which by
> name is decided when the directive or function is defined. It's not
> decided by the caller (see reasons below).
>
> For example, the 1st parameter of #include can only be passed by
> position, as in `<#include "foo.ftl">`. You can't write
> `<#include template="foo.ftl">`. However, the `ignoreMissing`
> parameter (and in FM2 we also had `encoding` and `parse` parameters)
> can only be passed by name, as in
> `<#include "foo.ftl" ignoreMissing=true>`. You can't write
> `<#include "foo.ftl", true>`.
>
> Some may have used languages where the caller decides what is passed
> by position and what by name, but IMO it just leads to chaos in our
> case. I also realize and accept that everybody has different taste,
> but people read/edit other people's code a lot, so the taste of the
> original author often doesn't matter much. Plus if someone overuses
> positional parameters (laziness/rushing and C/Java habits may make
> people do that...), the template becomes less readable, especially for
> someone less experienced with writing templates. Consistency regarding
> how core directives are called is even more valuable, as people
> copy-paste it from StackOverflow etc. Imagine if in some cases you see
> `<#if test=foo>`, while in others <#if foo>... conf