1. chunk
1.1. 该内建函数将序列分隔为多个序列, 长度为第一个参数给定的值(比如: mySeq?chunk(3))。结果是包含这些序列的一个序列。最后一个序列可能比给定的长度要小, 除非第二个参数也给定了(比如: mySeq?chunk(3, '-')), 这就是用来填充最后一个序列, 以达到给定的长度。
1.2. 该内建函数通常在输出的序列中使用表格/柱状的格式。当被用于html表格时, 第二个参数通常是"\xA0"(也就是不换行的空格代码, 也就是我们所知的"nbsp"), 所以空td的边界就不会不丢失。
1.3. 第一个参数必须是一个数字, 而且至少是1。如果这个数字不是整数, 那么它会被静默地去掉小数部分(也就是说3.1和3.9都会被规整为3)。第二个参数可以是任意类型的值。
2. first
2.1. 序列的第一个子变量。如果序列为空, 那么模板处理将会中止。
3. join
3.1. 使用给定的分隔符来连接序列中的项为一个独立的字符串。
3.2. 序列中不是字符串的项会被转换为字符串, 使用${...}相同的转换规则(当然这里不会应用自动转义)。
3.3. join(...)最多可以有3个参数
3.3.1. 分隔符, 是必须的, 插入到每一项中间的字符串。
3.3.2. 空值, 默认是""(空字符串), 如果序列为空, 使用该值。
3.3.3. 列表结尾, 默认是"" (空字符串), 如果列表序列不为空, 该值在最后一个值后面输出。
4. last
4.1. 序列的最后一个子变量。如果序列为空,那么模板处理将会中止。
5. reverse
5.1. 序列的反序形式。
6. seq_contains
6.1. 辨别序列中是否包含指定值。它包含一个参数, 就是来查找的值。
6.2. 为了查找值, 该内建函数使用了FreeMarker的比较规则(就像使用==操作符)。你可以使用它来查找标量值(也就是字符串, 数字, 布尔值, 或日期/时间类型)。对于其他类型结果通常都是 false。
6.3. seq_前缀在该内建函数名字中是需要的, 用来和contains内建函数区分开。contains用来在字符串中查找子串(因为变量可以同时当作字符串和序列)。
7. seq_index_of
7.1. 返回序列中第一次出现该值时的索引位置, 如果序列不包含指定的值时返回-1。要查找的值作为第一个参数。
7.2. 为了查找值, 该内建函数使用了FreeMarker的比较规则(就像使用了==操作符)。你可以使用它来查找标量值(也就是字符串, 数字, 布尔值, 或日期/时间类型)。对于其他类型结果通常是-1。
7.3. 搜索开始的索引值可以由第二个可选参数来确定。如果在同一个序列中相同的项可以多次出现时, 这是很有用的。第二个参数的数值没有什么限制: 如果它是负数, 那么就和它是零的效果一样, 而如果它是比序列长度还大的数, 那么就和它是序列长度值的效果一样。小数值会被切成整数。
7.4. seq_前缀在该内建函数名字中是需要的, 用来和index_of内建函数区分开。index_of用来在字符串中查找子串(因为变量可以同时当作字符串和序列)。
8. seq_last_index_of
8.1. 返回序列中最后一次出现值的索引位置, 如果序列不包含指定的值时返回-1。也就是说, 和seq_index_of相同, 只是在序列中从最后一项开始向前搜索。
8.2. 它也支持可选的第二个参数来确定从哪里开始搜索的索引位置。
8.3. seq_前缀在该内建函数名字中是需要的, 用来和last_index_of内建函数区分开。last_index_of用来在字符串中查找子串(因为变量可以同时当作字符串和序列)。
9. size
9.1. 序列中子变量的数量(作为数字值)。假设序列中至少有一个子变量, 那么序列s中最大的索引是s?size - 1(因为第一个子变量的序列是0)。
10. sort
10.1. 以升序方式返回序列。(要使用降序排列时, 使用它之后使用reverse内建函数。)这仅在子变量都是字符串时有效, 或者子变量都是数字, 或者子变量都是日期值(日期, 时间, 或日期+时间)。如果子变量是字符串, 它使用本地化(语言)的具体单词排序(通常是大小写不敏感的)。
11. 例子
11.1. 新建一个名为FMBuiltInsForSequences的动态Web工程, 同时添加相关jar包。
11.2. 编写FMFactory.java
package com.fm.util;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
public class FMFactory {
private final static FMFactory instance = new FMFactory();
private FMFactory() {}
public static FMFactory getInstance() {
return instance;
}
private Map<String, Configuration> map = new ConcurrentHashMap<String, Configuration>();
// 创建单个Configuration实例
public synchronized Configuration getCfg(Object servletContext, String path) {
if(null != map.get(path)) {
return map.get(path);
}
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg.setServletContextForTemplateLoading(servletContext, path);
cfg.setDefaultEncoding("utf-8");
// 本地化设置
cfg.setLocale(Locale.CHINA);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
map.put(path, cfg);
return cfg;
}
}
11.3. 编写BuiltInsForSequences.java
package com.fm.action;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fm.util.FMFactory;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class BuiltInsForSequences extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Configuration cfg = FMFactory.getInstance().getCfg(req.getServletContext(), "/WEB-INF/templates");
Template template = cfg.getTemplate("builtinsforsequences.html");
Map<String, Object> root = new HashMap<String, Object>();
Writer out = new OutputStreamWriter(resp.getOutputStream());
try {
template.process(root, out);
} catch (TemplateException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
11.4. 修改web.xml
11.5. 在/WEB-INF/templates下编写builtinsforsequences.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>序列内建函数</title>
</head>
<body>
<h2>分隔序列为多个序列</h2>
<#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>
<#list seq?chunk(4) as row>
<#list row as cell>${cell} </#list><br />
</#list><br />
<#list seq?chunk(4, '-') as row>
<#list row as cell>${cell} </#list><br />
</#list>
<h2>序列的第一个子变量</h2>
${['你好', '我好', '他也好']?first}
<h2>使用给定的分隔符来连接序列中的项为一个独立的字符串</h2>
[${["red", "green", "blue"]?join(", ")}]<br />
[${["red", "green", "blue"]?join(", ", "-")}]<br />
[${[]?join(", ", "-")}]<br />
[${["red", "green", "blue"]?join(", ", "-", ".")}]<br />
[${[]?join(", ", "-", ".")}]
<h2>序列的最后一个子变量</h2>
${['你好', '我好', '他也好']?last}
<h2>序列的反序形式</h2>
${['你好', '我好', '他也好']?reverse?join(", ")}
<h2>辨别序列中是否包含指定值</h2>
"blue": ${["red", 16, "blue", "cyan"]?seq_contains("blue")?string("yes", "no")}<br />
"yellow": ${["red", 16, "blue", "cyan"]?seq_contains("yellow")?string("yes", "no")}
<h2>返回序列中第一次出现该值时的索引位置</h2>
"Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe")}<br />
"lili"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("lili")}<br />
从-2的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", -2)}<br />
从-1的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", -1)}<br />
从0的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", 0)}<br />
从1的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", 1)}<br />
从2的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", 2)}<br />
从3的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", 3)}<br />
从4的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中第一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_index_of("Joe", 4)}
<h2>返回序列中最后一次出现值的索引位置</h2>
"Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe")}<br />
"lili"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("lili")}<br />
从-2的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", -2)}<br />
从-1的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", -1)}<br />
从0的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", 0)}<br />
从1的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", 1)}<br />
从2的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", 2)}<br />
从3的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", 3)}<br />
从4的位置起, "Joe"在["Joe", "Fred", "Joe", "Susan"]中最后一次出现的位置: ${["Joe", "Fred", "Joe", "Susan"]?seq_last_index_of("Joe", 4)}
<h2>序列中子变量的数量</h2>
${['你好', '我好', '他也好']?size}
<h2>以升序方式返回序列</h2>
${['hello', 'world', 'thanks', 'word']?sort?join(", ")}<br />
${[1, 22, 3, 1]?sort?join(", ")}<br />
${['1900-05-12', '2021-05-12', '1999-01-01', '1900-12-21']?sort?join(", ")}<br />
${['00:00:01', '12:00:01', '02:00:01', '12:59:01']?sort?join(", ")}<br />
${['1900-05-12 00:00:01', '1999-05-12 12:00:01', '1900-05-12 10:00:01']?sort?join(", ")}
</body>
</html>
11.6. 运行项目