Freemaker_入門+深入+開發指南+學習筆記


freemaker的基本語法

freemaker的基本語法:
<# ... > 中存放所有freemaker的內容,之外的內容全部原樣輸出。
<@ ... /> 是函數調用
兩個定界符內的內容中,第一個符號表示指令或者函數名,其后的跟隨參數。freemaker提供的控制包括如下: 
<#if condition><#elseif condition><#else></#if> 條件判斷
<#list hash_or_seq as var></#list> 遍歷hash表或者collection(freemaker稱作sequence)的成員 
<#macro name param1 param2 ... ><#nested param></#macro> 宏,無返回參數
<#function name param1 param2><#return val></#function> 函數,有返回參數
var?member_function(...) 用函數對var進行轉換,freemaker稱為build-ins。實際內部實現類似member_function(var, ...)
stringA[M .. N] 取子字符串,類似substring(stringA, M, N)
{key:value, key2:value2 ...} 直接定義一個hash表
[item0, item1, item2 ...] 直接定義一個序列
hash0[key0] 存取hash表中key對應的元素
seq0[5] 存取序列指定下標的元素 
<@function1 param0 param1 ... /> 調用函數function1
<@macro0 param0 param1 ; nest_param0 nest_param1 ...> nest_body </@macro> 調用宏,並處理宏的嵌套
<#assign var = value > 定義變量並初始化
<#local var = value> 在 macro 或者 function 中定義局部變量並初始化 
<#global var = value > 定義全局變量並初始化
${var} 輸出並替換為表達式的值
<#visit xmlnode> 調用macro匹配xmlnode本身及其子節點
<#recurse xmlnode> 調用macro匹配xmlnode的子節點

 

