Freemaker 自定義指令和函式
freemarker現有的一些內建函式不能滿足我們的需求,這些內建函式包括:chunk, is_date, last, root, j_string, contains, is_hash, long, float, ends_with, namespace, matches, time, values, seq_last_index_of, uncap_first, byte, substring, is_transform, web_safe, groups, seq_contains, is_macro, index_of, word_list, int, is_method, eval, parent, xml, number, capitalize, if_exists, rtf, node_type, double, is_directive, url, size, default, is_boolean, split, node_name, is_enumerable, seq_index_of, is_sequence, sort, is_node, sort_by, left_pad, cap_first, interpret, children, node_namespace, chop_linebreak, date, short, last_index_of, is_collection, ancestors, length, trim, datetime, is_string, reverse, c, keys, upper_case, js_string, has_content, right_pad, replace, is_hash_ex, new, is_number, is_indexable, lower_case, string, exists, html, first, starts_with
這時freemarker的靈活,為我們提供了很好的擴充套件方案,自定義的函式,可以方便我們在寫到畫面的變數進一步計算出我們想要的結果,自定義的標籤指令可以對一些畫面共通的部分作出有效簡單複用的操作,當然也可以實現自定義函式中的功能,不過,對於不同的分工還是要用不同的技術才好,效率和使用才會更優。
(一) 自定義函式:
寫一個類繼承TemplateMethodModel,比如說:
<span style="font-size:14px;">public class TruncateTemplateMethodModel implements TemplateMethodModel { public Object exec(List arguments) throws TemplateModelException { return arguments.get(0).toString().substring(0,1); } }</span>
a.ftl為:${truncate("abc")}執行後將會輸出: a<span style="font-size:14px;"> Map root = new HashMap(); root.put("truncate",new TruncateTemplateMethodModel(); Template temp = cfg.getTemplate("a.ftl"); StringWriter out = new StringWriter(); temp.process(root, out); out.flush(); System.out.println(out.getBuffer().toString());</span>
若與spring整合的話,也很簡單,只要在:*-servlet.xml中加入以下程式碼即可:
<span style="font-size:14px;"><bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="suffix" value=".ftl"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="exposeRequestAttributes" value="true"/>
<property name="exposeSessionAttributes" value="true"/>
<property name="attributesMap">
<map>
<entry key="truncate"><bean class="com.rj8g.linkage.renba.web.TruncateTemplateMethodModel" /></entry>
</map>
</property>
</bean></span>
例2
使用類似格式 ${avg(10, 20)} 其中avg為函式名,10,20為傳入的引數
定義在前臺:
<span style="font-size:14px;"><#function name param1 param2 ... paramN>
...
<#return returnValue>
...
</#function></span>
<span style="font-size:14px;"><#function avg x y>
<#return (x + y) / 2>
</#function></span>
輸出結果:15
定義在後臺:
方法變數在存於實現了TemplateMethodModel介面的模板中。這個介面僅包含一個方法:TemplateModel exec(java.util.List arguments)。當使用方法呼叫表示式呼叫方法時,exec方法將會被呼叫。形參將會包含FTL方法呼叫形參的值。exec方法的返回值給出了FTL方法呼叫表示式的返回值。TemplateMethodModelEx介面擴充套件了TemplateMethodModel介面。它沒有任何新增的方法。事實上這個物件實現這個標記介面暗示給FTL引擎,形式引數應該直接以TemplateModel-s形式放進java.util.List。否則將會以String-s形式放入List。
<span style="font-size:14px;">public class IndexOfMethod implements TemplateMethodModel {
public TemplateModel exec(List args) throws TemplateModelException {
if (args.size() != 2) {
throw new TemplateModelException("Wrong arguments");
}
return new SimpleNumber(((String) args.get(1)).indexOf((String) args.get(0)));
}
}</span>
然後將例項放入到根資料模型中:
<span style="font-size:14px;">root.put("indexOf", new IndexOfMethod());</span>
<span style="font-size:14px;"><#assign x = "something">
${indexOf("met", x)}
${indexOf("foo", x)}</span>
輸出結果:
2 -1
(二) 自定義指令
使用以下格式呼叫自定義指令:<@user_def_dir_exp param1=val1 param2=val2 ... paramN=valN/>
定義在前臺:
<#macro name param1 param2 ... paramN>
...
<#nested loopvar1, loopvar2, ..., loopvarN>
...
<#return>
...
</#macro>
例子:
<#macro test foo bar baaz>
Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<#-- call the macro: -->
<@test foo="a" bar="b" baaz=5*5-2/>
輸出結果:
Test text, and the params: a, b, 23
定義在後臺:
Java程式設計師可以使用TemplateDirectiveModel介面在Java程式碼中實現自定義指令。詳情可以參加API文件。
注意:
TemplateDirectiveModel在FreeMarker 2.3.11版本時才加入。用來代替快被廢棄的TemplateTransformModel。
public class UpperDirective implements TemplateDirectiveModel { public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { // 檢查引數是否傳入 if (!params.isEmpty()) { throw new TemplateModelException("This directive doesn't allow parameters."); } if (loopVars.length != 0) { throw new TemplateModelException("This directive doesn't allow loop variables."); } // 是否有非空的嵌入內容 if (body != null) { // 執行嵌入體部分,和FTL中的<#nested>一樣,除了 // 我們使用我們自己的writer來代替當前的output writer. body.render(new UpperCaseFilterWriter(env.getOut())); } else { throw new RuntimeException("missing body"); } } /** * {@link Writer}改變字元流到大寫形式, * 而且把它傳送到另外一個{@link Writer}中。 */ private static class UpperCaseFilterWriter extends Writer { private final Writer out; UpperCaseFilterWriter (Writer out) { this.out = out; } public void write(char[] cbuf, int off, int len) throws IOException { char[] transformedCbuf = new char[len]; for (int i = 0; i < len; i++) { transformedCbuf[i] = Character. toUpperCase(cbuf[i + off]); } out.write(transformedCbuf); } public void flush() throws IOException { out.flush(); } }例子:
foo <@upper> bar <#-- 這裡允許使用所有的FTL --> <#list ["red", "green", "blue"] as color> ${color} </#list> baaz </@upper> wombat
輸出結果:
foo
BAR
RED
GREEN
BLUE
BAAZ
wombat