visit, recurse, fallback

Page Contents

Synopsis

<#visit node using namespace>
or
<#visit node>
<#recurse node using namespace>
or
<#recurse node>
or
<#recurse using namespace>
or
<#recurse>
<#fallback>

Where:

Description

The visit and recurse directives are used for the recursive processing of trees. In practice, this will mostly be used for processing XML.

Visit

When you call <#visit node>, it looks for a user-defined directive (like a macro) to invoke that has the name deducted from the node's name (node?node_name) and namespace (node?node_namesoace). The rules of name deduction:

The node for which the user-defined directive was invoked is available for it as special variable .node. Example:

<#-- Assume that nodeWithNameX?node_name is "x" -->
<#visit nodeWithNameX>
Done.
<#macro x>
   Now I'm handling a node that has the name "x".
   Just to show how to access this node: this node has ${.node?children?size} children.
</#macro>  

The output will be something like:

   Now I'm handling a node that has the name "x".
   Just to show how to access this node: this node has 3 children.
Done.  

If one or more namespaces is specified using the optional using clause, then visit will look for the directives in those namespaces only, with the earlier specified namespaces in the list getting priority. If no using clause is specified, the namespace or sequence of namespaces specified with the using clause of the last uncompleted visit call is reused. If there is no such pending visit call, then the current namespace is used. For example, if you execute this template:

<#import "n1.ftl" as n1>
<#import "n2.ftl" as n2>

<#-- This will call n2.x (because there is no n1.x): -->
<#visit nodeWithNameX using [n1, n2]>

<#-- This will call the x of the current namespace: -->
<#visit nodeWithNameX>

<#macro x>
  Simply x
</#macro>  

and this is n1.ftl:

<#macro y>
  n1.y
</#macro>  

and this is n2.ftl:

<#macro x>
  n2.x
  <#-- This callc n1.y as it inherits the "using [n1, n2]" from the pending visit call: -->
  <#visit nodeWithNameY>
  <#-- This will call n2.y: -->
  <#visit nodeWithNameY using .namespace>
</#macro>

<#macro y>
  n2.y
</#macro>  

then this will print:


  n2.x
  n1.y
  n2.y

  Simply x
   

If visit doesn't find a user-defined directive in either FTL namespaces with the name identical to the name deduced with the rules described earlier, then it tries to find an user-defined directive with name @node_type, or if the node does not support node type property (i.e. node?node_type returns undefined variable), then with name @default. For the lookup, it uses the same mechanism as was explained earlier. If it still doesn't find an user-defined directive to handle the node, then visit stops template processing with error. Some XML specific node types have special handling in this regard; see: XML Processing Guide/Declarative XML processing/Details. Example:

<#-- Assume that nodeWithNameX?node_name is "x" -->
<#visit nodeWithNameX>

<#-- Assume that nodeWithNameY?node_type is "foo" -->
<#visit nodeWithNameY>

<#macro x>
Handling node x
</#macro>

<#macro @foo>
There was no specific handler for node ${node?node_name}
</#macro>  

This would print:

Handling node x
  
There was no specific handler for node y

   

Recurse

The <#recurse> directive is really syntactic sugar. It visits all children nodes of the node (and not the node itself). So, to write:

<#recurse someNode using someLib 

is equivalent to writing:

<#list someNode?children as child><#visit child using someLib></#list>  

However, target node is optional in the recurse directive. If the target node is unspecified, it simply uses the .node. Thus, the terse instruction <#recurse> is equivalent to:

<#list .node?children as child><#visit child></#list>  

As a side comment for those who are familiar with XSLT, <#recurse> is pretty much exactly analogous to the <xsl:apply-templates/> instruction in XSLT.

Fallback

As you could learn earlier, in the documentation of the visit directive, the user-defined directive that handles the node is maybe searched in multiple FTL name-spaces. The fallback directive can be used in a user-defined directive that was invoked to handle a node. It directs FreeMarker to continue the searching for the user-defined directive in the further name-spaces (that is, in the name-spaces that are after the name-space of the currently invoked user-defined directive in the list of name-spaces). If a handler for the node is found then it is invoked, otherwise fallback does nothing.

A typical usage of this to write customization layer over a handler library, that sometimes passes the handling to the customized library:

<#import "/lib/docbook.ftl" as docbook>

<#--
  We use the docbook library, but we override some handlers
  in this namespace.
-->
<#visit document using [.namespace, docbook]>

<#--
  Override the "programlisting" handler, but only in the case if
  its "role" attribute is "java"
-->
<#macro programlisting>
  <#if .node.@role[0]!"" == "java">
    <#-- Do something special here... -->
    ...
  <#else>
    <#-- Just use the original (overidden) handler -->
    <#fallback>
  </#if>
</#macro>  
FreeMarker Manual -- For FreeMarker 2.3.22
HTML generated: 2015-02-28 21:34:03 GMT
Edited with XMLMind XML Editor
Here!