FreeMaker一篇通[【轉】

FreeMaker一篇通[【轉】

2007-08-09 19:38

FreeMaker一篇通

前言

Freemaker是一個強大的模板引擎,相比velocity而言,其強大的過程調用、遞歸和閉包回調功能讓freemaker可以完成幾乎所有我們所想的功能。從個人看法而言,freemaker完全有能力作為MDA的代碼輔助生成工具。
本文試圖越過傳統的概念性介紹,通過一組例子直接把讀者帶入到Freemaker應用的較高層階。

正文

大家看文章標題就應該知道,我想用一篇文章,把大家從對freemaker的陌生直接帶入到比較深入的境界,所以不想說一些基礎性的東西,如果大家不習慣我的表達方法,大可通過google去找習慣於自己閱讀方式的相關文章。

我用過velocity,最近才用freemaker,才知道我以前的選擇是錯了,因為velocity不支持過程的調用,所以我為velocity增加了很多的東西,寫了很多代碼,而且腳本也累贅得要命。freemaker首先吸引我的是它強大的過程調用和遞歸處理能力,其次則是xml風格的語法結構有着明顯的邊界,不象velocity要注意段落之間要留空格。所以我建議大家直接使用Freemaker,雖然freemaker沒有.net版本,我想不嵌入程序中使用的話,freemaker是絕對的首選。(題外話,誰有興趣移植一個NFreeMaker?)

在使用之前我們先要設置運行環境,在使用Freemaker的時候,我們需要下載相關的程序:
freemaker: http://freemarker.sourceforge.net/
fmpp: http://fmpp.sourceforge.net/

其中fmpp是一個freemaker的輔助工具,有了它,我們可以實現更多的功能。以下例子必須fmpp輔助。

這里我們首先提出問題。大家看如下的一個xml文件,雖然freemaker的能力不僅在於處理xml文件,但是用xml作為例子更直觀一些:

<?xml version='1.0' encoding="gb2312" ?>
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:DruleForm-Lite.xsd">
         <type name="Type1" >
             <labels>
                 <label lang="zh-CN" value="投保單"/>
             </labels>
             <field name="Field11" type="Float" lbound="1" ubound="1" >
                 <labels>
                     <label lang="zh-CN" value="投保單ID"/>
                 </labels>
             </field>
             <field name="Field12" type="String" lbound="1" ubound="*"/>
             <field name="Field13" type="Integer" lbound="1" ubound="*"/>
             <field name="Field14" type="Type2" lbound="1" ubound="*">
                 <type name="Type2">
                     <field name="Field21" type="String" lbound="1" ubound="*"/>
                     <field name="Field22" type="Integer" lbound="1" ubound="*"/>    
                 </type>
             </field>
             <field name="Field15" type="InsuranceProduct" lbound="1" ubound="*"/>
         <type>
         <type name="Type3">
             <field name="Field31" type="Type1" lbound="1" ubound="*" />
         </type>
     </types>

[代碼1]
我們的任務是把這個文件轉化為相應的C#代碼。大家先看轉換模板的代碼:

1<#ftl ns_prefixes={"ns": "urn:DruleForm-Lite.xsd"}> 
2<#-- 定義xml namespace,以便在以下代碼中使用,注意,ftl指令必須使用單獨的行 -->
3<@pp.setOutputEncoding encoding="gb2312" /> <#-- 使用fmpp提供的函數來設置輸出編碼 -->
4
5<#recurse doc> <#-- 根入口,代碼1部分的xml存放在變量doc中,doc變量的填充由fmpp根據config.fmpp中的配置進行 -->
6
7<#macro "ns:types"> <#-- xslt風格的匹配處理入口 -->
8<#recurse> <#-- 直接進行types節點內的匹配 -->
9</#macro>
10
11<#macro "ns:type"> <#-- 匹配type節點 -->
12 class ${.node.@name} <#-- 其中.node是保留字,表示當前節點,引用的@name是xslt風格 -->
13 {
14   <#recurse> <#-- 繼續匹配 -->
15 }
16</#macro>
17
18<#macro "ns:field">
19   public ${.node.@type}${.node.@name};
20</#macro>
21
22<#macro @element> <#-- 所有沒有定義匹配的節點到這里處理 -->
23</#macro>
24
25
[代碼2]

我們使用的配置文件設置如下:

sourceRoot: src
outputRoot: out
logFile: log.fmpp
modes: [
copy(common/**/*.*, resource/*.*)
     execute(*.ftl)
ignore(templates/*.*, .project, **/*.xml, xml/*.*, *.js)
]
removeExtensions: ftl
sourceEncoding: gb2312
data: {
doc: xml(freemaker.xml)
}

[代碼3]

然后我們在dos模式下運行指令:
E:\work\blogs\freemaker>f:\download\freemaker\fmpp\bin\fmpp

最后的輸出結果是這樣的,存放在文件out\freemaker.中:

     class Type1 
     {
         public Float Field11;
         public String Field12;
         public Integer Field13;
         public Type2 Field14;
         public Float Field15;
     }

     class Type3 
     {
         public Type1 Field31;
     }

[代碼4]

先來解釋一下freemaker的基本語法了,
<# ... > 中存放所有freemaker的內容,之外的內容全部原樣輸出。
<@ ... /> 是函數調用
兩個定界符內的內容中,第一個符號表示指令或者函數名,其后的跟隨參數。freemaker提供的控制包括如下:
<#if condition><#elseif condition><#else></#if> 條件判斷
<#list hash_or_seq as var></#list> 遍歷hash表或者collection(freemaker稱作sequence)的成員
<#macro name param1 param2 ... ><#nested param></#macro> 宏,無返回參數
<#function name param1 param2><#return val></#function>函數,有返回參數
var?member_function(...) 用函數對var進行轉換,freemaker稱為build-ins。實際內部實現類似member_function(var, ...)
stringA[M .. N] 取子字符串,類似substring(stringA, M, N)
{key:value, key2:value2 ...} 直接定義一個hash表
[item0, item1, item2 ...] 直接定義一個序列
hash0[key0] 存取hash表中key對應的元素
seq0[5] 存取序列指定下標的元素
<@function1 param0 param1 ... /> 調用函數function1
<@macro0 param0 param1 ; nest_param0 nest_param1 ...> nest_body </@macro> 調用宏,並處理宏的嵌套
<#assign var = value > 定義變量並初始化
<#local var = value> 在 macro 或者 function 中定義局部變量並初始化
<#global var = value > 定義全局變量並初始化
${var} 輸出並替換為表達式的值
<#visit xmlnode> 調用macro匹配xmlnode本身及其子節點
<#recurse xmlnode> 調用macro匹配xmlnode的子節點

[表1]


大家仔細對比xml文件,發現少了什么嗎?對了,少了一個Type2定義,我們把代碼2中的ns:type匹配(第11行)修改一下:

<#macro "ns:field">
   public ${.node.@type}${.node.@name};
   <#recurse > <#-- 深入處理子節點 -->
</#macro>


[代碼5]

結果輸出文件中的內容就變為如下:

     class Type1 
     {
         public Float Field11;
         public String Field12;
         public Integer Field13;
         public Type2 Field14;
         class Type2 
         {
             public String Field21;
             public Integer Field22;
         }
         public Float Field15;
     }

     class Type3 
     {
         public Type1 Field31;
     }

[代碼6]

如果各位有意向把Type2提到跟Type1和Type3同一級別的位置,那么我們要繼續修改代碼了。把代碼2的 <#recurse doc>行(第5行)修改成如下:

<#assign inner_types=pp.newWritableHash()> <#-- 調用fmpp功能函數,生成一個可寫的hash -->
<#recurse doc> <#-- 根入口,代碼1部分的xml存放在變量doc中,doc變量的填充由fmpp根據config.fmpp中的配置進行 -->
<#if inner_types?size gt 0 > <#-- 如果存放有類型 -->
<#list inner_types?values as node> <#-- 遍歷哈西表的值 -->
   <#visit node> <#-- 激活相應的macro處理,類似於xslt的apply-template。大家把visit改成recurse看一下不同的效果 -->
</#list>
</#if>
[代碼7]

同時把macro ns:field(第18行)修改成如下:

<#macro "ns:field">
   public ${.node.@type}${.node.@name};
   <#if .node["ns:type"]?has_content > <#-- 如果當前節點下存在type節點 -->
    <#local t = .node["ns:type"] >
    <@pp.set hash=inner_types key="${t.@name}" value=t /> <#-- 哈西表中增加內容,key為嵌套類型的name屬性,value為該類型節點 -->
   </#if>
</#macro>

[代碼8]

運行得到輸出文件類似這樣:

     class Type1 
     {
         public Float Field11;
         public String Field12;
         public Integer Field13;
         public Type2 Field14;
         public Float Field15;
     }

     class Type3 
     {
         public Type1 Field31;
     }

     class Type2 
     {
         public String Field21;
         public Integer Field22;
     }

[代碼9]

大家比較一下,看看我們修改的地方出現了哪些效果?然后記得大家要做另外2件事情,
1。把第一行修改成為<#ftl ns_prefixes={"D": "urn:DruleForm-Lite.xsd"}> ,然后把所有的 <#macro "ns:type"> 修改成<#macro type>,把所有的.node["ns:type"]修改成 .node.type,看看能不能運行?是不是覺得簡單方便些了?記住,第一行的那個D表示是default namespace的意思哦。
2。在第二行插入<#compress>,在最后一行添加</#compress>。再運行一下看看結果有什么不同?

一個例子下來,大家基本對freemaker有了一些感覺了,為了糾正大家認為freemaker就是一個xml處理工具的誤解,我們再來做一個簡單的實驗。這次我們要做的是一個正常的編程題目,做一個100以內的Fibonacci數列的程序。程序如下:

迭代次數:
<#list 1 .. 10 as n>
${n} = ${fibo(n)}
</#list>

<#compress>
<#function fibo n>
<#if n lte 1>
   <#return 1>
<#elseif n = 2>
   <#return 1>
<#else>
   <#return fibo(n-1) + fibo(n-2)>
</#if>
</#function>
</#compress>


[代碼10]

這個例子里邊有一些問題需要注意,大家看我的 #if n lte 1 這一行,為什么我這么寫?因為常規的大於小於號和xml的節點有沖突,為了避免問題,所以用 gt(>) gte(>=) lt(<) lte(<=) 來代表。

另外,復雜的字符串處理如何來做?就留作家庭作業吧,大家記得取substr的方法是 str[first .. last] 就可以了。如下的例子可能會給你一點提示:

<#assign str = "hello!$world!">
<#assign mid = (str?length + 1)/2-1 >
<#list mid .. 0 as cnt>
${str[(mid - cnt) .. (mid + cnt)]?left_pad(mid*2)}
</#list>

[代碼11]

最后,說一下非常有用的macro的nested指令,沒有它,也許freemaker會失去大部分的魅力。我個人認為這也是freemaker全面超越velocity的地方。大家先看一下代碼:

<#assign msg = "hello">
<@macro0 ; index >
${msg} ${index}
</@macro0>

<#macro macro0>
<#list 0 .. 10 as number>
   <#nested number>
</#list>
</#macro>


[代碼12]

這段代碼的作用就是一個閉包(closure)。我們用java的匿名類實現相同的功能就是這樣:

interface ICallback
{
public void call(int index);
}

void Main()
{
String msg = "hello";
macro0(
   new ICallback()
   {
    public void call(int index)
    {
     System.out.println(msg + index.toString());
    }
   }
);
}

void macro0(ICallback callback)
{
for(int i = 0; i < 10; ++i)
{
   callback.call(i);
}
}

freemaker學習筆記--設計指導 

 

<# ... > 中存放所有freemaker的內容,之外的內容全部原樣輸出。
<@ ... /> 是函數調用
兩個定界符內的內容中,第一個符號表示指令或者函數名,其后的跟隨參數。freemaker提供的控制包括如下:
<#if condition><#elseif condition><#else></#if> 條件判斷
<#list hash_or_seq as var></#list> 遍歷hash表或者collection(freemaker稱作sequence)的成員
<#macro name param1 param2 ... ><#nested param></#macro> 宏,無返回參數
<#function name param1 param2><#return val></#function>函數,有返回參數
var?member_function(...) 用函數對var進行轉換,freemaker稱為build-ins。實際內部實現類似member_function(var, ...)
stringA[M .. N] 取子字符串,類似substring(stringA, M, N)
{key:value, key2:value2 ...} 直接定義一個hash表
[item0, item1, item2 ...] 直接定義一個序列
hash0[key0] 存取hash表中key對應的元素
seq0[5] 存取序列指定下標的元素
<@function1 param0 param1 ... /> 調用函數function1
<@macro0 param0 param1 ; nest_param0 nest_param1 ...> nest_body < /@macro > 調用宏,並處理宏的嵌套
<#assign var = value > 定義變量並初始化
<#local var = value> 在 macro 或者 function 中定義局部變量並初始化
<#global var = value > 定義全局變量並初始化
${var} 輸出並替換為表達式的值
<#visit xmlnode> 調用macro匹配xmlnode本身及其子節點
<#recurse xmlnode> 調用macro匹配xmlnode的子節點 


<#if condition > </#if> 
<#list SequenceVar as variable > repeatThis</#list>
<#include "/copyright_footer.html">

一個ftl標記不能放在另外一個ftl標記里面,但是注釋標記能夠放在ftl標記里面。

系統預定義指令采用<#...></#>
用戶自定義指令采用<@...></@>

hash片段可以采用: products[10..19] or products[5..] 的格式。

序列也可以做加法計算:passwords +{"joe":"secret42"}

缺省值: name!"unknown" 或者 (user.name)!"unknown" 或者 name! 或者 (user.name)!

null值檢查: name?? or (user.name)??

轉義列表:

Escape sequence

Meaning

\"

Quotation mark (u0022)

\'

Apostrophe (a.k.a. apostrophe-quote) (u0027)

\\

Back slash (u005C)

\n

Line feed (u000A)

\r

Carriage return (u000D)

\t

Horizontal tabulation (a.k.a. tab) (u0009)

\b

Backspace (u0008)

\f

Form feed (u000C)

\l

Less-than sign: <

\g

Greater-than sign: >

\a

Ampersand: &

\{

Curly bracket: {

\xCode

Character given with its hexadecimal Unicode code (UCS code)


如果想打印${,則需要將{轉義,可以寫成"$\{user}",或者可以用生字符(r指令):$(r "${xx}"}

序列構成:<#list ["winter","spring", "summer", "autumn"] asx>${x}</#list>

不同的對象可以存放在一個序列里面,比如:[2 + 2,[1, 2, 3, 4], "whatnot"]. 第一個是數字,第二個是序列,第三個是字符串。

可用采用start..end的方式來定義一個數字序列,start可以小於end,同時,end也可以省略。

hash取值支持一下四種模式:book.author.name, book["author"].name, book.author.["name"], book["author"]["name"].

特殊變量是指freemaker引擎本身定義的變量。訪問時,以.variable_name的語法訪問。

變量表達式支持嵌套模式,比如:${"Hello${user}!"}。

變量表達式在指令中的使用情況:
變量表達式可以在指令中,用“”的方式存在,不如:<#include"/footer/${company}.html">. 
但是不允許下面的方式存在: <#if ${isBig}>Wow!</#if>, 正確寫法是:<#ifisBig>Wow!</#if>. 
而且 <#if"${isBig}">Wow!</#if>寫法也不正確,因為"${isBig}"返回的是字符串,不是boolean類型。

字符串中取字符或字符串采用以下語法:${user[0]},${user[0..2]}${user[4..]},${user?string(4)}

序列操作:
   加法:<#list ["Joe", "Fred"] + ["Julia","Kate"] as user> 但要注意串聯之后的讀取速度變慢。
   子序列:seq[1..4]

序列和hash的串聯都只能用於兩個相加,不能有多個相加的模式,hash相加,如果兩個相加的hash存在相同的key,則后面會覆蓋前面的。

在使用>=或者>時,需要注意一些問題,因為freemaker會將>解釋成標記的關閉符,為了解決這個問題,需要在表達式加上括號,比如: <#if (x > y)>. 或者使用 &gt; and &lt符號來代替。

無值變量(包括無該變量,null,返回void,無屬性等):unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!
缺省值可以是任何類型,不一定是數字,比如:hits!0 或者 colors!["red","green", "blue"]. 

如果缺省值忽略,那么將會默認為空串、空序列或者空hash,因為freemarker支持多類型的值。不過要讓默認值為0或false,則不能省略缺省值。

非頂層變量的無值處理:
   product.color!"red":只處理product不為空,color為空的缺省值處理,如果product為空,則freemaker會拋出異常。(product.color)!"red":則會處理product為空,color為空,或者沒有color屬性的無值情況。

無值變量的判斷操作:unsafe_expr?? or (unsafe_expr)??

判斷變量是否是無值。

普通變量插入方式: ${expression},${3+5);
數字變量插入方式: #{expression} or #{expressionformat}:過期。
變量只能用於文本區或者是字符串里面,比如:<h1>Hello${name}!</h1>以及 <#include"/footer/${company}.html">

數字值的插入:根據缺省的number_format輸出,以及可以通過setting來達到設置數字格式的目的,也可以通過內置函數string來改變輸出格式。

日期類型的格式設置:date_format, time_format 和 datetime_format


定義宏:
   不帶參數:<#macro 宏名>...</#macro>,引用<@宏名 />
   帶參數:<#macro宏名 參數...>...</#macro>,引用<@宏名 參數1=值1.../>,帶有參數的宏,調用是參數的值必須和參數的個數相同。當然也可以在宏定義時給參數一些默認值。比如:<#macro greet person color="black">


宏里面的嵌套內容:
<#macro border>
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    <#nested>
  </tr></td></table>
</#macro> 
在宏的定義body中加入<#nested>指令。嵌套的內容可以是任何正確的ftl塊。

宏的本地變量在嵌套內容中是不可見的。

宏定義時,<#nest>指令相當於調用定義的內容,而使用宏時,nest body相當於定義。

<#macro repeat count>  <#list 1..count as x>    <#nested x, x/2, x==count>  </#list></#macro><@repeatcount=4 ; c, halfc, last>  ${c}.${halfc}<#iflast> Last!</#if></@repeat> 



定義變量:
   在模板中定義的變量將會隱藏(不是更改)數據模型根下面的同名的變量。

模板中的3種類型變量:
   1:plain variables,能夠在模板中的任何地方訪問,一個模板include另外一個模板,也可以訪問被包含模板的變量。可以通過assign或者macro指令產生或替換變量。
   如果要訪問數據模型中的變量,則可以通過.global來訪問:
   <#assign user = "JoeHider">
   ${user}         <#-- prints: Joe Hider -->
   ${.globals.user} <#-- prints: Big Joe--> 
   2:Local variables,宏定義body中用local指令創建或者替換。
   3:Loop variables:由list指令產生。


namespaces:
<#import "/lib/my_test.ftl" as my> <#-- the hashcalled "my" will be the "gate" -->
<@my.copyright date="1999-2002"/>
${my.mail} 

設置命名空間里面的變量:<#assignmail="jsmith@other.com" in my>

命名空間與數據模型:命名空間的ftl可以訪問數據模型的變量。同樣命名空間的變量也會隱藏數據模型中同名的變量。

空白問題:
1:White-space stripping,默認為enabled,清除ftl標記帶來的空白以及縮進。處理模板的空白。
2:t, rt, lt指令。
3:ftl的參數strip_text.

用compress directive或者transform來處理輸出。
<#compress>...</#compress>:消除空白行。
<@compress single_line=true>.../@compress將輸出壓縮為一行。

可替換語法:
freemarker可用"["代替"<".在模板的文件開頭加上[#ftl].

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1791781

Spring MVC 使用 Freemarker

·        Freemaker是取代JSP的又一種視圖技術,和Velocity非常類似,但是它比Velocity多了一個格式化的功能,因此使用上較Velocity方便一點,但語法也稍微復雜一些。

將Velocity替換為Freemarker只需要改動一些配置文件,同樣,在Spring中使用Freemarker也非常方便,根本無須與Freemarker的API打交道。我們將Spring_Velocity工程復制一份,命名為Spring_Freemarker,結構如圖7-44所示。

圖7-44 

修改dispatcher-servlet.xml,將velocityConfig刪除,修改viewResolver為FreeMarker ViewResolver,並添加一個freemarkerConfig。

<!-- 使用Freemarker視圖解析器 -->

<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">

    <propertyname="contentType" value="text/html;charset=UTF-8" />

    <property name="prefix"value="/" />

    <property name="suffix"value=".html" />

</bean>

<!-- 配置Freemarker-->

<bean id="freemarkerConfig"class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">

    <!-- 視圖資源位置 -->

    <propertyname="templateLoaderPath" value="/" />

    <propertyname="defaultEncoding" value="UTF-8" />

</bean>

模板test.html可以稍做修改,加入Freemarker內置的格式化功能來定制Date類型的輸出格式。

<html>

<head>

    <metahttp-equiv="Content-Type" content="text/html;charset=utf-8">

   <title>Spring_Freemarker</title>

</head>

<body>

    <h3>Hello, ${name}, it is${time?string("yyyy-MM-dd HH:mm:ss")}</h3>

</body>

</html>

添加freemarker.jar到web/WEB-INF/lib目錄后,啟動Resin,可以看到由Freemarker渲染的頁面

 

 

FreeMarker入門文章引用自:

1、快速入門 
(1)模板 + 數據模型 = 輸出 
l         FreeMarker基於設計者和程序員是具有不同專業技能的不同個體的觀念 
l         他們是分工勞動的:設計者專注於表示——創建HTML文件、圖片、Web頁面的其它可視化方面;程序員創建系統,生成設計頁面要顯示的數據 
l         經常會遇到的問題是:在Web頁面(或其它類型的文檔)中顯示的信息在設計頁面時是無效的,是基於動態數據的 
l         在這里,你可以在HTML(或其它要輸出的文本)中加入一些特定指令,FreeMarker會在輸出頁面給最終用戶時,用適當的數據替代這些代碼 
l         下面是一個例子: 
<html> 
<head> 
  <title>Welcome!</title> 
</head> 
<body> 
  <h1>Welcome ${user}!</h1> 
  <p>Our latest product: 
  <ahref="${latestProduct.url}">${latestProduct.name}</a>! 
</body> 
</html>   
l         這個例子是在簡單的HTML中加入了一些由${…}包圍的特定代碼,這些特定代碼是FreeMarker的指令,而包含FreeMarker的指令的文件就稱為模板(Template)
l         至於user、latestProduct.url和latestProduct.name來自於數據模型(data model) 
l         數據模型由程序員編程來創建,向模板提供變化的信息,這些信息來自於數據庫、文件,甚至於在程序中直接生成 
l         模板設計者不關心數據從那兒來,只知道使用已經建立的數據模型 
l         下面是一個可能的數據模型: 
(root) 
  | 
  +- user = "Big Joe" 
  | 
  +- latestProduct 
      | 
      +- url ="products/greenmouse.html" 
      | 
      +- name = "green mouse" 
l         數據模型類似於計算機的文件系統,latestProduct可以看作是目錄,而user、url和name看作是文件,url和name文件位於latestProduct目錄中(這只是一個比喻,實際並不存在)
l         當FreeMarker將上面的數據模型合並到模板中,就創建了下面的輸出: 
<html> 
<head> 
  <title>Welcome!</title> 
</head> 
<body> 
  <h1>Welcome Big Joe!</h1> 
  <p>Our latest product: 
  <a href="products/greenmouse.html">greenmouse</a>! 
</body> 
</html>   
(2)數據模型 
l         典型的數據模型是樹型結構,可以任意復雜和深層次,如下面的例子: 
(root) 
  | 
  +- animals 
  |   | 
  |   +- mouse 
  |   |   |   
  |   |   +- size = "small" 
  |   |   |   
  |   |   +- price = 50 
  |   | 
  |   +- elephant 
  |   |   |   
  |   |   +- size = "large" 
  |   |   |   
  |   |   +- price = 5000 
  |   | 
  |   +- python 
  |       |   
  |       +- size = "medium" 
  |       |   
  |       +- price = 4999 
  | 
  +- test = "It is a test" 
  | 
  +- whatnot 
      | 
      +- because = "don't know" 
l         類似於目錄的變量稱為hashes,包含保存下級變量的唯一的查詢名字 
l         類似於文件的變量稱為scalars,保存單值 
l         scalars保存的值有兩種類型:字符串(用引號括起,可以是單引號或雙引號)和數字(不要用引號將數字括起,這會作為字符串處理)
l         對scalars的訪問從root開始,各部分用“.”分隔,如animals.mouse.price
l         另外一種變量是sequences,和hashes類似,只是不使用變量名字,而使用數字索引,如下面的例子:
(root) 
  | 
  +- animals 
  |   | 
  |   +- (1st) 
  |   |   | 
  |   |   +- name = "mouse" 
  |   |   | 
  |   |   +- size = "small" 
  |   |   | 
  |   |   +- price = 50 
  |   | 
  |   +- (2nd) 
  |   |   | 
  |   |   +- name = "elephant" 
  |   |   | 
  |   |   +- size = "large" 
  |   |   | 
  |   |   +- price = 5000 
  |   | 
  |   +- (3rd) 
  |       | 
  |       +- name = "python" 
  |       | 
  |       +- size = "medium" 
  |       | 
  |       +- price = 4999 
  | 
  +- whatnot 
      | 
      +- fruits 
          | 
          +- (1st) ="orange" 
          | 
          +- (2nd) ="banana" 
l         這種對scalars的訪問使用索引,如animals[0].name 
(3)模板 
l         在FreeMarker模板中可以包括下面三種特定部分: 
?         ${…}:稱為interpolations,FreeMarker會在輸出時用實際值進行替代 
?         FTL標記(FreeMarker模板語言標記):類似於HTML標記,為了與HTML標記區分,用#開始(有些以@開始,在后面敘述) 
?         注釋:包含在<#--和-->(而不是<!--和-->)之間 
l         下面是一些使用指令的例子: 
?         if指令 
<#if animals.python.price < animals.elephant.price> 
  Pythons are cheaper than elephants today. 
<#else> 
  Pythons are not cheaper than elephants today. 
</#if>   
?         list指令 
<p>We have these animals: 
<table border=1> 
  <tr><th>Name<th>Price 
  <#list animals as being> 
  <tr><td>${being.name}<td>${being.price} Euros 
  </#list> 
</table>   
輸出為: 
<p>We have these animals: 
<table border=1> 
  <tr><th>Name<th>Price 
  <tr><td>mouse<td>50 Euros 
  <tr><td>elephant<td>5000 Euros 
  <tr><td>python<td>4999 Euros 
</table>   
?         include指令 
<html> 
<head> 
  <title>Test page</title> 
</head> 
<body> 
  <h1>Test page</h1> 
  <p>Blah blah... 
<#include "/copyright_footer.html"> 
</body> 
</html>   
?         一起使用指令 
<p>We have these animals: 
<table border=1> 
  <tr><th>Name<th>Price 
  <#list animals as being> 
  <tr> 
    <td> 
      <#if being.size ="large"><b></#if> 
      ${being.name} 
      <#if being.size = "large"></b></#if>
    <td>${being.price} Euros 
  </#list> 
</table>  

 

FreeMarker設計指南

快速入門

(1)模板 + 數據模型 = 輸出 
FreeMarker基於設計者和程序員是具有不同專業技能的不同個體的觀念他們是分工勞動的:
設計者專注於表示——創建HTML文件、圖片、Web頁面的其它可視化方面;
程序員創建系統,生成設計頁面要顯示的數據。
經常會遇到的問題是:在Web頁面(或其它類型的文檔)中顯示的信息在設計頁面時是無效的,是基於動態數據的。在這里,你可以在HTML(或其它要輸出的文本)中加入一些特定指令,FreeMarker會在輸出頁面給最終用戶時,用適當的數據替代這些代碼。
 
先來解釋一下freemaker的基本語法了,
<# ... > 中存放所有freemaker的內容,之外的內容全部原樣輸出。
<@ ... /> 是函數調用
兩個定界符內的內容中,第一個符號表示指令或者函數名,其后的跟隨參數。freemaker提供的控制包括如下:
<#if condition><#elseif condition><#else> 條件判斷
<#list hash_or_seq as var> 遍歷hash表或者collection(freemaker稱作sequence)的成員
<#macro name param1 param2 ... ><#nested param> 宏,無返回參數
<#function name param1 param2><#return val>函數,有返回參數
var?member_function(...) 用函數對var進行轉換,freemaker稱為build-ins。實際內部實現類似member_function(var, ...)
stringA[M .. N] 取子字符串,類似substring(stringA, M, N)
{key:value, key2:value2 ...} 直接定義一個hash表
[item0, item1, item2 ...] 直接定義一個序列
hash0[key0] 存取hash表中key對應的元素
seq0[5] 存取序列指定下標的元素
<@function1 param0 param1 ... /> 調用函數function1
<@macro0 param0 param1 ; nest_param0 nest_param1 ...> nest_body </@macro> 調用宏,並處理宏的嵌套
<#assign var = value > 定義變量並初始化
<#local var = value> 在 macro 或者 function 中定義局部變量並初始化
<#global var = value > 定義全局變量並初始化
${var} 輸出並替換為表達式的值
<#visit xmlnode> 調用macro匹配xmlnode本身及其子節點
<#recurse xmlnode> 調用macro匹配xmlnode的子節點
 
下面是一個例子: 
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>  
這個例子是在簡單的HTML中加入了一些由${…}包圍的特定代碼,這些特定代碼是FreeMarker的指令,而包含FreeMarker的指令的文件就稱為模板(Template)。
至於user、latestProduct.url和latestProduct.name來自於數據模型(data model)。
數據模型由程序員編程來創建,向模板提供變化的信息,這些信息來自於數據庫、文件,甚至於在程序中直接生成。
模板設計者不關心數據從那兒來,只知道使用已經建立的數據模型。
下面是一個可能的數據模型: 
(root)
  |
  +- user = "Big Joe"
  |
  +- latestProduct
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"
數據模型類似於計算機的文件系統,latestProduct可以看作是目錄。 
2、數據模型 
(1)基礎 
在快速入門中介紹了在模板中使用的三種基本對象類型:scalars、hashes 和sequences,其實還可以有其它更多的能力: 
scalars:存儲單值 
hashes:充當其它對象的容器,每個都關聯一個唯一的查詢名字 
sequences:充當其它對象的容器,按次序訪問 
方法:通過傳遞的參數進行計算,以新對象返回結果 
用戶自定義FTL標記:宏和變換器 
通常每個變量只具有上述的一種能力,但一個變量可以具有多個上述能力,如下面的例子: 
(root)
 |
 +- mouse = "Yerri"
     |
     +- age = 12
     |
     +- color = "brown">  
mouse既是scalars又是hashes,將上面的數據模型合並到下面的模板: 
${mouse}       <#-- use mouse as scalar -->
${mouse.age}   <#-- use mouse as hash -->
${mouse.color} <#-- use mouse as hash -->  
輸出結果是: 
Yerri
12
brown  
(2)Scalar變量 
Scalar變量存儲單值,可以是: 
字符串:簡單文本,在模板中使用引號(單引號或雙引號)括起 
數字:在模板中直接使用數字值 
日期:存儲日期/時間相關的數據,可以是日期、時間或日期-時間(Timestamp);通常情況,日期值由程序員加到數據模型中,設計者只需要顯示它們 
布爾值:true或false,通常在<#if …>標記中使用 
(3)hashes 、sequences和集合 
有些變量不包含任何可顯示的內容,而是作為容器包含其它變量,者有兩種類型: 
hashes:具有一個唯一的查詢名字和它包含的每個變量相關聯 
sequences:使用數字和它包含的每個變量相關聯,索引值從0開始 
集合變量通常類似sequences,除非無法訪問它的大小和不能使用索引來獲得它的子變量;集合可以看作只能由<#list …>指令使用的受限sequences 
(4)方法 
方法變量通常是基於給出的參數計算值。 
下面的例子假設程序員已經將方法變量avg放到數據模型中,用來計算數字平均值: 
The average of 3 and 5 is: ${avg(3, 5)}
The average of 6 and 10 and 20 is: ${avg(6, 10, 20)}
The average of the price of python and elephant is: 
    ${avg(animals.python.price, animals.elephant.price)}
(5)宏和變換器 
宏和變換器變量是用戶自定義指令(自定義FTL標記),會在后面講述這些高級特性 
(6)節點 
節點變量表示為樹型結構中的一個節點,通常在XML處理中使用,會在后面的專門章節中講 
3、模板 
(1)整體結構 
模板使用FTL(FreeMarker模板語言)編寫,是下面各部分的一個組合: 
文本:直接輸出 
Interpolation:由${和},或#{和}來限定,計算值替代輸出 
FTL標記:FreeMarker指令,和HTML標記類似,名字前加#予以區分,不會輸出 
注釋:由<#--和-->限定,不會輸出 
下面是以一個具體模板例子: 
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <#-- Greet the user with his/her name -->
  <h1>Welcome ${user}!</h1>
  <p>We have these animals:
  <ul>
  <#list animals as being>
    <li>${being.name} for ${being.price} Euros
  </#list>
  </ul>
</body>
</html>  
注意事項: 
FTL區分大小寫,所以list是正確的FTL指令,而List不是;${name}和${NAME}是不同的 
Interpolation只能在文本中使用 
FTL標記不能位於另一個FTL標記內部,例如: 
<#if <#include 'foo'>='bar'>...</if>
注釋可以位於FTL標記和Interpolation內部,如下面的例子: 
<h1>Welcome ${user <#-- The name of user -->}!</h1>
<p>We have these animals:
<ul>
<#list <#-- some comment... --> animals as <#-- again... --> being>
...  
余的空白字符會在模板輸出時移除 
(2)指令 
在FreeMarker中,使用FTL標記引用指令。有三種FTL標記,這和HTML標記是類似的: 
開始標記:<#directivename parameters> 
結束標記:</#directivename> 
空內容指令標記:<#directivename parameters/> 
有兩種類型的指令:預定義指令和用戶定義指令。 
用戶定義指令要使用@替換#,如<@mydirective>...</@mydirective>(會在后面講述)。 
FTL標記不能夠交叉,而應該正確的嵌套,如下面的代碼是錯誤的: 
<ul>
<#list animals as being>
  <li>${being.name} for ${being.price} Euros
  <#if use = "Big Joe">
     (except for you)
</#list>
</#if> <#-- WRONG! -->
</ul>  
如果使用不存在的指令,FreeMarker不會使用模板輸出,而是產生一個錯誤消息。 
FreeMarker會忽略FTL標記中的空白字符,如下面的例子: 
<#list
  animals       as
     being
>
${being.name} for ${being.price} Euros
</#list    >  
但是,<、</和指令之間不允許有空白字符。 
(3)表達式 
直接指定值 
字符串 
使用單引號或雙引號限定 
如果包含特殊字符需要轉義,如下面的例子: 
${"It's \"quoted\" and
this is a backslash: \\"}

${'It\'s "quoted" and
this is a backslash: \\'} 
輸出結果是: 
It's "quoted" and
this is a backslash: \

It's "quoted" and
this is a backslash: \ 
下面是支持的轉義序列: 
轉義序列 
含義 
\" 
雙引號(u0022) 
\' 
單引號(u0027) 
 
反斜杠(u005C) 
\n 
換行(u000A) 
\r 
Return (u000D) 
\t 
Tab (u0009) 
\b 
Backspace (u0008) 
\f 
Form feed (u000C) 
\l 
< 
\g 
> 
\a 
& 
\{ 
{ 
\xCode 
4位16進制Unicode代碼 
有一類特殊的字符串稱為raw字符串,被認為是純文本,其中的\和{等不具有特殊含義,該類字符串在引號前面加r,下面是一個例子: 
${r"${foo}"}

${r"C:\foo\bar"}  
輸出的結果是: 
${foo}

C:\foo\bar  
數字 
直接輸入,不需要引號 
精度數字使用“.”分隔,不能使用分組符號 
目前版本不支持科學計數法,所以“1E3”是錯誤的 
不能省略小數點前面的0,所以“.5”是錯誤的 
數字8、+8、08和8.00都是相同的 
布爾值 
true和false,不使用引號 
序列 
由逗號分隔的子變量列表,由方括號限定,下面是一個例子: 
<#list ["winter", "spring", "summer", "autumn"] as x>
${x}
</#list> 
輸出的結果是: 
winter
spring
summer
autumn
列表的項目是表達式,所以可以有下面的例子: 
[2 + 2, [1, 2, 3, 4], "whatnot"]
可以使用數字范圍定義數字序列,例如2..5等同於[2, 3, 4, 5],但是更有效率,注意數字范圍沒有方括號 
可以定義反遞增的數字范圍,如5..2 
散列(hash) 
由逗號分隔的鍵/值列表,由大括號限定,鍵和值之間用冒號分隔,下面是一個例子: 
{"name":"green mouse", "price":150}
鍵和值都是表達式,但是鍵必須是字符串 
獲取變量 
頂層變量: ${variable},變量名只能是字母、數字、下划線、$、@和#的組合,且不能以數字開頭 
從散列中獲取數據 
可以使用點語法或方括號語法,假設有下面的數據模型: 
(root)
 |
 +- book
 |   |
 |   +- title = "Breeding green mouses"
 |   |
 |   +- author
 |       |
 |       +- name = "Julia Smith"
 |       |
 |       +- info = "Biologist, 1923-1985, Canada"
 |
 +- test = "title" 
下面都是等價的: 
book.author.name
book["author"].name
book.author.["name"]
book["author"]["name"]
使用點語法,變量名字有頂層變量一樣的限制,但方括號語法沒有該限制,因為名字是任意表達式的結果 
從序列獲得數據:和散列的方括號語法語法一樣,只是方括號中的表達式值必須是數字;注意:第一個項目的索引是0 
序列片斷:使用[startIndex..endIndex]語法,從序列中獲得序列片斷(也是序列);startIndex和endIndex是結果為數字的表達式 
特殊變量:FreeMarker內定義變量,使用.variablename語法訪問 
字符串操作 
Interpolation(或連接操作) 
可以使用${..}(或#{..})在文本部分插入表達式的值,例如: 
${"Hello ${user}!"}

${"${user}${user}${user}${user}"}  
可以使用+操作符獲得同樣的結果 
${"Hello " + user + "!"}

${user + user + user + user}
${..}只能用於文本部分,下面的代碼是錯誤的: 
<#if ${isBig}>Wow!</#if>

<#if "${isBig}">Wow!</#if>
應該寫成: 
<#if isBig>Wow!</#if>
子串 
例子(假設user的值為“Big Joe”): 
${user[0]}${user[4]}

${user[1..4]}
結果是(注意第一個字符的索引是0): 
BJ

ig J 
序列操作 
連接操作:和字符串一樣,使用+,下面是一個例子: 
<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
- ${user}
</#list>
輸出結果是: 
- Joe
- Fred
- Julia
- Kate
散列操作 
連接操作:和字符串一樣,使用+,如果具有相同的key,右邊的值替代左邊的值,例如: 
<#assign ages = {"Joe":23, "Fred":25} + {"Joe":30, "Julia":18}>
- Joe is ${ages.Joe}
- Fred is ${ages.Fred}
- Julia is ${ages.Julia}  
輸出結果是: 
- Joe is 30
- Fred is 25
- Julia is 18  
算術運算 
+、-、×、/、%,下面是一個例子: 
${x * x - 100}
${x / 2}
${12 % 10}
輸出結果是(假設x為5): 
-75
2.5
2  
操作符兩邊必須是數字,因此下面的代碼是錯誤的: 
${3 * "5"} <#-- WRONG! -->  
使用+操作符時,如果一邊是數字,一邊是字符串,就會自動將數字轉換為字符串,例如: 
${3 + "5"}  
輸出結果是: 
35
使用內建的int(后面講述)獲得整數部分,例如: 
${(x/2)?int}
${1.1?int}
${1.999?int}
${-1.1?int}
${-1.999?int}
輸出結果是(假設x為5): 
2
1
1
-1
-1
比較操作符 
使用=(或==,完全相等)測試兩個值是否相等,使用!= 測試兩個值是否不相等 
=和!=兩邊必須是相同類型的值,否則會產生錯誤,例如<#if 1 = "1">會引起錯誤 
Freemarker是精確比較,所以對"x"、"x "和"X"是不相等的 
對數字和日期可以使用<、<=、>和>=,但不能用於字符串 
由於Freemarker會將>解釋成FTL標記的結束字符,所以對於>和>=可以使用括號來避免這種情況,例如<#if (x > y)> 
另一種替代的方法是,使用lt、lte、gt和gte來替代<、<=、>和>= 
邏輯操作符 
&&(and)、||(or)、!(not),只能用於布爾值,否則會產生錯誤 
例子: 
<#if x < 12 && color = "green">
  We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
  It's not hot.
</#if>  
內建函數 
內建函數的用法類似訪問散列的子變量,只是使用“?”替代“.”,下面列出常用的一些函數 
 
字符串使用的: 
html:對字符串進行HTML編碼 
cap_first:使字符串第一個字母大寫 
lower_case:將字符串轉換成小寫 
upper_case:將字符串轉換成大寫 
trim:去掉字符串前后的空白字符 
 
序列使用的: 
size:獲得序列中元素的數目 
 
數字使用的: 
int:取得數字的整數部分(如-1.9?int的結果是-1) 
例子(假設test保存字符串"Tom & Jerry"): 
${test?html}
${test?upper_case?html}
輸出結果是: 
Tom &amp; Jerry
TOM &amp; JERRY  
操作符優先順序 
操作符組 
操作符 
后綴 
[subvarName] [subStringRange] . (methodParams) 
一元 
+expr、-expr、! 
內建 
? 
乘法 
*、 / 、% 
加法 
+、- 
關系 
<、>、<=、>=(lt、lte、gt、gte) 
相等 
==(=)、!= 
邏輯and 
&& 
邏輯or 
雙豎線
數字范圍 
.. 
(4)Interpolation 
Interpolation有兩種類型: 
通用Interpolation:${expr} 
數字Interpolation:#{expr}或#{expr; format} 
注意:Interpolation只能用於文本部分 
通用Interpolation 
插入字符串值:直接輸出表達式結果 
插入數字值:根據缺省格式(由#setting指令設置)將表達式結果轉換成文本輸出;可以使用內建函數string格式化單個Interpolation,下面是一個例子: 
<#setting number_format="currency"/>
<#assign answer=42/>
${answer}
${answer?string}  <#-- the same as ${answer} -->
${answer?string.number}
${answer?string.currency}
${answer?string.percent} 
輸出結果是: 
$42.00
$42.00
42
$42.00
4,200%
插入日期值:根據缺省格式(由#setting指令設置)將表達式結果轉換成文本輸出;可以使用內建函數string格式化單個Interpolation,下面是一個使用格式模式的例子: 
${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")}
${lastUpdated?string("EEE, MMM d, ''yy")}
${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")}  
輸出的結果類似下面的格式: 
2003-04-08 21:24:44 Pacific Daylight Time
Tue, Apr 8, '03
Tuesday, April 08, 2003, 09:24:44 PM (PDT)
插入布爾值:根據缺省格式(由#setting指令設置)將表達式結果轉換成文本輸出;可以使用內建函數string格式化單個Interpolation,下面是一個例子: 
<#assign foo=true/>
${foo?string("yes", "no")}
輸出結果是: 
yes
數字Interpolation的#{expr; format}形式可以用來格式化數字,format可以是: 
mX:小數部分最小X位 
MX:小數部分最大X位 
例子: 
<#-- If the language is US English the output is: -->
<#assign x=2.582/>
<#assign y=4/>
#{x; M2}   <#-- 2.58 -->
#{y; M2}   <#-- 4    -->
#{x; m1}   <#-- 2.6 -->
#{y; m1}   <#-- 4.0 -->
#{x; m1M2} <#-- 2.58 -->
#{y; m1M2} <#-- 4.0  -->  
4、雜項 
(1)用戶定義指令 
宏和變換器變量是兩種不同類型的用戶定義指令,它們之間的區別是宏是在模板中使用macro指令定義,而變換器是在模板外由程序定義,這里只介紹宏 
基本用法 
宏是和某個變量關聯的模板片斷,以便在模板中通過用戶定義指令使用該變量,下面是一個例子: 
<#macro greet>
  <font size="+2">Hello Joe!</font>
</#macro>  
作為用戶定義指令使用宏變量時,使用@替代FTL標記中的# 
<@greet></@greet>
如果沒有體內容,也可以使用: 
<@greet/>
參數 
在macro指令中可以在宏變量之后定義參數,如: 
<#macro greet person>
  <font size="+2">Hello ${person}!</font>
</#macro> 
可以這樣使用這個宏變量: 
<@greet person="Fred"/> and <@greet person="Batman"/> 
輸出結果是: 
  <font size="+2">Hello Fred!</font>

 and   <font size="+2">Hello Batman!</font>
宏的參數是FTL表達式,所以下面的代碼具有不同的意思: 
<@greet person=Fred/>
這意味着將Fred變量的值傳給person參數,該值不僅是字符串,還可以是其它類型,甚至是復雜的表達式 
可以有多參數,下面是一個例子: 
<#macro greet person color>
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro> 
可以這樣使用該宏變量: 
<@greet person="Fred" color="black"/> 
其中參數的次序是無關的,因此下面是等價的: 
<@greet color="black" person="Fred"/>
只能使用在macro指令中定義的參數,並且對所有參數賦值,所以下面的代碼是錯誤的: 
<@greet person="Fred" color="black" background="green"/>
<@greet person="Fred"/>
可以在定義參數時指定缺省值,如: 
<#macro greet person color="black">
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>  
這樣<@greet person="Fred"/>就正確了 
宏的參數是局部變量,只能在宏定義中有效 
嵌套內容 
用戶定義指令可以有嵌套內容,使用<#nested>指令執行指令開始和結束標記之間的模板片斷 
例子: 
<#macro border>
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    <#nested>
  </tr></td></table>
</#macro>  
這樣使用該宏變量: 
<@border>The bordered text</@border>
輸出結果: 
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    The bordered text
  </tr></td></table>
<#nested>指令可以被多次調用,例如: 
<#macro do_thrice>
  <#nested>
  <#nested>
  <#nested>
</#macro>
<@do_thrice>
  Anything.
</@do_thrice>  
輸出結果: 
  Anything.
  Anything.
  Anything. 
嵌套內容可以是有效的FTL,下面是一個有些復雜的例子: <@border> <ul> <@do_thrice> <li><@greet person="Joe"/> </@do_thrice> </ul> </@border> }}} 輸出結果: 
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
      <ul>
    <li><font size="+2">Hello Joe!</font>
    <li><font size="+2">Hello Joe!</font>
    <li><font size="+2">Hello Joe!</font>
  </ul>
  </tr></td></table>  
宏定義中的局部變量對嵌套內容是不可見的,例如: 
<#macro repeat count>
  <#local y = "test">
  <#list 1..count as x>
    ${y} ${count}/${x}: <#nested>
  </#list>
</#macro>
<@repeat count=3>${y?default("?")} ${x?default("?")} ${count?default("?")}</@repeat>
輸出結果: 
    test 3/1: ? ? ?
    test 3/2: ? ? ?
    test 3/3: ? ? ?
在宏定義中使用循環變量 
用戶定義指令可以有循環變量,通常用於重復嵌套內容,基本用法是:作為nested指令的參數傳遞循環變量的實際值,而在調用用戶定義指令時,在<@…>開始標記的參數后面指定循環變量的名字 
例子: 
<#macro repeat count>
  <#list 1..count as x>
    <#nested x, x/2, x==count>
  </#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>  
輸出結果: 
  1. 0.5
  2. 1
  3. 1.5
  4. 2 Last!
指定的循環變量的數目和用戶定義指令開始標記指定的不同不會有問題 
調用時少指定循環變量,則多指定的值不可見 
調用時多指定循環變量,多余的循環變量不會被創建 
(2)在模板中定義變量 
在模板中定義的變量有三種類型: 
plain變量:可以在模板的任何地方訪問,包括使用include指令插入的模板,使用assign指令創建和替換 
局部變量:在宏定義體中有效,使用local指令創建和替換 
循環變量:只能存在於指令的嵌套內容,由指令(如list)自動創建 
宏的參數是局部變量,而不是循環變量;局部變量隱藏(而不是覆蓋)同名的plain變量;循環變量隱藏同名的局部變量和plain變量,下面是一個例子: 
<#assign x = "plain">
1. ${x}  <#-- we see the plain var. here -->
<@test/>
6. ${x}  <#-- the value of plain var. was not changed -->
<#list ["loop"] as x>
  7. ${x}  <#-- now the loop var. hides the plain var. -->
<#assign x = "plain2"> <#-- replace the plain var, hiding does not mater here -->
 8. ${x}  <#-- it still hides the plain var. -->
</#list>
   9. ${x}  <#-- the new value of plain var. -->
<#macro test>
  2. ${x}  <#-- we still see the plain var. here -->
  <#local x = "local">
  3. ${x}  <#-- now the local var. hides it -->
  <#list ["loop"] as x>
    4. ${x}  <#-- now the loop var. hides the local var. -->
  </#list>
  5. ${x}  <#-- now we see the local var. again -->
</#macro>  
輸出結果: 
1. plain
  2. plain
  3. local
    4. loop
  5. local
6. plain
    7. loop
    8. loop
9. plain2
內部循環變量隱藏同名的外部循環變量,如: 
<#list ["loop 1"] as x>
  ${x}
  <#list ["loop 2"] as x>
    ${x}
    <#list ["loop 3"] as x>
      ${x}
    </#list>
    ${x}
  </#list>
  ${x}
</#list>
輸出結果: 
  loop 1
    loop 2
      loop 3
    loop 2
  loop 1 
模板中的變量會隱藏(而不是覆蓋)數據模型中同名變量,如果需要訪問數據模型中的同名變量,使用特殊變量global,下面的例子假設數據模型中的user的值是Big Joe: 
<#assign user = "Joe Hider">
${user}          <#-- prints: Joe Hider -->
${.globals.user} <#-- prints: Big Joe -->  
(3)名字空間 
通常情況,只使用一個名字空間,稱為主名字空間 
為了創建可重用的宏、變換器或其它變量的集合(通常稱庫),必須使用多名字空間,其目的是防止同名沖突 
創建庫 
下面是一個創建庫的例子(假設保存在lib/my_test.ftl中): 
<#macro copyright date>
  <p>Copyright (C) ${date} Julia Smith. All rights reserved.
  <br>Email: ${mail}</p>
</#macro>  
<#assign mail = "jsmith@acme.com"> 
使用import指令導入庫到模板中,Freemarker會為導入的庫創建新的名字空間,並可以通過import指令中指定的散列變量訪問庫中的變量: 
<#import "/lib/my_test.ftl" as my>
<#assign mail="fred@acme.com">
<@my.copyright date="1999-2002"/>
${my.mail}
${mail}  
輸出結果: 
  <p>Copyright (C) 1999-2002 Julia Smith. All rights reserved.
  <br>Email: jsmith@acme.com</p>
jsmith@acme.com
fred@acme.com  
可以看到例子中使用的兩個同名變量並沒有沖突,因為它們位於不同的名字空間 
可以使用assign指令在導入的名字空間中創建或替代變量,下面是一個例子: 
<#import "/lib/my_test.ftl" as my>
${my.mail}
<#assign mail="jsmith@other.com" in my>
${my.mail}  
輸出結果: 
jsmith@acme.com
jsmith@other.com  
數據模型中的變量任何地方都可見,也包括不同的名字空間,下面是修改的庫: 
<#macro copyright date>
  <p>Copyright (C) ${date} ${user}. All rights reserved.</p>
</#macro>
<#assign mail = "${user}@acme.com">   
假設數據模型中的user變量的值是Fred,則下面的代碼: 
<#import "/lib/my_test.ftl" as my>
<@my.copyright date="1999-2002"/>
${my.mail}   
輸出結果: 
  <p>Copyright (C) 1999-2002 Fred. All rights reserved.</p>
Fred@acme.com   


補充(靜態方法的調用):

方法1:
##定義配置文件 freemarkerstatic.properties 
_Validator=com.longyou.util.Validator 
_Functions=com.longyou.util.Functions 
_EscapeUtils=com.longyou.util.EscapeUtils 
/調用代碼 
${_Functions.toUpperCase("Hello")}<br> 
${_EscapeUtils.escape("狼的原野")}

方法2:
${stack.findValue("@package.ClassName@method")}


補充:常用語法
EG.一個對象BOOK
1.輸出 ${book.name}
空值判斷:${book.name?if_exists },
${book.name?default(‘xxx’)}//默認值xxx
${ book.name!"xxx"}//默認值xxx
日期格式:${book.date?string('yyyy-MM-dd')}
數字格式:${book?string.number}--20
${book?string.currency}--<#-- $20.00 -->
${book?string.percent}—<#-- 20% -->
插入布爾值:
<#assign foo=ture />
${foo?string("yes","no")} <#-- yes -->
 
 
 
 
2.邏輯判斷
a:
<#if condition>...
<#elseif condition2>...
<#elseif condition3>......
<#else>...
其中空值判斷可以寫成<#if book.name?? >
 
b:
<#switch value>
<#case refValue1>
...
<#break>
<#case refValue2>
...
<#break>
...
<#case refValueN>
...
<#break>
<#default>
...
 
3.循環讀取
<#list sequence as item>
...
空值判斷<#if bookList?size = 0>
e.g.
<#list employees as e>
${e_index}. ${e.name}
輸出:
1. Readonly
2. Robbin
freemarker中Map的使用
<#list testMap?keys as testKey> 
       < option value="${testKey}" > 
              ${testMap[testKey]} 
     </option> 
</#list>
freemarker的Eclipse插件
If you use Eclipse 2.x: 
Open the Window menu, then Open Perspective -> Install/Update 
Click with the right mouse button on the Feature Updates view, then select New -> Site Bookmark 
In the displayed dialog box, type "FreeMarker" for Name and "http://www.freemarker.org/eclipse/update" for URL. Leave the "Bookmark type" radio buttons on "Eclipse update site". 
Click Finish 
Open the tree node under the newly created update site named "FreeMarker", select the "FreeMarker X.Y.Z" feature, and install it using the Install now button in the preview pane. 
If you use Eclipse 3.x: 
Help -> Software updates -> Find and install.... 
Choose "Search for new features to install". 
Click Add Update Site..., and type "FreeMarker" for Name and "http://www.freemarker.org/eclipse/update" for URL. 
Check the box of the "FreeMarker" feature. 
"Next"-s until it is installed... 
關鍵字: FreeMarker  
引用地址: http://www.dlog.cn/html/trackback.do?id=13925&type=1 (復制地址) 

FreeMarker設計指南(2)  

2、數據模型

(1)基礎

        在快速入門中介紹了在模板中使用的三種基本對象類型:scalars、hashes 和sequences,其實還可以有其它更多的能力:

        scalars:存儲單值

        hashes:充當其它對象的容器,每個都關聯一個唯一的查詢名字

        sequences:充當其它對象的容器,按次序訪問

        方法:通過傳遞的參數進行計算,以新對象返回結果

        用戶自定義FTL標記:宏和變換器

        通常每個變量只具有上述的一種能力,但一個變量可以具有多個上述能力,如下面的例子:

(root)

 |

 +-mouse = "Yerri"

    |

    +- age = 12

    |

    +- color = "brown"> 

        mouse既是scalars又是hashes,將上面的數據模型合並到下面的模板:

${mouse}       <#-- use mouse as scalar -->

${mouse.age}   <#-- use mouse as hash -->

${mouse.color} <#-- use mouse as hash--> 

        輸出結果是:

Yerri

12

brown 

(2)Scalar變量

        Scalar變量存儲單值,可以是:

        字符串:簡單文本,在模板中使用引號(單引號或雙引號)括起

        數字:在模板中直接使用數字值

        日期:存儲日期/時間相關的數據,可以是日期、時間或日期-時間(Timestamp);通常情況,日期值由程序員加到數據模型中,設計者只需要顯示它們

        布爾值:true或false,通常在<#if …>標記中使用

(3)hashes 、sequences和集合

        有些變量不包含任何可顯示的內容,而是作為容器包含其它變量,者有兩種類型:

        hashes:具有一個唯一的查詢名字和它包含的每個變量相關聯

        sequences:使用數字和它包含的每個變量相關聯,索引值從0開始

        集合變量通常類似sequences,除非無法訪問它的大小和不能使用索引來獲得它的子變量;集合可以看作只能由<#list…>指令使用的受限sequences

(4)方法

        方法變量通常是基於給出的參數計算值

        下面的例子假設程序員已經將方法變量avg放到數據模型中,用來計算數字平均值:

The average of 3 and 5 is: ${avg(3, 5)}

The average of 6 and 10 and 20 is: ${avg(6,10, 20)}

The average of the price of python andelephant is: ${avg(animals.python.price, animals.elephant.price)}

(5)宏和變換器

        宏和變換器變量是用戶自定義指令(自定義FTL標記),會在后面講述這些高級特性

(6)節點

        節點變量表示為樹型結構中的一個節點,通常在XML處理中使用,會在后面的專門章節中講述




Trackback:http://tb.blog.csdn.net/TrackBack.aspx?PostId=164465

 

 

解析FreeMarker視圖

·        解析FreeMarker視圖

 

聲明一個針對FreeMarker的視圖解析器:

 

  <bean id="viewResolver"class="org.springframework.

         ➥web.servlet.view.freemarker.FreeMarkerViewResolver">

    <propertyname="suffix"><value>.ftl</value></property>

  </bean>

 

FreeMarkerViewResolver和VelocityViewResolver或InternalResourceViewResolver的工作機制相同。模板資源是通過在視圖的邏輯名上增加prefix屬性的值作為前綴,以及增加suffix屬性的值作為后綴進行解析的。和VelocityViewResolver一樣,在這里我們又一次只設置suffix屬性,因為模板的路徑已經在FreeMarkerConfigurer的templateLoaderPath屬性中定義了。

暴露請求和會話屬性

在第9.1.3節中,你看到如何告訴VelocityViewResolver將請求和會話屬性復制到模型map中,從而它們能夠在模板中作為變量使用。采用同樣的方式配置FreeMarkerViewResolver,可以將請求和會話屬性作為變量暴露給FreeMarker模板使用。要做到這一點,可以設置exposeRequestAttributes或者exposeSessionAttributes為true:

 

  <bean id="viewResolver"class="org.springframework.

         web.servlet.view.freemarker.FreeMarkerViewResolver">

  …

    <propertyname="exposeRequestAttributes">

      <value>true</value>

    </property>

    <propertyname="exposeSessionAttributes">

      <value>true</value>

    </property>

  </bean>

 

這里,兩個屬性都被設置為true。結果是請求和會話屬性都被復制到模板的屬性集中,可以使用FreeMarker的表達式語言來訪問並顯示。

 

FreeMarker文章引用自:

發表時間:2007年11月2日 16時54分13秒        本文鏈接:http://user.qzone.qq.com/55117942/blog/1193993653評論/閱讀(0/5)

 

[頂]FreeMarker

包含FreeMarker的指令的文件就稱為模板(Template)。

  包含FreeMarker的指令的文件就稱為模板(Template)。
模板設計者不關心數據從那兒來,只知道使用已經建立的數據模型。
數據模型由程序員編程來創建,向模板提供變化的信息,這些信息來自於數據庫、文件,甚至於在程序中直接生成。 

數據類型:

一、基本:
1、scalars:存儲單值

字符串:簡單文本由單或雙引號括起來。
數字:直接使用數值。
日期:通常從數據模型獲得
布爾值:true或false,通常在<#if …>標記中使用

2、hashes:充當其它對象的容器,每個都關聯一個唯一的查詢名字

具有一個唯一的查詢名字和他包含的每個變量相關聯。

3、sequences:充當其它對象的容器,按次序訪問

使用數字和他包含的每個變量相關聯。索引值從0開始。

4、集合變量:

除了無法訪問它的大小和不能使用索引來獲得它的子變量:集合可以看作只能由<#list...>指令使用的受限sequences。

5、方法:通過傳遞的參數進行計算,以新對象返回結果

方法變量通常是基於給出的參數計算值在數據模型中定義。

6、用戶自定義FTL指令:宏和變換器

7、節點

節點變量表示為樹型結構中的一個節點,通常在XML處理中使用。

模板:

使用FTL(freeMarker模板語言)編寫

組成部分

一、整體結構

1、注釋:<#--注釋內容-->,不會輸出。

2、文本:直接輸出。

3、interpolation:由 ${var} 或 #{var} 限定,由計算值代替輸出。

4、FTL標記

二、指令:
freemarker指令有兩種:
1、預定義指令:引用方式為<#指令名稱>
2、用戶定義指令:引用方式為<@指令名稱>,引用用戶定義指令時須將#換為@。
注意:如果使用不存在的指令,FreeMarker不會使用模板輸出,而是產生一個錯誤消息。

freemarker指令由FTL標記來引用,FTL標記和HTML標記類似,名字前加#來加以區分。如HTML標記的形式為<h1></h1>則FTL標記的形式是<#list></#list>(此處h1標記和list指令沒有任何功能上的對應關系,只是做為說明使用一下)。

有三種FTL標記:
1)、開始標記:<#指令名稱>
2)、結束標記:</#指令名稱>
3)、空標記:<#指令名稱/>

注意:

1) FTL會忽略標記之中的空格,但是,<#和指令 與 </#和指令 之間不能有空格。
2) FTL標記不能夠交叉,必須合理嵌套。每個開始標記對應一個結束標記,層層嵌套。 如:
<#list>
<li>
${數據}
<#if 變量>
<p>game over!</p>
</#if>
</li>
</#list>

注意事項:
1)、FTL對大小寫敏感。所以使用的標記及interpolation要注意大小寫。name與NAME就是不同的對象。<#list>是正確的標記,而<#List>則不是。
2)、interpolation只能在文本部分使用,不能位於FTL標記內。如<#if ${var}>是錯誤的,正確的方法是:<#if var>,而且此處var必須為布爾值。
3)、FTL標記不能位於另一個FTL標記內部,注釋例外。注釋可以位於標記及interpolation內部。


三、表達式

1、直接指定值:

1-1、字符串:
由雙引號或單引號括起來的字符串,其中的特殊字符(如' " \等)需要轉義。


1-2、raw字符串:
有一種特殊的字符串稱為raw字符串,被認為是純文本,其中的\和{等不具有特殊含義,該類字符串在引號前面加r,下面是一個例子:
${r"/${data}"year""}屏幕輸出結果為:/${data}"year"


轉義    含義
序列   

\"  雙引號(u0022) 

\'  單引號(u0027) 

\\  反斜杠(u005C) 

\n  換行(u000A) 

\r  Return (u000D) 

\t  Tab (u0009) 

\b  Backspace (u0008) 

\f  Form feed (u000C) 

\l  < 

\g  > 

\a  & 

\{  { 

\xCode  4位16進制Unicode代碼 

1-3、數字:直接輸入,不需要引號 

1)、精度數字使用“.”分隔,不能使用分組符號 
2)、目前版本不支持科學計數法,所以“1E3”是錯誤的 
3)、不能省略小數點前面的0,所以“.5”是錯誤的 
4)、數字8、+8、08和8.00都是相同的 

1-4、布爾值:true和false,不使用引號

1-5、序列:由逗號分隔的子變量列表,由[]方括號限定。
1)、子變量列表可以是表達式
2)、可以使用數字范圍定義數字序列,<b>不需要方括號限定</b>,例如2..5等同於[2, 3, 4, 5],但是更有效率,可以定義反遞增范圍如:5..2。

1-6、散列(hash)
1)、由逗號分隔的鍵/值列表,由{}大括號限定,鍵和值之間用冒號分隔,如:{"key1":valu1,"key2":"character string"....}
2)、鍵和值都是表達式,但是鍵必須是字符串。

2、獲取變量:

2-1、頂層變量:${變量名}

變量名只能是字母、數字、下划線、$、#、@ 的組合,且不能以數字開頭。

2-2、散列:有兩種方法

1)、點語法:變量名字和頂層變量的名字受同樣的限制
2)、方括號語法:變量名字無限制,可以是任意的表達式的結果
book.author.name
book.author.["name"]
book["author"].name
book["author"]["name"]
以上是等價的。

2-3、序列:使用散列的方括號語法獲取變量,方括號中的表達式結果必須為數字。注意:第一個項目的索引為0。可以使用
[startindex..endindex]語法獲取序列片段。

2-4、特殊變量:FreeMarker內定義變量,使用.variablename語法訪問。

3、字符串操作

3-1、interpolation:使用${}或#{}在文本部分插入表達式的值,例如:

${"hello${username}!"}
${"${username}${username}${username}"}

也可以使用+來獲得同樣的結果:
${"hello"+username+"!"}
${username+username+username}

注意:${}只能用於文本部分而不能出現於標記內。

<#if ${user.login}>或<#if "${user.login}">都是錯誤的;
<#if user.login>是正確的。
本例中user.login的值必須是布爾類型。

3-2、子串:
舉例說明:假如user的值為"Big Joe"
${user[0]}${user[4]}結果是:BJ
${user[1..4]}結果是:ig J

4、序列操作

4-1、連接操作:可以使用+來操作,例如:
["title","author"]+["month","day"]

5、散列操作
5-1、連接操作:可以使用+來操作,如果有相同的KEY,則右邊的值會替代左邊的值,例如: 
{"title":散列,"author":"emma"}+{"month":5,"day":5}+{"month":6}結果month的值就是6。

6、算術運算

6-1、操作符:+、-、*、/、%
除+號以外的其他操作符兩邊的數據,必須都是數字類型。
如果+號操作符一邊有一個字符型數據,會自動將另一邊的數據轉換為字符型數據,運算結果為字符型數據。

6-2、比較操作符:
1)、= 
2)、==
3)、!=
4)、<
5)、<=
6)、>
7)、>=
1-3的操作符,兩邊的數據類型必須相同,否則會產生錯誤
4-7的操作符,對於日期和數字可以使用,字符串不可以使用。

注意:
1)、FreeMarker是精確比較,所以"x" "x " "X"是不等的。
2)、因為<和>對FTL來說是開始和結束標記,所以,可以用兩種方法來避免這種情況:
一種是使用括號<#if (a<b)> 
另一是使用替代輸出,對應如下:
<  lt 
<= lte 
>  gt 
>= gte 

6-3、邏輯操作符:只能用於布爾值,否則會出現錯誤。

&&(and)與運算
||(or)或運算
!(not)非運算

6-4、內建函數:使用方法類似於訪問散列的子變量,只是使用?代替.例如:${test?upper_case?html}

常用的內建函數列舉如下:

1)、字符串使用:

html:對字符串進行HTML編碼
cap_first:字符串第一個字母大寫
lower_first:字符串第一個字母小寫
upper_case:將字符串轉換成大寫
trim:去年字符前后的空白字符

2)、序列使用:
size:獲得序列中元素的數目

3)、數字使用:
int:取得數字的整數部分

7、操作符的優先順序:

后綴:[subbarName][subStringRange].(mathodParams)
一元:+expr、-expr、! (not)
內建:?
乘法:*、/、%
加法:+、-
關系:<、<=、>、>= (lt、lte、gt、gte)
相等:=、==、!=
邏輯與:&& (and)
邏輯或:|| (or)
數字范圍:..

四、interpolation

inperpolation只能用於文本,有兩種類型:通用interpolation及數字interpolation

1、通用interpolation

如${expr}

1-1、插入字符串值:直接輸出表達式結果。
1-2、插入數字值:根據缺省格式(由setting指令設置)將表達式結果轉換成文本輸出;可以使用內建函數string來格式化單個interpolation

如:
<#setting number_format="currency" />
<#assign answer=42 />
${answer}  <#-- ¥42.00 -->
${answer?string}  <#-- ¥42.00 -->
${answer?string.number}  <#-- 42 -->
${answer?string.currency}  <#-- ¥42.00 -->
${answer?string.percent}  <#-- 42,00% -->

1-3、插入日期值:根據缺省格式(由setting指令設置)將表達式結果轉換成文本輸出;可以使用內建函數string來格式化單個interpolation

如:

${lastupdata?string("yyyy-MM-dd HH:mm:ss zzzz")}  <#-- 2003-04-08 21:24:44 Pacific Daylight Time -->
${lastupdata?string("EEE,MMM d, ''yy")}  <#-- tue,Apr 8, '03 -->
${lastupdata?string("EEEE,MMMM dd, yyyy,hh:mm:ss a '('zzz')'")}  <#-- Tuesday,April 08, 2003, 09:24:44 PM (PDT)-->

1-4、插入布爾值:根據缺省格式(由setting指令設置)將表達式結果轉換成文本輸出;可以使用內建函數string來格式化單個interpolation

如:
<#assign foo=ture />
${foo?string("yes","no")} <#-- yes -->

2、數字interpolation:

有兩種形式:
1)、#{expr}
2)、#{expr;format}:format可以用來格式化數字,format可以是如下:
mX:小數部分最小X位
MX:小數部分最大X位

例如:
<#assign x=2.582 />
<#assign y=4 />
#{x;M2} <#-- 2.58 -->
#{y;M2} <#-- 4 -->
#{x;m1} <#-- 2.582 -->
#{y;m1} <#-- 4.0 -->
#{x;m1M2} <#-- 2.58 -->
#{y;m1M2} <#-- 4.0 -->


雜項

一、用戶定義指令

宏和變換器變量是兩種不同類型的用戶自定義指令,他們的區別是:

宏可以在模板中用macro指令來定義
變換器是在模板外由程序定義

1、宏:和某個變量關聯的模板片段,以便在模板中通過用戶自定義指令使用該變量
1-1、基本用法:
例如:
<#macro greet>
<font size="+2"> Hello JOE!</font>
</#macro>


使用時:
<@greet></@greet>
如果沒有體內容也可以用
<@greet />

1-2、變量:

1)、可以在宏定義之后定義參數,宏參數是局部變量,只在宏定義中有效。如:

<#macro greet person>
<font size="+2"> Hello ${person}!</font>
</#macro>
使用時:
<@greet person="emma"> and <@greet person="LEO">
輸出為:
<font size="+2"> Hello emma!</font>
<font size="+2"> Hello LEO!</font>

注意:宏的參數是FTL表達式,所以,person=emma和上面的例子中具有不同的意義,這意味着將變量emma的值傳給person,這個值可能是任意一種數據類型,甚至是一個復雜的表達式。


宏可以有多個參數,使用時參數的次序是無關的,但是只能使用宏中定義的參數,並且對所有參數賦值。如:
<#macro greet person color>
<font size="+2" color="${color}"> Hello ${person}!</font>
</#macro>

使用時:
<@greet color="black" person="emma" />正確
<@greet person="emma" />錯誤,color沒有賦值,此時,如果在定義宏時為color定義缺省值<#macro greet person color="black">這樣的話,這個使用方法就是正確的。
<@greet color="black" person="emma" bgcolor="yellow" />錯誤,宏greet定義中未指定bgcolor這個參數



2、嵌套內容:

2-1、自定義指令可以有嵌套內容,使用<#nested>指令,執行自定義指令開始和結束標記之間的模板片段。例如:
<#macro greet>
<p>
<#nested>
</p>
</#macro>


<@greet>hello Emma!</@greet>

輸出為
<p>hello Emma!</p>

2-2、<#nested>指令可以被多次調用,例如
<#macro greet>
<p>
<#nested>
<#nested>
<#nested>
<#nested>
</p>
</#macro>

<@greet>hello Emma!</@greet>

輸出為
<p>
hello Emma!
hello Emma!
hello Emma!
hello Emma!
</p>

2-3、嵌套的內容可以是有效的FTL,例如:
<#macro welcome>
<p>
<#nested>
</p>
</#macro>

<#macro greet person color="black">
<font size="+2" color="${color}"> Hello ${person}!</font>
</#macro>

<@welcome>
<@greet person="Emma" color="red" />
<@greet person="Andrew" />
<@greet person="Peter" />
</@welcome>

輸出為:
<p>
<font size="+2" color="red"> Hello Emma!</font>
<font size="+2" color="black"> Hello Andrew!</font>
<font size="+2" color="black"> Hello Peter!</font>
</p>

2-4、宏定義中的局部變量對嵌套內容是不可見的,例如:

<#macro repeat count>
<#local y="test" /> 
<#list 1..count as x>
${y}${count}/${x}:<#nested />
</#list>
</#macro>

<@repeat count=3> 
${y?default("?")} 
${x?default("?")}
${count?default("?")}
</@repeat>

輸出結果為
test 3/1:???
test 3/2:???
test 3/3:???

2-5、在宏定義中使用循環變量,通常用來重復嵌套內容,基本用法為:作為nested指令的參數,傳遞循環變量的實際值,而在調用自定義指令時,在標記的參數后面指定循環變量的名字。
例如:
<#macro repeat count>
<#list 1..count as x>
<#nested x,x/2,x==count />
</#list>
</#macro>

<@repeat count=4;c,halfc,last>
${c}. ${halfc}
<#if last>
last!
</#if>
</@repeat>

輸出結果是

1. 0.5
2. 1
3. 1.5
4. 2last!

注意:指定循環變量的數目和用戶定義指令開始標記指定的不同不會有問題
調用時,少指定循環變量,多指定的值會不見
調用時,多指定循環變量,多余的循環變量不會被創建

二、在模板中定義變量

1、在模板中定義的變量有三種類型

1-1、plain變量:可以在模板的任何地方訪問,包括使用include指令插入的模板,使用assign指令創建和替換。
1-2、局部變量:在宏定義體中有效,使用local指令創建和替換。
1-3、循環變量:只能存在於指令的嵌套內容,由指令(如list)自動創建。

注意:
1)、宏的參數是局部變量,不是循環變量。
2)、局部變量隱藏同名的plain變量
3)、循環變量隱藏同名的plain變量和局部變量。

例如:

<#assign x="plain">
1. ${x} <#-- plain -->

<@test />

6. ${x}
<#list ["loop"] as x>
7. ${x} <#-- loop -->
<#assign x="plain2">
8. ${x} <#-- loop -->
</#list>
9. ${x} <#-- plain2 -->

<#macro test>
2. ${x} <#-- plain -->
<#local x="local">
3. ${x} <#-- local -->
<#list ["loop"] as x>
4. ${x} <#-- loop -->
</#list>
5. ${x} <#-- local -->
</#macro>

4)、內部循環變量隱藏同名的外部循環變量

<#list ["loop1"] as x>
${x} <#-- loop1 -->
<#list ["loop2"] as x>
${x} <#-- loop2 -->
<#list ["loop3"] as x>
${x} <#-- loop3 -->
</#list>
${x} <#-- loop2 -->
</#list>
${x} <#-- loop1 -->
</#list>

5)、模板中的變量會隱藏數據模型中的同名變量,如果需訪問數據模型中的變量,使用特殊變量global。

例如:
假設數據模型中的user值為Emma
<#assign user="Man">
${user} <#-- Man -->
${.global.user} <#-- Emma -->

 

數據源+freemarker+servlet生成xml文件  

一.步驟:

1.在server.xml文件中建立數據源.

<Service name="Cms">
     <Connector debug="0" enableLookups="false" port="8084" protocol="AJP/1.3" redirectPort="8443"/>
    <Connector acceptCount="100" connectionTimeout="20000" debug="0" disableUploadTimeout="true" 

enableLookups="false" maxSpareThreads="75" maxThreads="150" minSpareThreads="25" port="8081" redirectPort="8443"/>
    <Engine defaultHost="localhost_Cms" name="Catalina_Cms">
      <Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_cmt_log." suffix=".txt" timestamp="true"/>
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
      <Host autoDeploy="true" debug="0" name="localhost_Cms" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
         <Context debug="0" docBase="D:WorkspaceCMSweb" path="/" reloadable="true" workDir="D:WorkspaceCMSj2src"> 
          <Resource name="jdbc/news_DB" auth="Container" type="javax.sql.DataSource"/>
        <ResourceParams name="jdbc/news_DB">
     <parameter>
       <name>factory</name>
       <!-- DBCP Basic Datasource Factory -->
       <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
     </parameter>
      
     <parameter>
       <name>maxActive</name>
       <value>1000</value>
     </parameter>
     <parameter>
        <name>validationQuery</name>
        <value>select 1+1</value>
     </parameter>
     <parameter>
       <name>maxIdle</name>
       <value>100</value>
     </parameter>
     <parameter>
       <name>maxWait</name>
       <value>10000</value>
     </parameter>
     <parameter>
       <name>removeAbandoned</name>
       <value>true</value>
     </parameter>
     <parameter>
       <name>removeAbandonedTimeout</name>
       <value>60</value>
     </parameter>
     <parameter>
       <name>logAbandoned</name>
       <value>false</value>
     </parameter>
      
     <parameter>
       <name>username</name>
       <value>aaaa</value>
     </parameter>
     <parameter>
       <name>password</name>
       <value>bbbbbb</value>
     </parameter>
     <parameter>
       <name>driverClassName</name>
       <value>net.sourceforge.jtds.jdbc.Driver</value>
     </parameter>
     <parameter>
       <name>url</name>
       <value>jdbc:jtds:sqlserver://111.111.111.111:1433/cms</value>
     </parameter>
      </ResourceParams>
      
   </Context>
      </Host>
</Engine>
  </Service> 

2.在web.xml文件中配置servlet 

    <servlet>
        <description>generate xml file</description>
        <servlet-name>NewsXmlServlet</servlet-name>
        <servlet-class>xml.NewsXmlServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>NewsXmlServlet</servlet-name>
        <url-pattern>/xmlServlet</url-pattern>
    </servlet-mapping>
    <servlet>

3.newsXmlServlet.java

package xml;

import java.io.*;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.util.Locale;

public class NewsXmlServlet extends HttpServlet...{

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException ...{
        // TODO Auto-generated method stub
        try ...{
            Connection conn=null;
            Context ctx = new InitialContext();
            DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/news_DB");
            conn=ds.getConnection();
            Statement stmt=conn.createStatement();
            ResultSet rs=stmt.executeQuery("select url,updatetime,tpf_edu_contentTitle,tpf_edu_contentneirong,tpf_edu_contentlaiyuan,tpf_edu_contentkeyword from tp5__edu_content where url<>'' and url is not null and dateDiff(d,updatetime,getDate())=1 order by updatetime desc");
            Configuration cfg=new Configuration();
            cfg.setDirectoryForTemplateLoading(new File("E:/wwwroot/CMS/web/WEB-INF/classes/xml"));
            Template tem=cfg.getTemplate("news.ftl");
            
            List list=new ArrayList();
            OutputStreamWriter out=new OutputStreamWriter(System.out);
            while(rs.next())...{
                Map item=new HashMap();
                item.put("title",rs.getString(3));
                item.put("link","http://test.com.cn"+rs.getString(1));
                item.put("pubdate",rs.getTimestamp(2));
                item.put("content",DelHtml(rs.getString(4)));
                item.put("source",rs.getString(5));
                item.put("keywords",DelHtml(rs.getString(6)));
                list.add(item);
            }
            Map data=new HashMap();
            data.put("items",list);
            StringWriter writer=new StringWriter();
            tem.process(data,writer);
            String content=writer.toString();
            writer.close();
            createXml(content);
            out.close();
            //resp.setContentType("text/xml; charset=utf-8");
            //resp.getWriter().write(content);
            
        } catch (NamingException e) ...{
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) ...{
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TemplateException e) ...{
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    public String DelHtml(String content)...{
        String contents=content.replaceAll("<\/?\s*(\S+)(\s*[^>]*)?\s*\/?>","");
         contents=contents.replaceAll("&ldquo;", "”");
         contents=contents.replaceAll("&rdquo;","”");
         contents=contents.replaceAll("&ldquo;", "‘");
         contents=contents.replaceAll("&rdquo;","’");
         contents=contents.replaceAll("&middot;","·");
         contents=contents.replaceAll("&mdash;","—");
         contents=contents.replaceAll("&hellip;","…");
         contents=contents.replaceAll("&nbsp;","");
         contents=contents.replaceAll(","," ");
        return contents;        
    }
    public void createXml(String fileContent)...{
        try ...{
            String filePath="E:/wwwroot/cmsHtml/education/news.xml";
            File fileXml=new File(filePath);
            if(!fileXml.exists())...{            
                fileXml.createNewFile();
            }
            
            /**//*FileWriter fileWriter=new FileWriter(fileXml);
            fileWriter.
            fileWriter.write(fileContent);
            fileWriter.close();*/
                OutputStreamWriter writer=new OutputStreamWriter(new FileOutputStream(fileXml), Charset.forName("utf-8"));     
                writer.write(fileContent);
                writer.close();
            } catch (IOException e) ...{
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
    }
    
    public void destroy() ...{
        // TODO Auto-generated method stub
        super.destroy();
    }

    public void init() throws ServletException ...{
        // TODO Auto-generated method stub
        super.init();
    }
    
}

4.news.ftl

 

<?xml version="1.0" encoding="utf-8" ?>
<document>
    <webSite>edu.aweb.com.cn</webSite>
    <webMaster>webmaster@aweb.com.cn</webMaster>
    <updatePeri>1440</updatePeri>
    <#list items as it>
    <item>
        <title><![CDATA[$...{it.title}]]></title>
        <link>$...{it.link}</link>
        <pubDate>$...{it.pubdate}</pubDate>
        <text><![CDATA[$...{it.content}]]></text>
        <image/>
        <source>$...{it.source}</source>
        <keywords><![CDATA[$...{it.keywords}]]></keywords>
    </item>
    </#list>
</document>

 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1889578

FreeMarker學習筆記3

2007-10-18 13:11

(3)模板

l         在FreeMarker模板中可以包括下面三種特定部分:

Ø         ${…}:稱為interpolations,FreeMarker會在輸出時用實際值進行替代

Ø         FTL標記(FreeMarker模板語言標記):類似於HTML標記,為了與HTML標記區分,用#開始(有些以@開始,在后面敘述)

Ø         注釋:包含在<#--和-->(而不是<!--和-->)之間

l         下面是一些使用指令的例子:

Ø         if指令

<#if animals.python.price < animals.elephant.price>

  Pythons are cheaper than elephants today.

<#else>

  Pythons are not cheaper than elephants today.

</#if>  

Ø         list指令

<p>We have these animals:

<table border=1>

  <tr><th>Name<th>Price

  <#list animals as being>

  <tr><td>${being.name}<td>${being.price} Euros

  </#list>

</table>  

玩過C#的都一眼就看出來了,和foreach一樣.用過java的for(xx:xx)的家伙也清楚吧.其實就是遍歷這個being.

輸出為:

<p>We have these animals:

<table border=1>

  <tr><th>Name<th>Price

  <tr><td>mouse<td>50 Euros

  <tr><td>elephant<td>5000 Euros

  <tr><td>python<td>4999 Euros

</table>  

Ø         include指令

<html>

<head>

  <title>Test page</title>

</head>

<body>

  <h1>Test page</h1>

  <p>Blah blah...

<#include "/copyright_footer.html">

</body>

</html>  

這個就不用解釋了,嵌入個網頁

Ø         一起使用指令

<p>We have these animals:

<table border=1>

  <tr><th>Name<th>Price

  <#list animals as being>

  <tr>

    <td>

      <#if being.size = "large"><b></#if>

      ${being.name}

      <#if being.size = "large"></b></#if>

    <td>${being.price} Euros

  </#list>

</table>  

Spring中使用FreeMaker或Vilocity模板發送郵件

        本文以用戶注冊后為用戶發送一封郵件為例子,講述如何在Spring中使用FreeMaker或Vilocity發送郵件。

       Spring配置文件:

 

xml 代碼

  1.   
  2.     <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">  
  3.         <property name="host" value="smtp.163.com"/>  
  4.         <property name="username" value="test"/>  
  5.         <property name="password" value="123456"/>  
  6.         <property name="javaMailProperties">  
  7.               <props>  
  8.                 <prop key="mail.smtp.auth">trueprop>  
  9.               props>  
  10.         property>  
  11.     bean>  
  12.   
  13.       
  14.     <bean id="freeMarkerConfigurer"                    class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">  
  15.         <property name="templateLoaderPath" value="/WEB-INF/freemakertemplate/" />  
  16.         <property name="freemarkerSettings">  
  17.             <props>  
  18.                 <prop key="template_update_delay">0prop>  
  19.                 <prop key="default_encoding">GBKprop>  
  20.                 <prop key="locale">zh_CNprop>  
  21.             props>  
  22.         property>  
  23.     bean>  
  24.        
  25.       
  26.     <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">  
  27.         <property name="resourceLoaderPath" value="/WEB-INF/vilocitytemplate/" />  
  28.         <property name="velocityProperties">  
  29.             <props>  
  30.                 <prop key="velocimacro.library">*.vmprop>  
  31.                 <prop key="default.contentType">text/html; charset=utf-8prop>  
  32.                 <prop key="output.encoding">utf-8prop>  
  33.                 <prop key="input.encoding">utf-8prop>  
  34.             props>  
  35.         property>  
  36.     bean>  
  37.        
  38.      <bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage" singleton="false">  
  39.         <property name="from" value="test@163.com"/>  
  40.     bean>  
  41.        
  42.     <bean id="mailEngine" class="test.MailEngine">  
  43.         <property name="mailSender" ref="mailSender"/>  
  44.           
  45.         <property name="velocityEngine" ref="velocityEngine"/>  
  46.          
  47.        <property name="freeMarkerConfigurer" ref="freeMarkerConfigurer" />  
  48.     bean>  

    
 
   

java 代碼

  1.  MailEngine類:   
  2.   
  3. public class MailEngine {   
  4.     protected static final Log log = LogFactory.getLog(MailEngine.class);   
  5.   
  6. //    private FreeMarkerConfigurer freeMarkerConfigurer;   
  7.     private VelocityEngine velocityEngine;   
  8.     private MailSender mailSender;   
  9.   
  10. //    public void setFreeMarkerConfigurer(   
  11. //            FreeMarkerConfigurer freeMarkerConfigurer) {   
  12. //        this.freeMarkerConfigurer = freeMarkerConfigurer;   
  13. //    }   
  14.   
  15.     public void setMailSender(MailSender mailSender) {   
  16.         this.mailSender = mailSender;   
  17.     }   
  18.   
  19.     public void setVelocityEngine(VelocityEngine velocityEngine) {   
  20.         this.velocityEngine = velocityEngine;   
  21.     }   
  22.   
  23.     /**  
  24.      * 通過模板產生郵件正文  
  25.      * @param templateName    郵件模板名稱  
  26.      * @param map            模板中要填充的對象  
  27.      * @return 郵件正文(HTML)  
  28.      */  
  29.     public String generateEmailContent(String templateName, Map map) {   
  30.         //使用FreeMaker模板   
  31. //        try {   
  32. //            Configuration configuration = freeMarkerConfigurer.getConfiguration();   
  33. //            Template t = configuration.getTemplate(templateName);   
  34. //            return FreeMarkerTemplateUtils.processTemplateIntoString(t, map);   
  35. //        } catch (TemplateException e) {   
  36. //            log.error("Error while processing FreeMarker template ", e);   
  37. //        } catch (FileNotFoundException e) {   
  38. //            e.printStackTrace();   
  39. //            //log.error("Error while open template file ", e);   
  40. //        } catch (IOException e) {   
  41. //            log.error("Error while generate Email Content ", e);   
  42. //        }   
  43.            
  44. //        使用Vilocity模板   
  45.         try {   
  46.            return VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateName, map);   
  47.         } catch (VelocityException e) {   
  48.             log.error("Error while processing Vilocity template ", e);   
  49.         }   
  50.            
  51.         return null;   
  52.     }   
  53.   
  54.     /**  
  55.      * 發送郵件  
  56.      * @param emailAddress        收件人Email地址的數組  
  57.      * @param fromEmail            寄件人Email地址, null為默認寄件人web@vnvtrip.com  
  58.      * @param bodyText            郵件正文  
  59.      * @param subject            郵件主題  
  60.      * @param attachmentName    附件名  
  61.      * @param resource            附件  
  62.      * @throws MessagingException  
  63.      */  
  64.     public void sendMessage(String[] emailAddresses, String fromEmail,   
  65.             String bodyText, String subject, String attachmentName,   
  66.             ClassPathResource resource) throws MessagingException {   
  67.         MimeMessage message = ((JavaMailSenderImpl) mailSender)   
  68.                 .createMimeMessage();   
  69.   
  70.         // use the true flag to indicate you need a multipart message   
  71.         MimeMessageHelper helper = new MimeMessageHelper(message, true);   
  72.   
  73.         helper.setTo(emailAddresses);   
  74.         if(fromEmail != null){   
  75.             helper.setFrom(fromEmail);   
  76.         }   
  77.         helper.setText(bodyText, true);   
  78.         helper.setSubject(subject);   
  79.            
  80.         if(attachmentName!=null && resource!=null)   
  81.             helper.addAttachment(attachmentName, resource);   
  82.   
  83.         ((JavaMailSenderImpl) mailSender).send(message);   
  84.     }   
  85.   
  86.     /**  
  87.      * 發送簡單郵件  
  88.      * @param msg      
  89.      */  
  90.     public void send(SimpleMailMessage msg) {   
  91.         try {   
  92.             ((JavaMailSenderImpl) mailSender).send(msg);   
  93.         } catch (MailException ex) {   
  94.             //log it and go on   
  95.             log.error(ex.getMessage());   
  96.         }   
  97.     }   
  98.        
  99.     /**  
  100.      * 使用模版發送HTML格式的郵件  
  101.      *  
  102.      * @param msg          裝有to,from,subject信息的SimpleMailMessage  
  103.      * @param templateName 模版名,模版根路徑已在配置文件定義於freemakarengine中  
  104.      * @param model        渲染模版所需的數據  
  105.      */  
  106.     public void send(SimpleMailMessage msg, String templateName, Map model) {   
  107.         //生成html郵件內容   
  108.         String content = generateEmailContent(templateName, model);   
  109.         MimeMessage mimeMsg = null;   
  110.         try {   
  111.             mimeMsg = ((JavaMailSenderImpl) mailSender).createMimeMessage();   
  112.             MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, true, "utf-8");   
  113.             helper.setTo(msg.getTo());   
  114.                
  115.             if(msg.getSubject()!=null)   
  116.                 helper.setSubject(msg.getSubject());   
  117.                
  118.             if(msg.getFrom()!=null)   
  119.                 helper.setFrom(msg.getFrom());   
  120.                
  121.             helper.setText(content, true);   
  122.                
  123.             ((JavaMailSenderImpl) mailSender).send(mimeMsg);   
  124.         } catch (MessagingException ex) {   
  125.             log.error(ex.getMessage(), ex);   
  126.         }   
  127.   
  128.     }   
  129. }   
  130.   
  131. 發送郵件:   
  132. SimpleMailMessage message = (SimpleMailMessage) getBean("mailMessage");   
  133.                 message.setTo(user.getName() + "<" + user.getEmail() + ">");   
  134.                    
  135.                 Map model = new HashMap();   
  136.                 model.put("user", user);   
  137.                    
  138.                 MailEngine engine = (MailEngine)getBean("mailEngine");   
  139.                 //Vilocity模板   
  140.                 engine.send(message, "notifyUser.vm", model);   
  141.                 //FreeMaker模板   
  142.                 //engine.send(message, "NotifyUser.ftl", model);   
  143.   
  144. 以上的User為用戶類。  

 

