diff --git a/briar-api/src/org/briarproject/api/clients/MessageTree.java b/briar-api/src/org/briarproject/api/clients/MessageTree.java new file mode 100644 index 0000000000000000000000000000000000000000..500db6a583435f5371ff4e403b7b37778a7f3098 --- /dev/null +++ b/briar-api/src/org/briarproject/api/clients/MessageTree.java @@ -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(); + } + +} diff --git a/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java b/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java index 00924f98ab0834de48700d4f855128f1de6dcb02..40779390539cd3dff457cc70470d1443780f1755 100644 --- a/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java +++ b/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java @@ -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; diff --git a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..55f69cf54008adbcb57e57788a24afe25f0284d7 --- /dev/null +++ b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java @@ -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; + } + +} diff --git a/briar-core/src/org/briarproject/forum/ForumModule.java b/briar-core/src/org/briarproject/forum/ForumModule.java index bde6a057df777386641fc75c4656c4e9272175b7..04d934a21ccccaabdae511448eee6d6fa55d5d4d 100644 --- a/briar-core/src/org/briarproject/forum/ForumModule.java +++ b/briar-core/src/org/briarproject/forum/ForumModule.java @@ -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; + } } diff --git a/briar-tests/src/org/briarproject/clients/MessageTreeTest.java b/briar-tests/src/org/briarproject/clients/MessageTreeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1c6e165311ea13955c7124704dd568bf7732e499 --- /dev/null +++ b/briar-tests/src/org/briarproject/clients/MessageTreeTest.java @@ -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; + } + } +}