Stripping of white space seems broken - freemarker

I am moving some XSLT templates to freemarker due to better Java compatibility but freemarker seems to have issues with handling whitespace. XML ends up with white space all over the place like this:
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row> <fo:table-cell
> <fo:block >
<fo:inline font-weight="bold"
I have tried adding the strip_whitespace=true directive but it has no effect. It is the default so it is probably already meant to be working.
In XSLT all white space between XML blocks is collapsed. This is made easier by placing any free text needs inside an <xsl:text> example text including padding </xsl:text> element.
It seems that freemarker lacks control over the text nodes and there is no way to collapse the whitespace.
UPDATE: here is an example of a portion of the freemarker template where it generates the fo:table-cell
<#macro tableHeaderM tableHeader>
<#list tableHeader.columns as column><#columnM column=column /></#list>
</#macro>
<#macro columnM column>
<fo:table-column column-width="${column.width}cm" />
</#macro>
<#macro rowM row table>
<fo:table-row><#list row.cells as cell><#cellM cell=cell table=table /></#list></fo:table-row>
</#macro>
<#macro cellM cell table>
<fo:table-cell<#cellFormattingM cell=cell table=table />><#blockObjectsM blockObjects=cell.blockObjects /></fo:table-cell>
</#macro>
<#macro cellFormattingM cell table>
<#if cell.border == true> border="0.1mm solid"</#if>
<#if cell.pad == true> padding="3pt"</#if>
<#if cell.shade == true> background-color="#eeeeee"</#if>
<#if table.collapse == true> margin-top="5px"</#if>
</#macro>

The white-space stripping and trimming in FreeMarker is solely for removing white-space that's present in the template itself (i.e., white-space added for source code formatting). The white-space in your case is present in the data-model. Unfortunately, you need to pre-process the DOM to get rid of those (or insert with ?trim and such). The FreeMarker DOM wrapper doesn't know which whitespace is insignificant.
Update (since the white-space doesn't come from the DOM):
strip_whitespace (white-space stripping) is by default true, so adding that won't remove more white-space. So your best bet is using <#t> (and maybe <#rt> and <#lt>) to remove more white-space explicitly.

Related

Check if FreeMarker #nested directive is empty

I want to output tags around a <#nested> directive in a macro, but only if it would actually output something. The actual use case is more complicated, this is just the broken down version.
How do I check for existence of <#nested> content?
<#macro opt tagname>
<#if (#nested)??> <-- what do I need to put here
<${tagname}>
<#nested>
</${tagname}>
</#if>
</#macro>
Example 1
Template: <#opt hello />
Output: (empty)
Example 2
Template: <#opt hello>goodbye</#opt>
Output: <hello>goodbye</hello>
You have to capture the nested content, and then print it if necessary. Like this (this assumes auto-escaping on):
<#macro opt tagname>
<#local nestedContent><#nested></#local>
<#if nestedContent?has_content>
<${tagname}>${nestedContent}</${tagname}>
</#if>
</#macro>
Without auto-escaping the #if changes to just <#if nestedContent != ''>.

Trying to generate a string that becomes a variable name in Freemarker or a "derived variable name"

I'm using a data-directive or list to pull in several values, of which, I don't know how many there will be, and I want to try and list through them, and create several variables in the list if you will.
<#list 1..10 as x>
<#-- the next line doesn't work, but what i'm trying to fix -->
<#assign .vars['VAR'+x?string] = rand(100) />
</#list>
But I can list them back out that way.
<#list 1..10 as x>
${.vars['VAR'+x?string]}
</#list>
The documentation for assign, says:
name: name of the variable. It is not expression. However, it can be
written as a string literal, which is useful if the variable name
contains reserved characters, for example <#assign "foo-bar" = 1>.
Note that this string literal does not expand interpolations (as
"${foo}").
Is there no way around this? Am I trying to do the impossible? Is there some way I can insert a derived name into the .vars... Hash is it?
A little more research that was close, but didn't get me there:
This prevoius question gives how to READ the derived variable, but I need to WRITE/CREATE the derived variable.
FreeMarker get variable value by concatenating another variable value
This prevoius question shows that I can use a string to assign a variable and re-iterates what we saw in the first link.
Variable name in Freemarker Template Language
As FreeMarker can't assign to elements of collections (but you can ?map(it -> ...) a collection to another), the only way is via ?interpret:
<#list 1..10 as x>
<#-- the next line doesn't work, but what i'm trying to fix -->
<#'<#assign VAR${x?c} = rand(100)>'?interpret />
</#list>
I wonder why do you need to assign to a dynamically named variables though, since reading dynamically named variables is also a pain, even if a lesser one.
Ultimately, I believe the correct way to phrase my solution was a sequence of hashes:
<#list 1..z_Coupon_Pet.NUM_COUPONS as x>
<#assign INSTORE_COUPON=call_coupon_from_table1() />
<#assign ONLINE_COUPON=call_coupon_from_table2() />
<#assign coupon_string_row= '{
"COUPON_NUM" : ${x},
"INSTORE" : "${INSTORE_COUPON?js_string}",
"ONLINE" : "${ONLINE_COUPON?js_string}"
}' />
<#if x==1>
<#assign coupon_hash_string = coupon_string_row />
<#else>
<#assign coupon_hash_string = coupon_hash_string + ',' + coupon_string_row />
</#if>
</#list>
</#if>
<#if coupon_hash_string?has_content>
<#assign coupon_hash=parsejson('[' + coupon_hash_string + ']') />
</#if>
We specifically avoid <#assign my_hash = my_hash + element /> because of this note in the documentation:
Note that hash concatenation is not to be used for many repeated concatenations, like for adding items to a hash inside a loop. While adding together hashes is fast and is constant time (independent of the size of the hashes added), the resulting hash is a bit slower to read than the hashes added together. Thus after tens... of additions the result can be impractically slow to read.