xml 代碼

  1. 模板:   
  2. <html>  
  3. <head>  
  4. <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">  
  5. <title>用戶注冊通知title>  
  6. head>  
  7. <body>  
  8. <p>${user.name} 您好,恭喜您,已經成為本站會員!p>  
  9. <table>  
  10. <tr><td>用戶名:td><td>${user.name}td>tr>  
  11. <tr><td>密碼:td><td>${user.password}td>tr>  
  12. table>  
  13. body>  
  14. html>  

FreeMaker開發指南

本文轉自福州IT信息網(http://www.fzic.net),詳細出處參考:http://www.fzic.net/SrcShow.asp?Src_ID=1328

 

ello 

類似String.split的用法

“abc;def;ghi”?split(“;”)返回sequence

將字符串按空格轉化成sequence,然后取sequence的長度

     var?word_list   效果同 var?split(“ ”)

var?word_list?size

取得字符串長度

var?length

大寫輸出字符

var?upper_case

小寫輸出字符

var?lower_case

首字符大寫

var?cap_first

首字符小寫

var?uncap_first

去掉字符串前后空格

var?trim

每個單詞的首字符大寫

var?capitalize

類似String.indexof:

“babcdabcd”?index_of(“abc”) 返回1

“babcdabcd”?index_of(“abc”,2) 返回5

類似String.lastIndexOf

last_index_of和String.lastIndexOf類似,同上

下面兩個可能在代碼生成的時候使用(在引號前加”\”)

j_string: 在字符串引號前加”\”

<#assign beanName = 'The "foo"bean.'>

String BEAN_NAME ="${beanName?j_string}";

打印輸出:

String BEAN_NAME = "The\"foo\" bean.";

js_string:

<#assign user = "Big Joe's\"right hand\".">

<script>

  alert("Welcome ${user}!");

</script> 

打印輸出

alert("Welcome Big Joe\'s \"righthand\"!");

替換字符串 replace

${s?replace(‘ba’, ‘XY’ )}

${s?replace(‘ba’, ‘XY’ , ‘規則參數’)}將s里的所有的ba替換成xy 規則參數包含: i r m s c f 具體含義如下:

· i: 大小寫不區分.

· f: 只替換第一個出現被替換字符串的字符串

· r:   XY是正則表達式

· m:Multi-line mode for regular expressions. In multi-line mode the expressions ^and $ match just after or just before, respectively, a line terminator or theend of the string. By default these expressions only match at the beginning andthe end of the entire string.

· s: Enablesdotall mode for regular expressions (same as Perl singe-line mode). In dotallmode, the expression . matches any character, including a line terminator. Bydefault this expression does not match line terminators.

· c: Permitswhitespace and comments in regular expressions.

 

在模板里對sequences和hashes初始化

sequences 

1. [“you”,”me”,”he”]

2. 1..100

3. [ {“Akey”:”Avalue”},{“Akey1”:”Avalue1”},

{“Bkey”:”Bvalue”},{“Bkey1”:”Bvalue1”},

]

 

hashes      {“you”:”a”,”me”:”b”,”he”:”c”}

 

注釋標志

<#--

這里是注釋

-->

舊版本的freemarker采用的是<#comment> 注釋 </#comment>方法

sequences內置方法

sequence?first

返回sequence的第一個值;前提條件sequence不能是null

sequence?last

返回sequence最后一個值

sequence?reverse

反轉sequence的值

sequence?size

返回sequence的大小

sequence?sort

對sequence按里面的對象toString()的結果進行排序

sequence?sort_by(value)

對sequence 按里面的對象的屬性value進行排序

如: sequence里面放入的是10 個user對象,user對象里面包含name,age等屬性

sequence?sort_by(name) 表示所有的user按user.name進行排序

hashes內置方法

hash?keys

返回hash里的所有keys, 返回結果類型sequence

hash?values

返回hash里的所有value, 返回結果類型sequence

4 freemarker在web開發中注意事項

freemarker與webwork整合

web中常用的幾個對象

Freemarker的ftl文件中直接使用內部對象:

${Request ["a"]}

${RequestParameters["a"]}

${Session ["a"]}

${Application ["a"]}

${JspTaglibs ["a"]}

與webwork整合之后 通過配置的servlet 已經把request,session等對象置入了數據模型中

在view中存在下面的對象

   我們可以在ftl中${req}來打印req對象

· req - thecurrent HttpServletRequest

· res - thecurrent HttpServletResponse

· stack - thecurrent OgnlValueStack

· ognl - theOgnlTool instance

· webwork - aninstance of FreemarkerWebWorkUtil

· action - thecurrent WebWork action

· exception -optional the Exception instance, if the view is a JSP exception or Servletexception view

view中值的搜索順序

${name}將會以下面的順序查找name值

· freemarkervariables

· value stack

· requestattributes

· sessionattributes

· servletcontext attributes

在模板里ftl里使用標簽

注意,如果標簽的屬性值是數字,那么必須采用nubmer=123方式給屬性賦值

JSP頁面

<%@page contentType="text/html;charset=ISO-8859-2"language="java"%>

<%@tagliburi="/WEB-INF/struts-html.tld" prefix="html"%>

<%@tagliburi="/WEB-INF/struts-bean.tld" prefix="bean"%>

<html>

  <body>

    <h1><bean:message key="welcome.title"/></h1>

    <html:errors/>

    <html:form action="/query">

      Keyword: <html:text property="keyword"/><br>

      Exclude: <html:text property="exclude"/><br>

      <html:submit value="Send"/>

    </html:form>

  </body>

</html>

模板ftl頁面

<#assignhtml=JspTaglibs["/WEB-INF/struts-html.tld"]>

<#assignbean=JspTaglibs["/WEB-INF/struts-bean.tld"]>

<html>

  <body>

    <h1><@bean.messagekey="welcome.title"/></h1>

    <@html.errors/>

    <@html.form action="/query">

      Keyword: <@html.text property="keyword"/><br>

      Exclude: <@html.text property="exclude"/><br>

      <@html.submit value="Send"/>

    </@html.form>

  </body>

</html> 

 

如何初始化共享變量

1.初始化全局共享數據模型

freemark在web上使用的時候對共享數據的初始化支持的不夠,不能在配置初始化的時候實現,而必須通過ftl文件來初始化全局變量。這是不能滿主需求的,我們需要在servlet init的時候留出一個接口來初始化系統的共享數據

具 體到和webwork整合,因為本身webwork提供了整合servlet,如果要增加全局共享變量,可以通過修改com.opensymphony.webwork.views.freemarker.FreemarkerServlet來實現,我們可以在這個 servlet初始化的時候來初始化全局共享變量

與webwork整合配置

配置web.xml

<servlet>

    <servlet-name>freemarker</servlet-name>

    <servlet-class>com.opensymphony.webwork.views.freemarker.FreemarkerServlet</servlet-class>

    <init-param>

      <param-name>TemplatePath</param-name>

<param-value>/</param-value>

<!—模板載入文件夾,這里相對context root,遞歸獲取該文件夾下的所有模板-->

    </init-param>

    <init-param>

      <param-name>NoCache</param-name> <!—是否對模板緩存-->

      <param-value>true</param-value>

    </init-param>

    <init-param>

      <param-name>ContentType</param-name>

      <param-value>text/html</param-value>

    </init-param>

    <init-param>

<param-name>template_update_delay</param-name>

<!—模板更新時間,0表示每次都更新,這個適合開發時候-->

      <param-value>0</param-value>

    </init-param>

    <init-param>

      <param-name>default_encoding</param-name>

      <param-value>GBK</param-value>

    </init-param>

    <init-param>

      <param-name>number_format</param-name>

      <param-value>0.##########</param-value><!—數字顯示格式-->

    </init-param>

    <load-on-startup>1</load-on-startup>

  </servlet>

  <servlet-mapping>

    <servlet-name>freemarker</servlet-name>

    <url-pattern>*.ftl</url-pattern>

  </servlet-mapping>

5高級方法

自定義方法

${timer("yyyy-MM-dd H:mm:ss", x)}

${timer("yyyy-MM-dd ", x)}

在模板中除了可以通過對象來調用方法外(${object.methed(args)})也可以直接調用java實現的方法,java類必須實現接 口TemplateMethodModel的方法exec(List args). 下面以把毫秒的時間轉換成按格式輸出的時間為例子

public class LongToDate implementsTemplateMethodModel {

   

public TemplateModel exec(List args) throwsTemplateModelException {

SimpleDateFormat mydate = newSimpleDateFormat((String) args.get(0)));

        return mydate.format(new Date(Long.parseLong((String)args.get(1)));

    }

將LongToDate對象放入到數據模型中

root.put("timer", newIndexOfMethod());

ftl模板里使用

<#assign x ="123112455445">

${timer("yyyy-MM-dd H:mm:ss", x)}

${timer("yyyy-MM-dd ", x)}

輸出

2001-10-12 5:21:12

2001-10-12

自定義 Transforms

實現自定義的<@transform>文本或表達式</@transform>的功能,允許對中間的最終文本進行解析轉換

例子:實現<@upcase>str</@upcase>將str轉換成STR 的功能

代碼如下:

import java.io.*;

import java.util.*;

importfreemarker.template.TemplateTransformModel;

class UpperCaseTransform implementsTemplateTransformModel {

    public Writer getWriter(Writer out, Map args) {

        return new UpperCaseWriter(out);

    }

    private class UpperCaseWriter extends Writer {

      

        private Writer out;

          

        UpperCaseWriter (Writer out) {

            this.out = out;

        }

        public void write(char[] cbuf, int off, int len)

                 throws IOException {

            out.write(new String(cbuf, off, len).toUpperCase());

        }

        public void flush() throws IOException {

            out.flush();

        }

        public void close() {

        }

    }

然后將此對象put到數據模型中

root.put("upcase", newUpperCaseTransform());

在view(ftl)頁面中可以如下方式使用

<@upcase>

hello world

</@upcase>

打印輸出:

HELLO WORLD

 

查看更多關於:FreeMaker  FreeMaker開發指南 

本文轉自福州IT信息網(http://www.fzic.net),詳細出處參考:http://www.fzic.net/SrcShow.asp?Src_ID=1328


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM