Apache Velocity 模版引擎简化了文本内容的创建,将封装在Java object model里的数据和Velocity 模板融合在一起。如果需要创建复杂的多单元行、列的表格,velocity标签不得不欠套在一起,也表现出复杂的层次性的结构,从而使整个模版内容难以阅读、管理。在这篇文章中,Matthias Laux 提供java包去简化html表格产生过程,并且,使用非常少的标签来实现以往编写复杂velocity模版所产生的表格效果。这个工具包也可以脱离于apache velcity使用,并能应用到其他表现机制中,例如JavaServer Pages.
我参于过的多个项目都有类似的需求。大量存在于数据库或xml文件的数据需要能够精确地被显示在html格式页面中。为了更好应用mvc结构,我使用Apache Velocity作为模版引擎来产生所有的页面。控制层的任务主要为:读取数据,并填充数据到一些java数据结构中,而这些结构是基于已知的用例的数据模型而设计,然后使用velocity模版来处理这些数据对象。
我使用过的大量数据都需要超多使用velocity端的html表格。而这些数据呈现出层次化的结构,从而表格也变得非常复杂.为了反映出相应的层次结构,我们不得不使用rowspan或者colspan属性来合并单元。
这种递归的结构导致我不得不使用实现一些通用的方法去处理文本中的复杂表格。除了基本的功能需求,我也希望能够满足以下的一些特征:
--即使实例化后,表格也能动态变化
--灵活的边界行为: 单元格插入时,表格自动变化
--表格压缩
--表格克隆
我在本文中将详细介绍解决方案:java工具包简化velocity产生表格的复杂性.首先,我将举例说明复杂的表格结构怎样导致到复杂的velocity模版。然后我将演示怎样使用我的解决方案,用最少的java代码和velocity标签来产生复杂表格。最后,通过一个真实的实例,你可以体会到这个工具包的其他便捷之处。
复杂数据表格,混乱的模版结构
table1是一个删减版本的复杂实例。表格中的许多单元格使用rowspan标签而合并在一起。
| MainTopic |
# |
Topic |
# |
SubTopic |
# |
| DOC |
31 |
Developer Guide |
17 |
Fact Sheet |
4 |
| General |
8 |
| HowTo |
1 |
| Module Whitepaper |
3 |
| Tutorial |
1 |
| General |
7 |
Concept Paper |
2 |
| IDN |
1 |
| Release Documentation |
3 |
| User Guide |
1 |
| Training Material |
7 |
eHF Conference |
7 |
| INF |
28 |
Deployment Infrastructure |
1 |
Performance |
1 |
| Development Environment |
12 |
Generator |
12 |
| Test Infrastructure |
14 |
Audit |
1 |
| Authentication |
1 |
| Document |
1 |
| Test Client |
10 |
| User Management |
1 |
| Tools for eHF Users |
1 |
Security Workbench |
1 |
| Total Count |
59 |
Total Count |
59 |
Total Count |
59 |
Table 1: 相对复杂的表格
table1中的rowspan属性值必须在页面创建的时候就动态指定,因为结构化的元素 (关键主题,主题,子主题)和字符串值直到那个阶段才会明确。另外,Velcotiy模版必须产生所需的html输出,它不得不使用混乱的标签结构来应付各种可能发生的状况。在listing 1也可以看到,模版中包括许多数据模型的引用,比如说$categoryData
Listing 1. 模版代码片段
<table cellpadding="2" cellspacing="1" border="1" style="empty-cells:show">
<tr bgcolor="$headerBackgroundColor">
<td align="center"> $headerFontOn MainTopic $headerFontOff
<td align="center"> $headerFontOn # $headerFontOff
<td align="center"> $headerFontOn Topic $headerFontOff
<td align="center"> $headerFontOn # $headerFontOff
<td align="center"> $headerFontOn SubTopic $headerFontOff
<td align="center"> $headerFontOn # $headerFontOff
</tr>
#foreach ( $mainTopic_Name in $categoryData.backlogElements.keySet() )
#set ( $mainTopic_Category = $categoryData.categories.get($mainTopic_Name) )
#set ( $mainTopic_Category_BacklogElementCount = $categoryData.backlogElements.get($mainTopic_Name).size() )
#set ( $topic_Category_Count = $mainTopic_Category.backlogElements.size() )
#set ( $rowCount1 = 0 )
#foreach ( $topic_Name in $mainTopic_Category.backlogElements.keySet() )
#set ( $topic_Category = $mainTopic_Category.categories.get($topic_Name) )
#foreach ( $subTopic_Name in $topic_Category.backlogElements.keySet() )
#set ( $rowCount1 = $rowCount1 + 1 )
#end
#end
#set ( $first1 = 1 )
#foreach ( $topic_Name in $mainTopic_Category.backlogElements.keySet() )
#set ( $topic_Category = $mainTopic_Category.categories.get($topic_Name) )
#set ( $topic_Category_BacklogElementCount = $mainTopic_Category.backlogElements.get($topic_Name).size() )
#set ( $subTopic_Category_Count = $topic_Category.backlogElements.size() )
#set ( $rowCount2 = 0 )
#foreach ( $subTopic_Name in $topic_Category.backlogElements.keySet() )
#set ( $rowCount2 = $rowCount2 + 1 )
#end
#set ( $first2 = 1 )
#foreach ( $subTopic_Name in $topic_Category.backlogElements.keySet() )
#set ( $subTopic_Category_BacklogElementCount = $topic_Category.backlogElements.get($subTopic_Name).size() )
<tr>
#if ( $first1 == 1 )
<td rowspan="$rowCount1" valign="top">
$plainFontOn
<a href="#$formatter.getAnchorName($mainTopic_Name)">
$mainTopic_Name
</a>
$plainFontOff
</td>
<td rowspan="$rowCount1" valign="top"
bgcolor="$colorScheme.getNextColor($mainTopic_Category_BacklogElementCount, $maxCount)">
$plainFontOn
$mainTopic_Category_BacklogElementCount
$plainFontOff
</td>
#set ( $first1 = 0 )
#end
#if ( $first2 == 1 )
<td rowspan="$rowCount2" valign="top">
<a href="#$formatter.getAnchorName($mainTopic_Name, $topic_Name)">
$plainFontOn $topic_Name $plainFontOff
</a>
</td>
<td rowspan="$rowCount2" valign="top"
bgcolor="$colorScheme.getNextColor($topic_Category_BacklogElementCount, $maxCount)">
$plainFontOn $topic_Category_BacklogElementCount $plainFontOff
</td>
#set ( $first2 = 0 )
#end
<td valign="top">
<a href="#$formatter.getAnchorName($mainTopic_Name, $topic_Name, $subTopic_Name)">
$plainFontOn $subTopic_Name $plainFontOff
</a>
</td>
<td valign="top" bgcolor="$colorScheme.getNextColor($subTopic_Category_BacklogElementCount, $maxCount)">
$plainFontOn $subTopic_Category_BacklogElementCount $plainFontOff
</td>
</tr>
#end
#end
#end
<tr bgcolor="$subHeaderBackgroundColor">
<td> $plainFontOn <b> Total Count </b> $plainFontOff
<td> $plainFontOn <b> $cnt1 </b> $plainFontOff
<td> $plainFontOn <b> Total Count </b> $plainFontOff
<td> $plainFontOn <b> $cnt2 </b> $plainFontOff
<td> $plainFontOn <b> Total Count </b> $plainFontOff
<td> $plainFontOn <b> $cnt3 </b> $plainFontOff
</tr>
</table><p>
很显然,Listing 1中的Velocity标签控制了实际输出样式,但是也使控制页面显示的html代码变得很难阅读和管理。
未完待续