Use different way to get replies for a post.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Core.java
index b603eff..4356120 100644 (file)
@@ -32,6 +32,8 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -54,6 +56,8 @@ import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Client;
 import net.pterodactylus.sone.data.Image;
 import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostBuilder;
+import net.pterodactylus.sone.data.PostBuilderFactory;
 import net.pterodactylus.sone.data.PostReply;
 import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Profile.Field;
@@ -62,7 +66,6 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
 import net.pterodactylus.sone.data.Sone.SoneStatus;
 import net.pterodactylus.sone.data.TemporaryImage;
-import net.pterodactylus.sone.data.impl.PostImpl;
 import net.pterodactylus.sone.data.impl.PostReplyImpl;
 import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
@@ -79,17 +82,18 @@ import net.pterodactylus.sone.utils.IntegerRangePredicate;
 import net.pterodactylus.util.config.Configuration;
 import net.pterodactylus.util.config.ConfigurationException;
 import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.service.AbstractService;
 import net.pterodactylus.util.thread.NamedThreadFactory;
-import net.pterodactylus.util.thread.Ticker;
 
-import com.google.common.base.Optional;
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Ordering;
 import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
-import com.google.common.primitives.Longs;
 import com.google.inject.Inject;
 
 import freenet.keys.FreenetURI;
@@ -168,6 +172,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
        /** All known Sones. */
        private final Set<String> knownSones = new HashSet<String>();
 
+       /** The post builder. */
+       private final PostBuilderFactory postBuilderFactory;
+
        /** All posts. */
        private final Map<String, Post> posts = new HashMap<String, Post>();
 
@@ -197,7 +204,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
        private final Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
 
        /** Ticker for threads that mark own elements as known. */
-       private final Ticker localElementTicker = new Ticker();
+       private final ScheduledExecutorService localElementTicker = Executors.newScheduledThreadPool(1);
 
        /** The time the configuration was last touched. */
        private volatile long lastConfigurationUpdate;
@@ -215,9 +222,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
         *            The WebOfTrust updater
         * @param eventBus
         *            The event bus
+        * @param postBuilderFactory
+        *            The post builder
         */
        @Inject
-       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus) {
+       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, PostBuilderFactory postBuilderFactory) {
                super("Sone Core");
                this.configuration = configuration;
                this.freenetInterface = freenetInterface;
@@ -227,6 +236,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                this.updateChecker = new UpdateChecker(eventBus, freenetInterface);
                this.webOfTrustUpdater = webOfTrustUpdater;
                this.eventBus = eventBus;
+               this.postBuilderFactory = postBuilderFactory;
        }
 
        //
@@ -511,35 +521,21 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
        }
 
        /**
-        * Returns the post with the given ID.
+        * Returns a post builder.
         *
-        * @param postId
-        *            The ID of the post to get
-        * @return The post with the given ID, or a new post with the given ID
+        * @return A new post builder
         */
