jive主题的消息有两种显示方式:flat和threaded,具体可以在jive_config.xml中配置,配置的选项是skin.default.threadmode。
当我们使用flat模式的时候,所有的消息将会在主题的根消息下面以时间顺序并且没有缩进地排列显示出来,这种分页显示方式与主题的分页显示方式是类似的。但是,这里的另一种实现方式,即缩进方式,它所有的消息是显示在它自己的父消息(非主题的根消息)之下,并且没有使用分页显示,这种显示方式就是threaded方式,在threaded方式中使用到了一个重要的类TreeWalker。
来看下面的代码(threadMode1.jsp)
TreeWalker treeWalker = thread.treeWalker();
ForumMessage rootMessage = thread.getRootMessage();
Iterator messages = treeWalker.recursiveChildren(rootMessage);
这段代码是使用treeWalker获得以rootMessage为树根的所有消息,注意,返回的是一个Iterator,可想而知,在下面的消息显示中,无非是枚举messages里的内容。
下面重点来看一下treeWalker的recursiveChilderen,它是如何递归返回消息的。
首先我们看TreeWalkerProxy里的recursiveChildren.是这样的
public Iterator recursiveChildren(ForumMessage parent) {
return new IteratorProxy(JiveGlobals.MESSAGE,
treeWalker.recursiveChildren(parent), authorization, permissions);
}
它其实是对treeWalker返回的迭代器的一层proxy封装,(这里的treeWalker应该是DbTreeWalder的实例了。再往里看到DbtreeWalker中的方法:
public Iterator recursiveChildren(ForumMessage parent) {
long [] messages = tree.getRecursiveChildren(parent.getID());
return new DatabaseObjectIterator(JiveGlobals.MESSAGE, messages,
parent.getForumThread());
}
现在到了关键的地方了,tree.getRecursiveChildren(parent.getID()),这个方法直接返回以rootMessage的ID为根的所有的子消息ID号了,前面讲到过DatabaseObjectIterator只是对这些消息ID的一个迭代器封装而已。
稍微解释一下:
tree是二叉树的一个不完全实现,类为LongTree,可以看到系统中有这样一段说明,
* 1
* |-- 3
* |-- |--4
* |-- |--6
* |-- 5
*
* array index: 0 | 1 | 2 | 3 | 4
*
* key: 1 | 3 | 4 | 5 | 6
* leftChild: 1 | 2 |-1 |-1 |-1
* rightSiblings -1 | 3 | 4 |-1 |-1
即系统中使用三个成员数组来描述上面的树,而对于一个主题来说,就存在这样的一棵树,这棵树tree是在treewalker初始化的时候完成的,即:
public DbTreeWalker(DbForumThread thread, DbForumFactory factory) {
......
ForumMessage root = thread.getRootMessage();
int numMessages = thread.getMessageCount(IGNORE_MODERATION_FILTER);
//初始化tree,这时只包含一个根
tree = new LongTree(root.getID(), numMessages);
Connection con = null;
PreparedStatement pstmt = null;
try {
con = ConnectionManager.getConnection();
pstmt = con.prepareStatement(GET_MESSAGES);
pstmt.setLong(1, thread.getID());
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
//枚举出数据库中的每个message并加入到数中,由于枚举时按时间升序,所以这个message的parent的id肯定已经在数上了。
long messageID = rs.getLong(1);
long parentMessageID = rs.getLong(2);
tree.addChild(parentMessageID, messageID);
}
}
......
}
树在初始化已经形成,现在还是来看核心的部分吧
tree.getRecursiveChildren(parent.getID()),
public long[] getRecursiveChildren(long parentKey) {
//找到根节点的id:startIndex
char startIndex = findKey(parentKey, (char)1);
//初始化返回的数组,数组的index即是树节点的index。数组的value将是tree的节点的key值,即message的ID值。
long [] depthKeys = new long[nextIndex-1];
int cursor = 0;
//得到根节点的左子数
char siblingIndex = leftChildren[startIndex];
while (siblingIndex != 0) {
//填充根节点的左子数,即把所有左子数上的节点的key都赋给depthKey.(这是递归)
cursor = fillDepthKeys(siblingIndex, depthKeys, cursor);
//得到根节点的右子数,继而所有右子数上的节点的key都赋给depthKey
siblingIndex = rightSiblings[siblingIndex];
}
long [] dKeys = new long[cursor];
for (int i=0; i<cursor; i++) {
dKeys[i] = depthKeys[i];
}
return dKeys;
}
这段代码,以深度优先算法返回了数的key。