Remove possibility to create new replies from core interface.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Core.java
index ce7f0ce..5ce01fe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Sone - Core.java - Copyright © 2010–2012 David Roden
+ * Sone - Core.java - Copyright © 2010–2013 David Roden
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -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,7 @@ 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;
 import net.pterodactylus.sone.freenet.wot.Identity;
@@ -74,18 +78,16 @@ import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent;
 import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent;
 import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent;
 import net.pterodactylus.sone.main.SonePlugin;
+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 net.pterodactylus.util.validation.EqualityValidator;
-import net.pterodactylus.util.validation.IntegerRangeValidator;
-import net.pterodactylus.util.validation.OrValidator;
 
 import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import com.google.common.collect.Collections2;
 import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
@@ -167,6 +169,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>();
 
@@ -196,7 +201,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;
@@ -214,9 +219,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;
@@ -226,6 +233,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                this.updateChecker = new UpdateChecker(eventBus, freenetInterface);
                this.webOfTrustUpdater = webOfTrustUpdater;
                this.eventBus = eventBus;
+               this.postBuilderFactory = postBuilderFactory;
        }
 
        //
@@ -510,35 +518,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);
                }
        }
 
@@ -564,25 +558,15 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
        }
 
        /**
-        * Returns the reply with the given ID. If there is no reply with the given
-        * ID yet, a new one is created, unless {@code create} is false in which
-        * case {@code null} is returned.
+        * Returns the reply with the given ID.
         *
         * @param replyId
         *            The ID of the reply to get
-        * @param create
-        *            {@code true} to always return a {@link Reply}, {@code false}
-        *            to return {@code null} if no reply can be found
         * @return The reply, or {@code null} if there is no such reply
         */
-       public PostReply getPostReply(String replyId, boolean create) {
+       public PostReply getPostReply(String replyId) {
                synchronized (replies) {
-                       PostReply reply = replies.get(replyId);
-                       if (create && (reply == null)) {
-                               reply = new PostReply(replyId);
-                               replies.put(replyId, reply);
-                       }
-                       return reply;
+                       return replies.get(replyId);
                }
        }
 
@@ -676,7 +660,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);
                                }
@@ -1109,16 +1093,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);
                                        }
                                }
                        }
@@ -1311,11 +1297,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 = postBuilderFactory.newPostBuilder().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. */
@@ -1537,17 +1523,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}
@@ -1556,7 +1544,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        public void run() {
                                markPostKnown(post);
                        }
-               }, "Mark " + post + " read.");
+               }, 10, TimeUnit.SECONDS);
                return post;
        }
 
@@ -1679,7 +1667,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone));
                        return null;
                }
-               final PostReply reply = new PostReply(sone, post, System.currentTimeMillis(), text.trim());
+               final PostReply reply = new PostReplyImpl(sone, post, System.currentTimeMillis(), text.trim());
                synchronized (replies) {
                        replies.put(reply.getId(), reply);
                }
@@ -1688,7 +1676,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}
@@ -1697,7 +1685,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        public void run() {
                                markReplyKnown(reply);
                        }
-               }, "Mark " + reply + " read.");
+               }, 10, TimeUnit.SECONDS);
                return reply;
        }
 
@@ -1940,6 +1928,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();
@@ -2187,7 +2176,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
        @SuppressWarnings("unchecked")
        private void loadConfiguration() {
                /* create options. */
-               options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new IntegerRangeValidator(0, Integer.MAX_VALUE), new OptionWatcher<Integer>() {
+               options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new IntegerRangePredicate(0, Integer.MAX_VALUE), new OptionWatcher<Integer>() {
 
                        @Override
                        public void optionChanged(Option<Integer> option, Integer oldValue, Integer newValue) {
@@ -2195,13 +2184,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider
                        }
 
                }));
-               options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
-               options.addIntegerOption("ImagesPerPage", new DefaultOption<Integer>(9, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
-               options.addIntegerOption("CharactersPerPost", new DefaultOption<Integer>(400, new OrValidator<Integer>(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator<Integer>(-1))));
-               options.addIntegerOption("PostCutOffLength", new DefaultOption<Integer>(200, new OrValidator<Integer>(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator<Integer>(-1))));
+               options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangePredicate(1, Integer.MAX_VALUE)));
+               options.addIntegerOption("ImagesPerPage", new DefaultOption<Integer>(9, new IntegerRangePredicate(1, Integer.MAX_VALUE)));
+               options.addIntegerOption("CharactersPerPost", new DefaultOption<Integer>(400, Predicates.<Integer> or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1))));
+               options.addIntegerOption("PostCutOffLength", new DefaultOption<Integer>(200, Predicates.<Integer> or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1))));
                options.addBooleanOption("RequireFullAccess", new DefaultOption<Boolean>(false));
-               options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75, new IntegerRangeValidator(0, 100)));
-               options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25, new IntegerRangeValidator(-100, 100)));
+               options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75, new IntegerRangePredicate(0, 100)));
+               options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25, new IntegerRangePredicate(-100, 100)));
                options.addStringOption("TrustComment", new DefaultOption<String>("Set from Sone Web Interface"));
                options.addBooleanOption("ActivateFcpInterface", new DefaultOption<Boolean>(false, new OptionWatcher<Boolean>() {