FreeMarker check the class name of an object - freemarker

is there a way to get the class name of an object in a freemarker template ?
For instance:
<#if component.javaType.class.name.equals("test") >
"something...."
</#else>
"something else ...."
</#if>
Thanks

There's no feature built in for that, but depending on the configuration settings and on the type of the object, this may works:
<#if component.class.name == 'com.example.Something'>
That works because component.foo simply means comonent.getFoo() in Java, so the above just means component.getClass().getName(). This, however doesn't work if the JavaBean properties of component aren't exposed, which (assuming the usual FreeMarker configuration) is the case for String-s, Number-s, Map-s, List-s and some more "standard" classes. If component can be a such object, but the comparison should be false for them anyway, you can write (component.class.name)!'unknown' == 'com.example.Something'.

Related

Freemarker not entering if/else if values are null

<#if myVar = "test">
A
<#else>
B
<#/if>
If myVar is null / not defined, neither A nor B will be output. This is fixed by adding ! after the variable:
<#if myVar! = "test">
A
<#else>
B
<#/if>
Is this intended? Because if so, it is extremely confusing and I can't imagine any user would expect it to behave this way. It is suggesting that null = "test" is somehow neither true nor false. It seems clear that null = "test" should always be considered false -- what am I missing?
Note: we have configured Freemarker to replace unknown variables with an empty string:
config.setTemplateExceptionHandler((ex, environment, out) ->
{
if (ex instanceof InvalidReferenceException)
{
try
{
out.write("");
}
catch (IOException e)
{
throw new TemplateException("Error while handling template exception.", e, environment);
}
}
else
{
throw ex;
}
});
So this would (at least I thought it would) end up being:
<#if "" = "test">
A
<#else>
B
<#/if>
In which case, I'd expect the else to be entered for sure -- but it is not.
Given this example:
<#assign myVar = null/>
<#if myVar == "test">
A
<#else>
B
</#if>
This throws the following error:
The following has evaluated to null or missing:
==> null
myVar has been evaluated to null or missing -- this seems like the source of my confusion; Freemarker does not differentiate between missing and existing-but-null values, I guess?
It's irrelevant if it's #if/#else or any other directive call, because when an error occurs (any kind of TemplateException, not just "missing value"), the whole statement is skipped. Not the variable resolution, not even the whole expression, but the whole statement. That's what the Manual and the Javadoc says too, like see: https://freemarker.apache.org/docs/pgui_config_errorhandling.html. Therefore, templateExceptionHandler setting is not for providing defaults for missing values. It's for handling situations that are genuinely errors. Like, someone did a mistake, something is not operational.
As of why not take the #else still. When an error occurs while evaluating the condition, the template engine just can't tell which branch would have been taken if the developers (or whoever) did not make a mistake. Picking either branch of the if/else is a gamble. (It's not even a 50-50 gamble, as the positive branch tends to be the right one considering the real world meaning of the output, yet, if anything, people expect the template engine to bet on the else branch.) So, hoping that you will print some error indicator at least, it rather doesn't pick either branch. But it's really automatic, as the whole #if/#elseif/#else thing is one big statement.
<#assign myVar = "test"/>
<#if myVar?? && myVar == "test">
A
<#else>
B
</#if>
Yes it is intended. When a variable is not defined or null, freemarker will raise an error.
It is explained in the FAQ: Why is FreeMarker so picky about null-s and missing variables, and what to do with it?
This is a design choice from the initial author of the framework. If you are not confortable with this choice, you can use the Default value operator (use myVar! instead of myVar), or use another templating framework.
The custom exception handler you added doesn't replace the expression with an empty String, it replaces the full if/else expression.

Problem detecting and using a nullable value in Freemarker

