visit, recurse, fallback - Apache FreeMarker Manual

Unsupported web browser - Use a modern browser to view this website!

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# visit, recurse, fallback

## Synopsis

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

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

    <#fallback>

Where:

- `node`: Expression evaluates to a node variable.
- `namespace`: A namespace, or a sequence of namespaces. A namespace can be given with the namespace hash (a.k.a. gate hash), or with a string literal that store the path of template that could be imported. Instead of namespace hashes, you can use plain hashes as well.

## 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_namespace`). The rules of name deduction:

- If the node doesn't support node namespaces (as text nodes in XML), then the directive name is simply the name of the node (`node?node_name`). A node does not support node namespaces if the `getNodeNamespace` method returns `null`.

- If the node does support node namespaces (as element nodes in XML), then a prefix deduced from the node namespace maybe appended before the node name with a colon used as separator (e.g. `e:book`). The prefix, and if there is a prefix used at all, depends on what prefixes has been registered with the `ns_prefixes` parameter of the `ftl` directive in the FTL namespace where `visit` looks for the handler directive (which is not necessary the same as the FTL namespace where `visit` was called from, as you will see later). Concretely, if there was no default namespace registered with `ns_prefixes` then for nodes that does not belong to any namespace (when `getNodeNamespace` returns `""`) no prefix is used. If there was a default namespace registered with `ns_prefixes` then for nodes that does not belong to any namespace prefix `N` is used, and for nodes that belong to the default node namespace no prefix is used. Otherwise, in both case, the prefix associated to the node namespace with the `ns_prefixes` is used. If there is not prefix associated to the node namespace of the node, then `visit` simply behave as if there was no directive found with the proper name.

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

Template

    <#-- 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:

Output

       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:

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`:

Template

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

and this is `n2.ftl`:

Template

    <#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:

Output

      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:

Template

    <#-- 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:

Output

    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:

Template

    <#recurse someNode using someLib>

is equivalent to writing:

Template

    <#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:

Template

    <#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:

Template

    <#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>

Previous Next