访问,递归,后备

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:

  • node:表达式的计算结果为node variable

  • namespacenamespace或一系列名称空间。可以使用命名空间哈希值(也称为门哈希)或存储存储可导入模板路径的字符串 Literals 来给定命名空间。除了命名空间哈希,还可以使用普通哈希。

Description

visitrecurse伪指令用于树的递归处理。实际上,这将主要用于processing XML.

Visit

调用<#visit node>时,它将查找要调用的用户定义指令(如宏),该指令具有从节点名称(node?node_name)和名称空间(node?node_namespace)中减去的名称。名称推导规则:

  • 如果节点不支持节点名称空间(如 XML 中的文本节点),那么伪指令名称就是该节点的名称(node?node_name)。如果getNodeNamespace方法返回null,则节点不支持节点名称空间。

  • 如果节点确实支持节点名称空间(作为 XML 中的元素节点),则可以将从节点名称空间中得出的前缀附加在节点名称之前,并使用冒号作为分隔符(例如e:book)。前缀(如果有的话是否使用了前缀)取决于在FTL namespace中通过ftl指令的ns_prefixes参数注册的前缀,其中visit查找处理程序指令(与 FTL 名称空间相同) visit是从调用的,您将在后面看到)。具体来说,如果没有使用ns_prefixes注册的默认名称空间,则对于不属于任何名称空间的节点(当getNodeNamespace返回""时)不使用前缀。如果存在用ns_prefixes注册的默认名称空间,则对于不属于任何名称空间前缀N的节点,将使用该前缀,而对于属于默认节点名称空间的节点,则不使用前缀。否则,在两种情况下,都将使用与ns_prefixes关联的节点名称空间的前缀。如果没有与该节点的节点名称空间关联的前缀,那么visit的行为就好像没有找到具有正确名称的指令一样。

为其调用了用户定义的指令的节点可作为特殊变量.node使用。例:

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

输出将类似于:

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.

如果使用可选的using子句指定了一个或多个名称空间,则visit将仅在这些名称空间中查找指令,列表中较早指定的名称空间将获得优先级。如果未指定using子句,则将重用上一个未完成的visit调用的using子句指定的名称空间或名称空间序列。如果没有这样的未决visit调用,则使用当前名称空间。例如,如果执行此模板:

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

这是n1.ftl

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

这是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>

然后将打印:

n2.x
  n1.y
  n2.y

  Simply x

如果visit在两个 FTL 名称空间中都没有找到用户定义的指令,而该名称与前面描述的规则推导的名称相同,则它会尝试找到名称为@node_type的用户定义的指令,或者该节点不支持节点类型属性(即node?node_type返回未定义的变量),然后命名为@default。对于查找,它使用与前面所述相同的机制。如果仍然找不到用户定义的指令来处理该节点,则visit会停止并出错处理模板。在这方面,某些特定于 XML 的节点类型有特殊处理。参见:XML 处理指南/声明性 XML 处理/详细信息。例:

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

这将打印:

Handling node x

There was no specific handler for node y

Recurse

<#recurse>指令实际上是语法糖。它访问该节点的所有子节点(而不是节点本身)。所以,写:

<#recurse someNode using someLib>

相当于写:

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

但是,目标节点在recurse指令中是可选的。如果未指定目标节点,则仅使用.node。因此,简短指令<#recurse>等效于:

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

对于那些熟悉 XSLT 的人来说,<#recurse>非常类似于 XSLT 中的<xsl:apply-templates/>指令。

Fallback

如您先前所知,在visit指令的文档中,可能在多个 FTL 命名空间中搜索处理该节点的用户定义的指令。 fallback伪指令可以在被调用以处理节点的用户定义的伪指令中使用。它指示 FreeMarker 在其他名称空间(即,名称空间列表中当前调用的用户定义指令的名称空间之后的名称空间)中 continue 搜索用户定义的指令。 )。如果找到该节点的处理程序,则将调用该处理程序,否则fallback不执行任何操作。

在处理程序库上编写定制层的典型用法是,有时将处理传递给定制库:

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