Rename post and reply implementations; use builder to create replies.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Core.java
index 4e7f18d..7f74e14 100644 (file)
 
 package net.pterodactylus.sone.core;
 
+import static com.google.common.base.Optional.of;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.FluentIterable.from;
+import static net.pterodactylus.sone.data.Identified.GET_ID;
+import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER;
 
 import java.net.MalformedURLException;
 import java.util.Collection;
@@ -61,13 +66,14 @@ import net.pterodactylus.sone.data.Reply;
 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.SoneImpl;
 import net.pterodactylus.sone.data.TemporaryImage;
 import net.pterodactylus.sone.database.Database;
 import net.pterodactylus.sone.database.DatabaseException;
 import net.pterodactylus.sone.database.PostBuilder;
+import net.pterodactylus.sone.database.PostBuilder.PostCreated;
 import net.pterodactylus.sone.database.PostProvider;
 import net.pterodactylus.sone.database.PostReplyBuilder;
+import net.pterodactylus.sone.database.PostReplyBuilder.PostReplyCreated;
 import net.pterodactylus.sone.database.PostReplyProvider;
 import net.pterodactylus.sone.database.SoneProvider;
 import net.pterodactylus.sone.fcp.FcpInterface;
@@ -94,7 +100,6 @@ import freenet.keys.FreenetURI;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
@@ -289,6 +294,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                this.fcpInterface = fcpInterface;
        }
 
+       public Database getDatabase() {
+               return database;
+       }
+
        /**
         * Returns the Sone rescuer for the given local Sone.
         *
@@ -351,7 +360,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        @Override
        public Collection<Sone> getLocalSones() {
                synchronized (sones) {
-                       return FluentIterable.from(sones.values()).filter(new Predicate<Sone>() {
+                       return from(sones.values()).filter(new Predicate<Sone>() {
 
                                @Override
                                public boolean apply(Sone sone) {
@@ -366,31 +375,17 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *
         * @param id
         *              The ID of the Sone
-        * @param create
-        *              {@code true} to create a new Sone if none exists, {@code false} to return
-        *              null if none exists
         * @return The Sone with the given ID, or {@code null}
         */
-       public Sone getLocalSone(String id, boolean create) {
-               synchronized (sones) {
-                       Sone sone = sones.get(id);
-                       if ((sone == null) && create) {
-                               sone = new SoneImpl(id, true);
-                               sones.put(id, sone);
-                       }
-                       if ((sone != null) && !sone.isLocal()) {
-                               sone = new SoneImpl(id, true);
-                               sones.put(id, sone);
-                       }
-                       return sone;
-               }
+       public Optional<Sone> getLocalSone(String id) {
+               return from(database.getSone(id).asSet()).firstMatch(LOCAL_SONE_FILTER);
        }
 
        /** {@inheritDocs} */
        @Override
        public Collection<Sone> getRemoteSones() {
                synchronized (sones) {
-                       return FluentIterable.from(sones.values()).filter(new Predicate<Sone>() {
+                       return from(sones.values()).filter(new Predicate<Sone>() {
 
                                @Override
                                public boolean apply(Sone sone) {
@@ -405,20 +400,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *
         * @param id
         *              The ID of the remote Sone to get
-        * @param create
-        *              {@code true} to always create a Sone, {@code false} to return {@code null}
-        *              if no Sone with the given ID exists
         * @return The Sone with the given ID
         */
-       public Sone getRemoteSone(String id, boolean create) {
-               synchronized (sones) {
-                       Sone sone = sones.get(id);
-                       if ((sone == null) && create && (id != null) && (id.length() == 43)) {
-                               sone = new SoneImpl(id, false);
-                               sones.put(id, sone);
-                       }
-                       return sone;
-               }
+       public Optional<Sone> getRemoteSone(String id) {
+               return from(database.getSone(id).asSet()).firstMatch(not(LOCAL_SONE_FILTER));
        }
 
        /**
@@ -463,15 +448,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                return trustedIdentities.containsEntry(origin.getIdentity(), target.getIdentity());
        }
 
-       /**
-        * Returns a post builder.
-        *
-        * @return A new post builder
-        */
-       public PostBuilder postBuilder() {
-               return database.newPostBuilder();
-       }
-
        /** {@inheritDoc} */
        @Override
        public Optional<Post> getPost(String postId) {
@@ -491,15 +467,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                return database.getDirectedPosts(recipientId);
        }
 
-       /**
-        * Returns a post reply builder.
-        *
-        * @return A new post reply builder
-        */
-       public PostReplyBuilder postReplyBuilder() {
-               return database.newPostReplyBuilder();
-       }
-
        /** {@inheritDoc} */
        @Override
        public Optional<PostReply> getPostReply(String replyId) {
@@ -662,7 +629,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                synchronized (sones) {
                        final Sone sone;
                        try {
-                               sone = getLocalSone(ownIdentity.getId(), true).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri()));
+                               sone = database.newSoneBuilder().by(ownIdentity).local().build().setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri()));
                        } catch (MalformedURLException mue1) {
                                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;
@@ -719,7 +686,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        return null;
                }
                synchronized (sones) {
-                       final Sone sone = getRemoteSone(identity.getId(), true);
+                       final Sone sone = database.newSoneBuilder().by(identity).build();
                        if (sone.isLocal()) {
                                return sone;
                        }
@@ -1090,11 +1057,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                logger.log(Level.WARNING, "Invalid post found, aborting load!");
                                return;
                        }
-                       PostBuilder postBuilder = postBuilder().withId(postId).from(sone.getId()).withTime(postTime).withText(postText);
+                       PostBuilder postBuilder = sone.newPostBuilder().withId(postId).withTime(postTime).withText(postText);
                        if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
-                               postBuilder.to(postRecipientId);
+                               postBuilder.to(of(postRecipientId));
                        }
-                       posts.add(postBuilder.build());
+                       posts.add(postBuilder.build(Optional.<PostCreated>absent()));
                }
 
                /* load replies. */
@@ -1112,8 +1079,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                logger.log(Level.WARNING, "Invalid reply found, aborting load!");
                                return;
                        }
-                       PostReplyBuilder postReplyBuilder = postReplyBuilder().withId(replyId).from(sone.getId()).to(postId).withTime(replyTime).withText(replyText);
-                       replies.add(postReplyBuilder.build());
+                       PostReplyBuilder postReplyBuilder = sone.newPostReplyBuilder(postId).withId(replyId).withTime(replyTime).withText(replyText);
+                       replies.add(postReplyBuilder.build(Optional.<PostReplyCreated>absent()));
                }
 
                /* load post likes. */
@@ -1224,7 +1191,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                followSone(sone, friendId);
                        }
                        for (Album album : sone.getRootAlbum().getAlbums()) {
-                               sone.getRootAlbum().removeAlbum(album);
+                               album.remove();
                        }
                        soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint);
                }