-       public Post getPost(String postId) {
-               return getPost(postId, true);
+       public PostBuilder postBuilder() {
+               return postBuilderFactory.newPostBuilder();
        }
 
        /**
-        * Returns the post with the given ID, optionally creating a new post.
-        *
-        * @param postId
-        *            The ID of the post to get
-        * @param create
-        *            {@code true} it create a new post if no post with the given ID
-        *            exists, {@code false} to return {@code null}
-        * @return The post, or {@code null} if there is no such post
+        * {@inheritDoc}
         */
        @Override
-       public Post getPost(String postId, boolean create) {
+       public Post getPost(String postId) {
                synchronized (posts) {
-                       Post post = posts.get(postId);
-                       if ((post == null) && create) {
-                               post = new PostImpl(postId);
-                               posts.put(postId, post);
-                       }
-                       return post;
+                       return posts.get(postId);
                }
        }
 
@@ -594,18 +590,20 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
         *            The post to get all replies for
         * @return All replies for the given post
         */
-       public List<PostReply> getReplies(Post post) {
-               Set<Sone> sones = getSones();
-               List<PostReply> replies = new ArrayList<PostReply>();
-               for (Sone sone : sones) {
-                       for (PostReply reply : sone.getReplies()) {
-                               if (reply.getPost().equals(post)) {
-                                       replies.add(reply);
-                               }
+       public List<PostReply> getReplies(final Post post) {
+               return Ordering.from(Reply.TIME_COMPARATOR).sortedCopy(FluentIterable.from(getSones()).transformAndConcat(new Function<Sone, Iterable<PostReply>>() {
+
+                       @Override
+                       public Iterable<PostReply> apply(Sone sone) {
+                               return sone.getReplies();
                        }
-               }
-               Collections.sort(replies, Reply.TIME_COMPARATOR);
-               return replies;
+               }).filter(new Predicate<PostReply>() {
+
+                       @Override
+                       public boolean apply(PostReply reply) {
+                               return post.equals(reply.getPost());
+                       }
+               }));
        }
 
        /**
@@ -677,7 +675,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                Set<Post> posts = new HashSet<Post>();
                synchronized (bookmarkedPosts) {
                        for (String bookmarkedPostId : bookmarkedPosts) {
-                               Post post = getPost(bookmarkedPostId, false);
+                               Post post = getPost(bookmarkedPostId);
                                if (post != null) {
                                        posts.add(post);
                                }
@@ -824,7 +822,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                                logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1);
                                return null;
                        }
-                       sone.setLatestEdition(Optional.fromNullable(Longs.tryParse(ownIdentity.getProperty("Sone.LatestEdition"))).or(0L));
+                       sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0));
                        sone.setClient(new Client("Sone", SonePlugin.VERSION.toString()));
                        sone.setKnown(true);
                        /* TODO - load posts ’n stuff */
@@ -879,7 +877,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity);
                        boolean newSone = sone.getRequestUri() == null;
                        sone.setRequestUri(getSoneUri(identity.getRequestUri()));
-                       sone.setLatestEdition(Optional.fromNullable(Longs.tryParse(identity.getProperty("Sone.LatestEdition"))).or(0L));
+                       sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0));
                        if (newSone) {
                                synchronized (knownSones) {
                                        newSone = !knownSones.contains(sone.getId());
@@ -1110,16 +1108,18 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                                List<Post> storedPosts = storedSone.getPosts();
                                synchronized (knownPosts) {
                                        for (Post post : sone.getPosts()) {
-                                               post.setSone(storedSone).setKnown(knownPosts.contains(post.getId()));
-                                               if (!storedPosts.contains(post)) {
-                                                       if (post.getTime() < getSoneFollowingTime(sone)) {
-                                                               knownPosts.add(post.getId());
-                                                               post.setKnown(true);
-                                                       } else if (!knownPosts.contains(post.getId())) {
-                                                               eventBus.post(new NewPostFoundEvent(post));
+                                               PostBuilder postBuilder = postBuilderFactory.newPostBuilder();
+                                               postBuilder.copyPost(post).from(storedSone);
+                                               Post newPost = postBuilder.build().setKnown(knownPosts.contains(post.getId()));
+                                               if (!storedPosts.contains(newPost)) {
+                                                       if (newPost.getTime() < getSoneFollowingTime(sone)) {
+                                                               knownPosts.add(newPost.getId());
+                                                               newPost.setKnown(true);
+                                                       } else if (!knownPosts.contains(newPost.getId())) {
+                                                               eventBus.post(new NewPostFoundEvent(newPost));
                                                        }
                                                }
-                                               posts.put(post.getId(), post);
+                                               posts.put(newPost.getId(), newPost);
                                        }
                                }
                        }
@@ -1312,11 +1312,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                                logger.log(Level.WARNING, "Invalid post found, aborting load!");
                                return;
                        }
-                       Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText);
+                       PostBuilder postBuilder = postBuilder().withId(postId).from(sone).withTime(postTime).withText(postText);
                        if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
-                               post.setRecipient(getSone(postRecipientId));
+                               postBuilder.to(getSone(postRecipientId));
                        }
-                       posts.add(post);
+                       posts.add(postBuilder.build());
                }
 
                /* load replies. */
@@ -1538,17 +1538,19 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone));
                        return null;
                }
-               final Post post = new PostImpl(sone, time, text.trim());
+               PostBuilder postBuilder = postBuilderFactory.newPostBuilder();
+               postBuilder.from(sone).randomId().withTime(time).withText(text.trim());
                if (recipient != null) {
-                       post.setRecipient(recipient);
+                       postBuilder.to(recipient);
                }
+               final Post post = postBuilder.build();
                synchronized (posts) {
                        posts.put(post.getId(), post);
                }
                eventBus.post(new NewPostFoundEvent(post));
                sone.addPost(post);
                touchConfiguration();
-               localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() {
+               localElementTicker.schedule(new Runnable() {
 
                        /**
                         * {@inheritDoc}
@@ -1557,7 +1559,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        public void run() {
                                markPostKnown(post);
                        }
-               }, "Mark " + post + " read.");
+               }, 10, TimeUnit.SECONDS);
                return post;
        }
 
@@ -1689,7 +1691,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                }
                sone.addReply(reply);
                touchConfiguration();
-               localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() {
+               localElementTicker.schedule(new Runnable() {
 
                        /**
                         * {@inheritDoc}
@@ -1698,7 +1700,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        public void run() {
                                markReplyKnown(reply);
                        }
-               }, "Mark " + reply + " read.");
+               }, 10, TimeUnit.SECONDS);
                return reply;
        }
 
@@ -1941,6 +1943,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
         */
        @Override
        public void serviceStop() {
+               localElementTicker.shutdownNow();
                synchronized (sones) {
                        for (Entry<Sone, SoneInserter> soneInserter : soneInserters.entrySet()) {
                                soneInserter.getValue().stop();
@@ -2394,7 +2397,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        public void run() {
                                Sone sone = getRemoteSone(identity.getId(), false);
                                sone.setIdentity(identity);
-                               sone.setLatestEdition(Optional.fromNullable(Longs.tryParse(identity.getProperty("Sone.LatestEdition"))).or(sone.getLatestEdition()));
+                               sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.getLatestEdition()));
                                soneDownloader.addSone(sone);
                                soneDownloader.fetchSone(sone);
                        }