Simulate null parameters in Freemarker macros

Viewed 22071

I'm building a site using Freemarker and have started heavily using macros. I know in Freemarker 2.3 that passing a null value into a macro as a parameter is equivalent to not passing a parameter at all so I've created a global variable called "null" to simulate null checking in my macros:

<#assign null="NUL" />

Now in my macros I can do this:

<#macro doSomething param1=null>
  <#if param1 != null>
    <div>WIN!</div>
  </#if>
</#macro>

The problem comes if I want to pass a parameter that isn't a scalar. For instance, passing a List (which in Freemarker is a SimpleSequence) to a macro and checking against my null keyword yields the error:

freemarker.template.TemplateException: The only legal comparisons are between two numbers, two strings, or two dates. Left hand operand is a freemarker.template.SimpleSequence Right hand operand is a freemarker.template.SimpleScalar

I took a look at the freemarker code and I can see the issue (ComparisonExpression.isTrue()):

if(ltm instanceof TemplateNumberModel && rtm instanceof TemplateNumberModel) { 
  ...
}
else if(ltm instanceof TemplateDateModel && rtm instanceof TemplateDateModel) {
  ...
}
else if(ltm instanceof TemplateScalarModel && rtm instanceof TemplateScalarModel) {
  ...
}
else if(ltm instanceof TemplateBooleanModel && rtm instanceof TemplateBooleanModel) {
  ...
}
// Here we handle compatibility issues
else if(env.isClassicCompatible()) {
  ...
}
else {
  throw new TemplateException("The only legal comparisons...", env);
}

So the only solution I can think of is to set isClassicCompatible to true, which I think will call toString() on both objects and compare the result. However, the documentation specifically says anything relying on old features should be rewritten.

My quesion is, is there a solution to this that doesn't rely on deprecated features?

6 Answers

Actually i do not understand why null's are not allowed. I have a java function to retrieve a specific image URL with given optional / nullable parameters like height.

I wrote a macro for it using following workaround:

Java function

/**
 * Converts the given value to <code>null</code> if it equals the default
 * value, else returns it directly.
 *
 * @param value
 * @param defaultValue
 *
 * @return
 */
public static Object nullIfDefault(final Object value, final Object defaultValue)
{
    if (value != null && value.equals(defaultValue))
        return null;
    return value;
}

/**
 * Converts the given value to <code>null</code> if it equals an empty
 * string, else returns it directly.
 *
 * @param value
 *
 * @return
 */
public static Object nullIfEmpty(final Object value)
{
    return ObjectUtils.nullIfDefault(value, "");
}

Macro

Import static function

<#assign ObjectUtils=statics['com.example.ObjectUtils']/>

<#macro compEntityImage imageRetriever entity height="" width="">

    <img src="${imageRetriever.getMediaFileUrl(entity, 
        ObjectUtils.nullIfEmpty(height), 
        ObjectUtils.nullIfEmpty(width))}">

</#macro>

Markup

<@compEntityImgage imageRetriever=imageRetriver entity=product height=500/>
Related