From d2ba7c9b7fcf20b22334d1a587b0bb93adc080b1 Mon Sep 17 00:00:00 2001 From: Ernir Erlingsson <ernir@ymirmobile.com> Date: Wed, 25 May 2016 00:43:03 +0200 Subject: [PATCH] generic sorted message tree --- .../briarproject/api/clients/MessageTree.java | 21 ++++ .../api/forum/ForumPostHeader.java | 3 +- .../briarproject/clients/MessageTreeImpl.java | 89 +++++++++++++++ .../org/briarproject/forum/ForumModule.java | 10 ++ .../briarproject/clients/MessageTreeTest.java | 102 ++++++++++++++++++ 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 briar-api/src/org/briarproject/api/clients/MessageTree.java create mode 100644 briar-core/src/org/briarproject/clients/MessageTreeImpl.java create mode 100644 briar-tests/src/org/briarproject/clients/MessageTreeTest.java 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 0000000000..500db6a583 --- /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 00924f98ab..4077939053 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 0000000000..55f69cf540 --- /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 bde6a057df..04d934a21c 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 0000000000..1c6e165311 --- /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; + } + } +} -- GitLab