Freemarker XPath expression to find a node with a child id

I'm using Freemarker XML processing to build a menu. I want to find if the current element, or any of it's children have an id matching activeNode.
But I'm having a lot of trouble working out the Freemarker syntax for the XPath. This seems to match all nodes with children links (note that item is a node reference to the customers node)
<#assign aNode="search-customers" />
<#assign xpath>link["#id=${aNode}"]</#assign>
<#if item[xpath][0]?has_content>
<#assign cssClass> class="active"</#assign>
</#if>
This is the relevant section of the xml I'm trying to match. For example, the search-customers node, should highlight the customers node.
<menu>
<top title="title.home" url="/admin" icon="fa fa-home" />
<top title="title.customers" id="customers" url="javascript:;" icon="fa fa-group">
<link title="title.search.customers" id="search-customers" url="/admin/customers"></link>
<link title="title.register.customer" id="register-customer" url="/admin/customers/new"></link>
<link title="title.contact.us" id="contact-us" url="/admin/contactus"></link>
<link title="title.subscribers" id="subscribers" url="/admin/subscribers"></link>
</top>
Ideally it would also match the current node id - so the css should be active if either a child node id, or the current node id matches activeNode.
Can anyone help with the Freemarker syntax for this?
Never mind, I figured it out eventually, I think it's an error in the docs that put me off using the square bracket syntax....
<#if strVal=aNode || item["link[#id='${aNode}']"]?has_content>
<#assign cssClass> class="active"</#assign>
<#else>
<#assign cssClass></#assign>
</#if>

does freemarker support show all variable in data-model?

