generic sorted message tree

This commit is contained in:
Ernir Erlingsson
2016-05-25 00:43:03 +02:00
parent a652f1a5d7
commit d2ba7c9b7f
5 changed files with 224 additions and 1 deletions

View File

@@ -0,0 +1,21 @@
package org.briarproject.api.clients;
import org.briarproject.api.sync.MessageId;
import java.util.Collection;
import java.util.Comparator;
public interface MessageTree<T extends MessageTree.MessageNode> {
void add(Collection<T> nodes);
void clear();
Collection<T> depthFirstOrder();
void setComparator(Comparator<T> comparator);
interface MessageNode {
MessageId getId();
MessageId getParentId();
long getTimestamp();
}
}

View File

@@ -1,9 +1,10 @@
package org.briarproject.api.forum;
import org.briarproject.api.clients.MessageTree;
import org.briarproject.api.identity.Author;
import org.briarproject.api.sync.MessageId;
public class ForumPostHeader {
public class ForumPostHeader implements MessageTree.MessageNode {
private final MessageId id;
private final MessageId parentId;

View File

@@ -0,0 +1,89 @@
package org.briarproject.clients;
import org.briarproject.api.clients.MessageTree;
import org.briarproject.api.sync.MessageId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
public class MessageTreeImpl<T extends MessageTree.MessageNode>
implements MessageTree<T> {
Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>();
List<T> roots = new ArrayList<T>();
private Comparator<T> comparator = new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return Long.valueOf(o1.getTimestamp()).compareTo(o2.getTimestamp());
}
};
@Inject
public MessageTreeImpl() {
}
@Override
public void clear() {
roots.clear();
nodeMap.clear();
}
@Override
public void add(Collection<T> nodes) {
// add all nodes to the node map
for (T node : nodes) {
nodeMap.put(node.getId(), new ArrayList<T>());
}
// parse the nodes for dependencies
for (T node : nodes) {
if (node.getParentId() == null) {
roots.add(node);
}
else {
// retrieve the parent's children
List<T> pChildren = nodeMap.get(node.getParentId());
pChildren.add(node);
}
}
sortAll();
}
private void sortAll() {
Collections.sort(roots, comparator);
// Sort all the sub-lists
for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) {
Collections.sort(entry.getValue(), comparator);
}
}
private void traverse(List<T> list, T node) {
list.add(node);
for (T child : nodeMap.get(node.getId())) {
traverse(list, child);
}
}
@Override
public Collection<T> depthFirstOrder() {
List<T> orderedList = new ArrayList<T>();
for (T root : roots) {
traverse(orderedList, root);
}
return Collections.unmodifiableList(orderedList);
}
@Override
public void setComparator(Comparator<T> comparator) {
this.comparator = comparator;
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.forum;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.MessageTree;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.MetadataEncoder;
@@ -9,12 +10,14 @@ import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.forum.ForumFactory;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumPostHeader;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.system.Clock;
import org.briarproject.clients.MessageTreeImpl;
import java.security.SecureRandom;
@@ -100,4 +103,11 @@ public class ForumModule {
return forumSharingManager;
}
@Provides
@Singleton
MessageTree<ForumPostHeader> provideForumMessageTree(
MessageTreeImpl<ForumPostHeader> messageTree) {
return messageTree;
}
}

View File

@@ -0,0 +1,102 @@
package org.briarproject.clients;
import org.briarproject.TestUtils;
import org.briarproject.api.clients.MessageTree;
import org.briarproject.api.sync.MessageId;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
public class MessageTreeTest {
private static final Logger LOG =
Logger.getLogger(MessageTreeTest.class.getName());
private MessageTree<TestNode> tree;
@Test
public void testMessageTree() {
tree = new MessageTreeImpl<>();
testSimpleTree();
tree.clear();
testSimpleTree();
}
private void testSimpleTree() {
TestNode[] nodes = new TestNode[5];
for (int i = 0; i < nodes.length; i++) {
nodes[i] = new TestNode();
}
/*
Construct the following tree:
4
1 ->
0 ->
2
3
*/
nodes[0].setParentId(nodes[1].getId());
nodes[2].setParentId(nodes[0].getId());
nodes[3].setParentId(nodes[1].getId());
long timestamp = System.currentTimeMillis();
nodes[4].setTimestamp(timestamp - 5);
nodes[1].setTimestamp(timestamp - 4);
nodes[0].setTimestamp(timestamp - 3);
nodes[3].setTimestamp(timestamp - 2);
nodes[2].setTimestamp(timestamp - 1);
// add all nodes except the last one
tree.add(Arrays.asList(Arrays.copyOf(nodes, nodes.length-1)));
tree.add(Collections.singletonList(nodes[nodes.length-1]));
TestNode[] sortedNodes = tree.depthFirstOrder().toArray(new TestNode[5]);
assertEquals(nodes[4], sortedNodes[0]);
assertEquals(nodes[1], sortedNodes[1]);
assertEquals(nodes[0], sortedNodes[2]);
assertEquals(nodes[2], sortedNodes[3]);
assertEquals(nodes[3], sortedNodes[4]);
}
private void printNodes(TestNode[] nodes, TestNode[] sortedNodes) {
for (int i = 0; i < sortedNodes.length; i++) {
for (int j = 0; j < nodes.length; j++) {
if (sortedNodes[i] == nodes[j]) {
LOG.info("index: " + j);
break;
}
}
}
}
class TestNode implements MessageTree.MessageNode {
private final MessageId id = new MessageId(TestUtils.getRandomId());
private MessageId parentId;
private long timestamp;
@Override
public MessageId getId() {
return id;
}
@Override
public MessageId getParentId() {
return parentId;
}
@Override
public long getTimestamp() {
return timestamp;
}
public void setParentId(MessageId parentId) {
this.parentId = parentId;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
}