@@ -1246,94 +1213,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        }
 
        /**
-        * Creates a new post.
-        *
-        * @param sone
-        *              The Sone that creates the post
-        * @param text
-        *              The text of the post
-        * @return The created post
-        */
-       public Post createPost(Sone sone, String text) {
-               return createPost(sone, System.currentTimeMillis(), text);
-       }
-
-       /**
-        * Creates a new post.
-        *
-        * @param sone
-        *              The Sone that creates the post
-        * @param time
-        *              The time of the post
-        * @param text
-        *              The text of the post
-        * @return The created post
-        */
-       public Post createPost(Sone sone, long time, String text) {
-               return createPost(sone, null, time, text);
-       }
-
-       /**
-        * Creates a new post.
-        *
-        * @param sone
-        *              The Sone that creates the post
-        * @param recipient
-        *              The recipient Sone, or {@code null} if this post does not have a
-        *              recipient
-        * @param text
-        *              The text of the post
-        * @return The created post
-        */
-       public Post createPost(Sone sone, Optional<Sone> recipient, String text) {
-               return createPost(sone, recipient, System.currentTimeMillis(), text);
-       }
-
-       /**
-        * Creates a new post.
-        *
-        * @param sone
-        *              The Sone that creates the post
-        * @param recipient
-        *              The recipient Sone, or {@code null} if this post does not have a
-        *              recipient
-        * @param time
-        *              The time of the post
-        * @param text
-        *              The text of the post
-        * @return The created post
-        */
-       public Post createPost(Sone sone, Optional<Sone> recipient, long time, String text) {
-               checkNotNull(text, "text must not be null");
-               checkArgument(text.trim().length() > 0, "text must not be empty");
-               if (!sone.isLocal()) {
-                       logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone));
-                       return null;
-               }
-               PostBuilder postBuilder = database.newPostBuilder();
-               postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim());
-               if (recipient.isPresent()) {
-                       postBuilder.to(recipient.get().getId());
-               }
-               final Post post = postBuilder.build();
-               database.storePost(post);
-               eventBus.post(new NewPostFoundEvent(post));
-               sone.addPost(post);
-               touchConfiguration();
-               localElementTicker.schedule(new Runnable() {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @Override
-                       public void run() {
-                               markPostKnown(post);
-                       }
-               }, 10, TimeUnit.SECONDS);
-               return post;
-       }
-
-       /**
         * Deletes the given post.
         *
         * @param post
@@ -1411,44 +1290,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        }
 
        /**
-        * Creates a new reply.
-        *
-        * @param sone
-        *              The Sone that creates the reply
-        * @param post
-        *              The post that this reply refers to
-        * @param text
-        *              The text of the reply
-        * @return The created reply
-        */
-       public PostReply createReply(Sone sone, Post post, String text) {
-               checkNotNull(text, "text must not be null");
-               checkArgument(text.trim().length() > 0, "text must not be empty");
-               if (!sone.isLocal()) {
-                       logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone));
-                       return null;
-               }
-               PostReplyBuilder postReplyBuilder = postReplyBuilder();
-               postReplyBuilder.randomId().from(sone.getId()).to(post.getId()).currentTime().withText(text.trim());
-               final PostReply reply = postReplyBuilder.build();
-               database.storePostReply(reply);
-               eventBus.post(new NewPostReplyFoundEvent(reply));
-               sone.addReply(reply);
-               touchConfiguration();
-               localElementTicker.schedule(new Runnable() {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @Override
-                       public void run() {
-                               markReplyKnown(reply);
-                       }
-               }, 10, TimeUnit.SECONDS);
-               return reply;
-       }
-
-       /**
         * Deletes the given reply.
         *
         * @param reply
@@ -1483,24 +1324,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        }
 
        /**
-        * Deletes the given album. The owner of the album has to be a local Sone, and
-        * the album has to be {@link Album#isEmpty() empty} to be deleted.
-        *
-        * @param album
-        *              The album to remove
-        */
-       public void deleteAlbum(Album album) {
-               checkNotNull(album, "album must not be null");
-               checkArgument(album.getSone().isLocal(), "album’s Sone must be a local Sone");
-               if (!album.isEmpty()) {
-                       return;
-               }
-               album.getParent().removeAlbum(album);
-               database.removeAlbum(album);
-               touchConfiguration();
-       }
-
-       /**
         * Creates a new image.
         *
         * @param sone
@@ -1517,7 +1340,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                checkNotNull(temporaryImage, "temporaryImage must not be null");
                checkArgument(sone.isLocal(), "sone must be a local Sone");
                checkArgument(sone.equals(album.getSone()), "album must belong to the given Sone");
-               Image image = album.newImageBuilder().withId(temporaryImage.getId()).createdNow().sized(temporaryImage.getWidth(), temporaryImage.getHeight()).build();
+               Image image = album.newImageBuilder().withId(temporaryImage.getId()).sized(temporaryImage.getWidth(), temporaryImage.getHeight()).build();
                imageInserter.insertImage(temporaryImage, image);
                return image;
        }
@@ -1733,7 +1556,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
 
                        /* save albums. first, collect in a flat structure, top-level first. */
