1. 我们已经知道如何使用基本的Java类(Map, String,等)来构建数据模型了。在内部, 模板中可用的变量都是实现了freemarker.template.TemplateModel接口的Java对象。但在数据模型中, 可以使用基本的Java集合类作为变量, 因为这些变量会在内部被替换为适当的TemplateModel类型。这种功能特性被称作是对象包装。
2. 对象包装功能可以透明地把任何类型的对象转换为实现了TemplateModel接口类型的实例。
3. 包装(转换)这些对象, 需要使用合适的, 也就是所谓的对象包装器实现。
4. 那么首先来熟悉一下TemplateModel接口
5. 有一个粗略的freemarker.template.TemplateModel子接口对应每种基本变量类型(TemplateHashModel对应哈希表, TemplateSequenceModel对应序列, TemplateNumberModel对应数字等等)。
6. 如果想尝试自己的TemplateModel实现, 一个简单的方式是创建它的实例, 然后将这个实例放入数据模型中(也就是把它放到哈希表的根root上)。对象包装器将会给模板提供它的原状, 因为它已经实现了TemplateModel接口, 所以没有转换(包装)的需要。
7. 请注意一个类可以实现多个TemplateModel接口, 这就是为什么FTL变量可以有多种类型。
8. 有4种类型的标量: 布尔值, 数字, 字符串, 日期类型(子类型: 日期(没有时间部分), 时间(没有日期部分), 或者日期-时间)。
9. 每一种标量类型都是TemplateTypeModel接口的实现, 这里的Type就是类型的名称。这些接口只定义了一个方法: type getAsType();。它返回变量的Java类型(boolean, Number, String和Date各自代表的值)。
10. 由于历史遗留的原因, 字符串标量的接口是TemplateScalarModel, 而不是TemplateStringModel。(因为早期的FreeMarker字符串就是标量。)
11. freemarker.template.TemplateBooleanModel接口
12. freemarker.template.TemplateNumberModel接口
13. freemarker.template.TemplateScalarModel接口
14. freemarker.template.TemplateDateModel接口
15. 标量这些接口的一个细小的实现和SimpleType类名在freemarker.template包中是可用的。但是却没有SimpleBooleanModel类型; 为了代表布尔值, 可以使用TemplateBooleanModel.TRUE和TemplateBooleanModel.FALSE来单独使用。
16. 由于历史遗留的原因, 字符串标量的实现类是SimpleScalar, 而不是SimpleString。
17. TemplateBooleanModel.TRUE实际上是freemarker.template.TrueTemplateBooleanModel类
18. FalseTemplateBooleanModel实际上是freemarker.template.FalseTemplateBooleanModel类
19. freemarker.template.SimpleNumber实现类
20. freemarker.template.SimpleScalar实现类
21. freemarker.template.SimpleDate实现类
22. 在FTL中标量是一成不变的。当在模板中设置变量的值时, 使用其他的实例来替换TemplateTypeModel实例时, 是不用改变原来实例中存储的值的。
23. 对于日期类型来说, 有一些难题, 因为Java API通常不区别java.util.Date只存储日期部分(April 4, 2003), 时间部分(10:19:18 PM), 或两者都存(April 4, 2003 10:19:18 PM)。为了用本文正确显示值(或者进行其它确定的操作), FreeMarker必须知道java.util.Date的哪个部分存储了有意义上的信息, 哪部分没有被使用(通常是标记为0的)。不幸的是, 通常该信息只是当值从数据库中取得时可用, 因为大多数数据库有独立的日期, 时间和日期-时间(又叫做时间戳)类型, java.sql有3个对应的java.util.Date子类和它们相匹配。
24. TemplateDateModel接口有两个方法: 分别是java.util.Date getAsDate()和int getDateType()。该接口典型的实现是存储一个java.util.Date对象, 加上一个整数来辨别子类型。这个整数的值也必须是TemplateDateModel接口中的常量之一: DATE, TIME, DATETIME和UNKNOWN。
25. 如果对象包装器要包装java.util.Date类, 它不是java.sql日期类的实例, 那就不能决定子类型是什么, 所以使用UNKNOWN。之后, 如果模板需要使用这个变量, 而且操作也需要子类型, 那就会停止执行并抛出错误。为了避免这种情况的发生, 对于那些可能有问题的变量, 模板开发人员必须明确地指定子类型, 使用内建函数date, time或datetime(比如: lastUpdated?datetime)。请注意, 如果和格式化参数一起使用内建函数string, 比如: foo?string("MM/dd/yyyy"), 那么FreeMarker就不必知道子类型了。
26. 容器包括哈希表, 序列和集合三种类型。
27. 哈希表是实现了TemplateHashModel接口的Java对象。TemplateHashModel有两个方法: TemplateModel get(String key), 这个方法根据给定的名称返回子变量, boolean isEmpty(), 这个方法表明哈希表是否含有子变量。get方法当在给定的名称没有找到子变量时返回null。
28. TemplateHashModelEx接口扩展了TemplateHashModel。它增加了更多的方法, 使得可以使用内建函数values和keys来枚举哈希表中的子变量。
29. 经常使用的实现类是SimpleHash, 该类实现了TemplateHashModelEx接口。从内部来说, 它使用一个java.util.Hash类型的对象存储子变量。
30. 序列是实现了TemplateSequenceModel接口的Java对象。它包含两个方法: TemplateModel get(int index)和int size()。
31. 经常使用的实现类是SimpleSequence。该类内部使用一个java.util.List类型的对象存储它的子变量。SimpleSequence有添加子元素的方法。序列创建之后应该使用这些方法来填充序列。
32. 集合是实现了TemplateCollectionModel接口的Java对象。这个接口定义了一个方法: TemplateModelIterator iterator()。TemplateModelIterator接口和java.util.Iterator 相似, 但是它返回TemplateModelIterator而不是Object, 而且它能抛出TemplateModelException异常。
33. 通常使用的实现类是SimpleCollection。