Type independent built-ins

These are the built-ins that don't care (much) about the type of their left hand argument.



This built-in exists since FreeMarker 2.3.23.

This is basically the in-line (expression) version of the switch-case-default directives. Its generic format is like matchedValue?switch(case1, result1, case2, result2, ... caseN, resultN, defaultResult), where defaultResult can be omitted. Example:

<#list ['r', 'w', 'x', 's'] as flag>
  ${flag?switch('r', 'readable', 'w' 'writable', 'x', 'executable', 'unknown flag: ' + flag)}
  unknown flag: s

That is, switch will find the first case parameter (left to right) whose value equals to matchedValue, then it returns the value of the result parameter that's directly after that case parameter. If it doesn't find an equal case, then it will return the value of the defaultResult, or if there's no defaultResult parameter (i.e., if the number of parameters is even) then it stops the template processing with error.

Further details:

  • The comparison of matchedValue to the case parameter value behaves exactly like the == operator. Hence it only compares scalars and only same-type values. Thus, something like x?switch(1, "r1", "c2", "r2") doesn't make sense, as if x is non-numerical then the first case will cause error, and if x is numerical then the second case will cause error (unless x is 1, as then we won't do further comparisons after the first one).

  • Unlike with normal method calls, only those parameters of switch(...) are evaluated that are indeed needed. For example, in two()?switch(c1(), r1(), c2(), r2(), c3(), r3()), if two() returns 2, c1() returns 1, and c2() returns 2, then only the following functions will be called, and in this order: m(), c1(), c2(), r2(). (Naturally, arguments that aren't evaluated can refer to missing variables without causing error.) It's guaranteed that the case parameter expressions are evaluated left to right, and only until the first match was found. It's also guaranteed that only the result expression that belongs to the first matching case will be evaluated. It's also guaranteed that the defaultResult expression will only be evaluated if there was no matching case parameter.

  • The case parameter expressions need not be constant values, they can be arbitrary complex expressions. Of course, the same goes for and the result, defaultResult, and matchedValue.

  • There's no restriction regarding the type of the case parameter values, like they can be strings, or numbers, or dates, etc. However, because of how the == operator works, it doesn't make sense to use case parameters of different types inside the same switch (see earlier why).

  • Unlike with the case directive, there's no fall-through behavior there, that is, there's no need for an equivalent of the break directive.


If you need to switch by a boolean value, you should use the then built-in instead, like matchedBoolean?then(whenTrue, whenFalse).


If you need to do arbitrary logical tests instead of simple equality comparisons at the case parameters, you can do something like this (here we tests for ranges): true?switch(priority <= 1, "low", priority == 2, "medium", priority >= 3, "high")