I have a POJO object that I have serialized from JSON (in Java). I am using an object wrapper constructed via:
DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27);
builder.setExposeFields(true);
objectWrapper = builder.build();
I use the setExposeFields(true) because the object I am wrapping is not a Java bean, but rather just a POJO that contains public fields.
I am doing the following in my template:
<#ConditionOccurrence co = c/>
<#macro ConditionOccurrence co>
<#list co?keys as key>
${key}
</#list>
${co.occurrenceStartDate!'wtf'}
${co["occurrenceStartDate"]}
A condition occurrence of: ${codesetName(co.codesetId, "any condition")}
<#if co.first!false>- for the first time in the person's history</#if>
<#if (co["occurrenceStartDate"])??>co.OSD is null: </#if>
</#macro>
Note, the 'c' is an element in a sequence, and is not important to the exact problem I am having.
The output of the template shows this:
stopReason
getClass
gender
CorrelatedCriteria
providerSpecialty
occurrenceStartDate
occurrenceEndDate
visitType
accept
codesetId
hashCode
conditionSourceConcept
equals
conditionType
toString
conditionTypeExclude
class
first
age
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
A condition occurrence of: Psoriasis
- for the first time in the person's history
co.OSD is null:
The first set of lines are all the keys in my POJO. This is correct.
the two lines of output:
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
This is showing that the field occurrenceStartDate is an object of type DateRange. note this could be null in some cases, so I am checking how to check for null...
The next part of the output:
- for the first time in the person's history
co.OSD is null:
This is showing that it is reading the 'first' attribute of the object correctly, and I have switched the raw JSON from 'true' to 'false' and the template responds properly to the change in this value. Note, in the object, the 'first' field is type Boolean.
The second line: co.OSD is null is what is confounding me. I confirmed earlier that outputting the 'occurrenceStartDate' field shows that it holds a DateRange object. But, this statement is evaluating to TRUE (ie: it is null):
#if (co["occurrenceStartDate"])??>co.OSD is null: </#if>
I have tried with both dot notation and bracket notation. For some reason, the ?? operator on that field is saying it is null. Note, the underlying object isn't a simple String or Number type, it is a simple POJO class DateRange with 3 String properties on it. Again, these are not JavaBeans, these are just POJOs.
Can anyone explain why the ?? operator says it is empty when it is clearly referencing an object? Btw: if I attempt to access co.occurrenceStartDate at all, it results in a template error that I'm referencing a null value, so the core problem here is why does the wrapper thing it is a null?
Thank you in advance for your help.
The ?? operator means "is present", not "is missing". So your line should be:
<#if !(co.occurrenceStartDate??)>co.OSD is null: </#if>

Compare String with Spring Security tag using Freemarker

How can I achieve the following with Freemarker and Spring Security taglibs?
<#if "[]" == <#security.authentication property='principal.userAccount.administratedVendors'/> >
My Accounts
</#if>
Currently, the above doesn't compile.
The root of here problem is that security.authentication shouldn't be a FreeMarker directive, but a FreeMarker function or method. Directives don't have a return value (therefore you can't use them in place of an expression, like you did). Directives can print to the output (among others), but that's just a "side effect", not a return value. FreeMarker functions/methods have a return value. But if the Spring integration doesn't provide the same call as function/method (check that), then the best you can do is this ugly workaround:
<#assign capturedOutput><#security.authentication property='principal.userAccount.administratedVendors'/></#assign>
<#if capturedOutput == '[]'>
My Accounts
</#if>
It's bit fragile in theory, as (I guess) principal.userAccount just prints the toString() of the property value object. While for most List implementations that will return "[]" for an empty list,, it's not guaranteed by the Java API.

FreeMarker Java 8 Optional value support

Does FreeMarker support Optional value in Java 8?
e.g. I have String id, and its getter method is like:
public Optional<String> getId() {
return Optional.ofNullable(Id);
}
How am I going to reference it in the .ftl file. It seems that {data.id} can not find the correct Optional value. But gives me Optional[1334586]
Well, Freemarker is not supposed to be aware of Optional or it is better to say that its dynamically typed so it works for any object.
Since you calling ${data.id} it's just calls toString on Optional which is totally expected behavior.
If you want to handle null values in your template and for that you want to use Optional, you may choose to set a default value if null, so Optional usage won't be needed:
Synopsis: unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!
Example: ${data.id!"No id."}
Or check if it's exists:
<#if data?? && data.id??>
Id found
<#else>
Id not found
</#if>
For more info check out the Freemarker docs. Specifically parts: Handling missing values and Missing value test operator.
If you just want to get the value from Optional in your template:
${data.id.orElse('')}
or
${data.id.get()!''}

How can I set a FreeMarker variable with an interpolated value? error: "You can't use "${" here as you are already in FreeMarker-expression-mode."?

I am absolutly new in FreeMarker and I have the following problem working on a Spring MVC application that use this template engine.
So into a controller method I put an int representing the current year (2016) into the model, in this way:
model.addAttribute("annoCorrente", annoCorrente);
Then, into my FreeMarker page I have to assign this value to a variable, so I write the following expression:
<#assign a = ${annoCorrente}>
But in this way I obtain the following error message:
[col. 86] You can't use "${" here as you are already in FreeMarker-expression-mode. Thus, instead of ${myExpression}, just write myExpression. (${...} is only needed where otherwise static text is expected, i.e, outside FreeMarker tags and ${...}-s.)
Why? How can I correctly initizialize a FreeMarker variable with the value obtained from the model associated to this view?
Change <#assign a = ${annoCorrente}> to <#assign a = annoCorrente>
(or you can do <#assign a = "${annoCorrente}"> but this is not recommended)

Resources