I want to see all variables in freemarker data-model, just like struts2 debug tag to show value stack.
Is there a way for freemarker to do this ?
There's no universal solution possible for that, but you can try
<#list .data_model?keys as key>
${key}
</#list>
This works if the data-model is just a usual Map or JavaBean, but for more sophisticated data-models it's up to the data-model implementation if it supports ?keys and if it indeed returns everything.
You also have the variables that you set in the templates, which can be listed like above, only instead of .data_model use .globals, .namespace (which means the current template namespace) and .locals.
You may also have Configuration-level shared variables, and there's no way to list those purely from FTL (you could write a custom TemplateMethodModel for it that reads Configuration.getSharedVariableNames() though, and call it from the template).
Of course, ideally, FreeMarker should have a <#show_variables> directive or something, that does a best effort to show all this... but sadly there is no such thing yet.
An even more detailed way would be this macro:
<#macro dump_object object debug=false>
<#compress>
<#if object??>
<#attempt>
<#if object?is_node>
<#if object?node_type == "text">${object?html}
<#else><${object?node_name}<#if object?node_type=="element" && object.##?has_content><#list object.## as attr>
${attr?node_name}="${attr?html}"</#list></#if>>
<#if object?children?has_content><#list object?children as item>
<#dump_object object=item/></#list><#else>${object}</#if> </${object?node_name}></#if>
<#elseif object?is_method>
#method
<#elseif object?is_sequence>
[<#list object as item><#dump_object object=item/><#if !item?is_last>, </#if></#list>]
<#elseif object?is_hash_ex>
{<#list object as key, item>${key?html}=<#dump_object object=item/><#if !item?is_last>, </#if></#list>}
<#else>
"${object?string?html}"
</#if>
<#recover>
<#if !debug><!-- </#if>LOG: Could not parse object <#if debug><pre>${.error}</pre><#else>--></#if>
</#attempt>
<#else>
null
</#if>
</#compress>
</#macro>
<#dump_object object=.data_model/>
This gives you a full dump of your data model.
Here is #lemhannes macro definition modified to emit JSON. Lightly tested on a fairly simple datamodel
<#macro dump_object object debug=false>
<#compress>
<#if object??>
<#attempt>
<#if object?is_node>
<#if object?node_type == "text">${object?json_string}
<#else>${object?node_name}<#if object?node_type=="element" && object.##?has_content><#list object.## as attr>
"${attr?node_name}":"${attr?json_string}"</#list></#if>
<#if object?children?has_content><#list object?children as item>
<#dump_object object=item/></#list><#else>${object}</#if>"${object?node_name}"</#if>
<#elseif object?is_method>
"#method"
<#elseif object?is_sequence>
[<#list object as item><#dump_object object=item/><#if !item?is_last>, </#if></#list>]
<#elseif object?is_hash_ex>
{<#list object as key, item>"${key?json_string}":<#dump_object object=item/><#if !item?is_last>, </#if></#list>}
<#else>
"${object?string?json_string}"
</#if>
<#recover>
<#if !debug>"<!-- </#if>LOG: Could not parse object <#if debug><pre>${.error}</pre><#else>-->"</#if>
</#attempt>
<#else>
null
</#if>
</#compress>
</#macro>

Is it possible to have freemarker's <#spring.showErrors to display errors in a div instead of span

Code:
<#spring.formInput 'myForm.spouseEmail' 'id="spouseEmail" class="text"'/>
<#spring.showErrors ', ' 'error'/>
Output:
<span class="error">not a well-formed email address</span>
What I want:
<div class="error">not a well-formed email address</div>
#Mike: it seems you have troubles understanding the nature of macros. They are already-written freemarker script to make your life easier. You can always write a customed one.
Some people think it obvious, but I myself find that it's not easy to know how to view the spring-freemarker macros source code. You can navigate to package org/springframework/spring-webmvc-3.0.5.jar/org/springframework/web/servlet/view/freemarker/spring.ftl in Eclipse's "Referenced Libraries".
Here's the macro "showErrors" gotten from "spring.ftl":
<#macro showErrors separator classOrStyle="">
<#list status.errorMessages as error>
<#if classOrStyle == "">
<b>${error}</b>
<#else>
<#if classOrStyle?index_of(":") == -1><#assign attr="class"><#else><#assign attr="style"></#if>
<span ${attr}="${classOrStyle}">${error}</span>
</#if>
<#if error_has_next>${separator}</#if>
</#list>
</#macro>
To achieve your goal, it's very simple: just write a custom macro which is exactly like the code above, replace span by div
No, but you can easily write your own macro to do whatever you want. Get your inspiration from spring.showErrors itself.

Resources