-                       List<Album> albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList();
+                       List<Album> albums = from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList();
 
                        int albumCounter = 0;
                        for (Album album : albums) {
@@ -1742,7 +1565,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle());
                                configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription());
                                configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId());
-                               configuration.getStringValue(albumPrefix + "/AlbumImage").setValue(album.getAlbumImage() == null ? null : album.getAlbumImage().getId());
+                               configuration.getStringValue(albumPrefix + "/AlbumImage").setValue(album.getAlbumImage().transform(GET_ID).orNull());
                        }
                        configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null);
 
@@ -2015,14 +1838,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        @Override
                        @SuppressWarnings("synthetic-access")
                        public void run() {
-                               Sone sone = getRemoteSone(identity.getId(), false);
-                               if (sone.isLocal()) {
-                                       return;
-                               }
-                               sone.setIdentity(identity);
-                               sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.getLatestEdition()));
-                               soneDownloader.addSone(sone);
-                               soneDownloader.fetchSone(sone);
+                               Optional<Sone> sone = getRemoteSone(identity.getId());
+                               sone.get().setIdentity(identity);
+                               sone.get().setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.get().getLatestEdition()));
+                               soneDownloader.addSone(sone.get());
+                               soneDownloader.fetchSone(sone.get());
                        }
                });
        }
@@ -2084,4 +1904,48 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                touchConfiguration();
        }
 
+       public Optional<PostCreated> postCreated() {
+               return Optional.<PostCreated>of(new PostCreated() {
+                       @Override
+                       public void postCreated(final Post post) {
+                               if (post.isKnown()) {
+                                       return;
+                               }
+                               eventBus.post(new NewPostFoundEvent(post));
+                               if (post.getSone().isLocal()) {
+                                       localElementTicker.schedule(new Runnable() {
+                                               @Override
+                                               public void run() {
+                                                       markPostKnown(post);
+                                               }
+                                       }, 10, TimeUnit.SECONDS);
+                               }
+                       }
+               });
+       }
+
+       public Optional<PostReplyCreated> postReplyCreated() {
+               return Optional.<PostReplyCreated>of(new PostReplyCreated() {
+                       @Override
+                       public void postReplyCreated(final PostReply postReply) {
+                               if (postReply.isKnown()) {
+                                       return;
+                               }
+                               eventBus.post(new NewPostReplyFoundEvent(postReply));
+                               if (postReply.getSone().isLocal()) {
+                                       localElementTicker.schedule(new Runnable() {
+
+                                               /**
+                                                * {@inheritDoc}
+                                                */
+                                               @Override
+                                               public void run() {
+                                                       markReplyKnown(postReply);
+                                               }
+                                       }, 10, TimeUnit.SECONDS);
+                               }
+                       }
+               });
+       }
+
 }