Store identities in database.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Core.java
index 9a8b7f9..4bf8a8a 100644 (file)
@@ -24,8 +24,8 @@ 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 static net.pterodactylus.sone.data.Sone.TO_FREENET_URI;
 
-import java.net.MalformedURLException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -62,7 +62,7 @@ import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.PostReply;
 import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Profile.Field;
-import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Reply.Modifier.ReplyUpdated;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
 import net.pterodactylus.sone.data.Sone.SoneStatus;
@@ -72,10 +72,9 @@ import net.pterodactylus.sone.database.DatabaseException;
 import net.pterodactylus.sone.database.ImageBuilder.ImageCreated;
 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.SoneBuilder.SoneCreated;
 import net.pterodactylus.sone.database.SoneProvider;
 import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
@@ -96,8 +95,7 @@ import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.service.AbstractService;
 import net.pterodactylus.util.thread.NamedThreadFactory;
 
-import freenet.keys.FreenetURI;
-
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
@@ -115,14 +113,11 @@ import com.google.inject.Inject;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider {
+public class Core extends AbstractService implements SoneProvider {
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(Core.class);
 
-       /** The start time. */
-       private final long startupTime = System.currentTimeMillis();
-
        /** The options. */
        private final Options options = new Options();
 
@@ -238,15 +233,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        //
 
        /**
-        * Returns the time Sone was started.
-        *
-        * @return The startup time (in milliseconds since Jan 1, 1970 UTC)
-        */
-       public long getStartupTime() {
-               return startupTime;
-       }
-
-       /**
         * Sets the configuration to use. This will automatically save the current
         * configuration to the given configuration.
         *
@@ -333,7 +319,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                }
        }
 
-       /** {@inheritDocs} */
        @Override
        public Collection<Sone> getSones() {
                synchronized (sones) {
@@ -341,15 +326,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                }
        }
 
-       /**
-        * Returns the Sone with the given ID, regardless whether it’s local or
-        * remote.
-        *
-        * @param id
-        *              The ID of the Sone to get
-        * @return The Sone with the given ID, or {@code null} if there is no such
-        *         Sone
-        */
+       @Override
+       public Function<String, Optional<Sone>> getSone() {
+               return database.getSone();
+       }
+
        @Override
        public Optional<Sone> getSone(String id) {
                synchronized (sones) {
@@ -357,7 +338,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                }
        }
 
-       /** {@inheritDocs} */
        @Override
        public Collection<Sone> getLocalSones() {
                synchronized (sones) {
@@ -382,7 +362,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                return from(database.getSone(id).asSet()).firstMatch(LOCAL_SONE_FILTER);
        }
 
-       /** {@inheritDocs} */
        @Override
        public Collection<Sone> getRemoteSones() {
                synchronized (sones) {
@@ -434,87 +413,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        }
 
        /**
-        * Returns whether the target Sone is trusted by the origin Sone.
-        *
-        * @param origin
-        *              The origin Sone
-        * @param target
-        *              The target Sone
-        * @return {@code true} if the target Sone is trusted by the origin Sone
-        */
-       public boolean isSoneTrusted(Sone origin, Sone target) {
-               checkNotNull(origin, "origin must not be null");
-               checkNotNull(target, "target must not be null");
-               checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin’s identity must be an OwnIdentity");
-               return trustedIdentities.containsEntry(origin.getIdentity(), target.getIdentity());
-       }
-
-       /** {@inheritDoc} */
-       @Override
-       public Optional<Post> getPost(String postId) {
-               return database.getPost(postId);
-       }
-
-       /** {@inheritDocs} */
-       @Override
-       public Collection<Post> getPosts(String soneId) {
-               return database.getPosts(soneId);
-       }
-
-       /** {@inheritDoc} */
-       @Override
-       public Collection<Post> getDirectedPosts(final String recipientId) {
-               checkNotNull(recipientId, "recipient must not be null");
-               return database.getDirectedPosts(recipientId);
-       }
-
-       /** {@inheritDoc} */
-       @Override
-       public Optional<PostReply> getPostReply(String replyId) {
-               return database.getPostReply(replyId);
-       }
-
-       /** {@inheritDoc} */
-       @Override
-       public List<PostReply> getReplies(final String postId) {
-               return database.getReplies(postId);
-       }
-
-       /**
-        * Returns all Sones that have liked the given post.
-        *
-        * @param post
-        *              The post to get the liking Sones for
-        * @return The Sones that like the given post
-        */
-       public Set<Sone> getLikes(Post post) {
-               Set<Sone> sones = new HashSet<Sone>();
-               for (Sone sone : getSones()) {
-                       if (sone.getLikedPostIds().contains(post.getId())) {
-                               sones.add(sone);
-                       }
-               }
-               return sones;
-       }
-
-       /**
-        * Returns all Sones that have liked the given reply.
-        *
-        * @param reply
-        *              The reply to get the liking Sones for
-        * @return The Sones that like the given reply
-        */
-       public Set<Sone> getLikes(PostReply reply) {
-               Set<Sone> sones = new HashSet<Sone>();
-               for (Sone sone : getSones()) {
-                       if (sone.getLikedReplyIds().contains(reply.getId())) {
-                               sones.add(sone);
-                       }
-               }
-               return sones;
-       }
-
-       /**
         * Returns whether the given post is bookmarked.
         *
         * @param post
@@ -549,7 +447,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                Set<Post> posts = new HashSet<Post>();
                synchronized (bookmarkedPosts) {
                        for (String bookmarkedPostId : bookmarkedPosts) {
-                               Optional<Post> post = getPost(bookmarkedPostId);
+                               Optional<Post> post = database.getPost(bookmarkedPostId);
                                if (post.isPresent()) {
                                        posts.add(post.get());
                                }
@@ -629,14 +527,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                logger.info(String.format("Adding Sone from OwnIdentity: %s", ownIdentity));
                synchronized (sones) {
                        final Sone sone;
-                       try {
-                               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;
-                       }
-                       sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0));
-                       sone.setClient(new Client("Sone", SonePlugin.VERSION.toString()));
+                       sone = database.newSoneBuilder().by(ownIdentity.getId()).local().using(new Client("Sone", SonePlugin.VERSION.toString())).build(Optional.<SoneCreated>absent());
+                       sone.modify().setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0)).update();
                        sone.setKnown(true);
                        /* TODO - load posts ’n stuff */
                        sones.put(ownIdentity.getId(), sone);
@@ -687,14 +579,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        return null;
                }
                synchronized (sones) {
-                       final Sone sone = database.newSoneBuilder().by(identity).build();
-                       if (sone.isLocal()) {
-                               return sone;
-                       }
-                       sone.setIdentity(identity);
-                       boolean newSone = sone.getRequestUri() == null;
-                       sone.setRequestUri(SoneUri.create(identity.getRequestUri()));
-                       sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0));
+                       Optional<Sone> existingSone = database.getSone(identity.getId());
+                       if (existingSone.isPresent() && existingSone.get().isLocal()) {
+                               return existingSone.get();
+                       }
+                       boolean newSone = !existingSone.isPresent();
+                       final Sone sone = newSone ? database.newSoneBuilder().by(identity.getId()).using(new Client("Sone", SonePlugin.VERSION.toString())).build(Optional.<SoneCreated>absent()) : existingSone.get();
+                       sone.modify().setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)).update();
                        if (newSone) {
                                synchronized (knownSones) {
                                        newSone = !knownSones.contains(sone.getId());
@@ -715,7 +606,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                @Override
                                @SuppressWarnings("synthetic-access")
                                public void run() {
-                                       soneDownloader.fetchSone(sone, sone.getRequestUri());
+                                       soneDownloader.fetchSone(sone, TO_FREENET_URI.apply(sone));
                                }
 
                        });
@@ -750,7 +641,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                }
                                for (PostReply reply : followedSone.get().getReplies()) {
                                        if (reply.getTime() < now) {
-                                               markReplyKnown(reply);
+                                               reply.modify().setKnown().update(Optional.<ReplyUpdated<PostReply>>absent());
                                        }
                                }
                        }
@@ -912,7 +803,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                        continue;
                                }
                                if (reply.getTime() < getSoneFollowingTime(sone)) {
-                                       reply.setKnown(true);
+                                       reply.modify().setKnown().update(Optional.<ReplyUpdated<PostReply>>absent());
                                } else if (!reply.isKnown()) {
                                        eventBus.post(new NewPostReplyFoundEvent(reply));
                                }
@@ -1025,12 +916,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
 
                /* load profile. */
                Profile profile = new Profile(sone);
-               profile.setFirstName(configuration.getStringValue(sonePrefix + "/Profile/FirstName").getValue(null));
-               profile.setMiddleName(configuration.getStringValue(sonePrefix + "/Profile/MiddleName").getValue(null));
-               profile.setLastName(configuration.getStringValue(sonePrefix + "/Profile/LastName").getValue(null));
-               profile.setBirthDay(configuration.getIntValue(sonePrefix + "/Profile/BirthDay").getValue(null));
-               profile.setBirthMonth(configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").getValue(null));
-               profile.setBirthYear(configuration.getIntValue(sonePrefix + "/Profile/BirthYear").getValue(null));
+               String firstName = configuration.getStringValue(sonePrefix + "/Profile/FirstName").getValue(null);
+               String middleName = configuration.getStringValue(sonePrefix + "/Profile/MiddleName").getValue(null);
+               String lastName = configuration.getStringValue(sonePrefix + "/Profile/LastName").getValue(null);
+               profile.modify().setFirstName(firstName).setMiddleName(middleName).setLastName(lastName).update();
+               Integer birthDay = configuration.getIntValue(sonePrefix + "/Profile/BirthDay").getValue(null);
+               Integer birthMonth = configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").getValue(null);
+               Integer birthYear = configuration.getIntValue(sonePrefix + "/Profile/BirthYear").getValue(null);
+               profile.modify().setBirthYear(birthYear).setBirthMonth(birthMonth).setBirthDay(birthDay).update();
 
                /* load profile fields. */
                while (true) {
@@ -1040,7 +933,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                break;
                        }
                        String fieldValue = configuration.getStringValue(fieldPrefix + "/Value").getValue("");
-                       profile.addField(fieldName).setValue(fieldValue);
+                       profile.setField(profile.addField(fieldName), fieldValue);
                }
 
                /* load posts. */
@@ -1169,7 +1062,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                /* load avatar. */
                String avatarId = configuration.getStringValue(sonePrefix + "/Profile/Avatar").getValue(null);
                if (avatarId != null) {
-                       profile.setAvatar(getImage(avatarId).orNull());
+                       profile.setAvatar(getImage(avatarId).transform(GET_ID));
                }
 
                /* load options. */
@@ -1207,7 +1100,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                }
                database.storePostReplies(sone, replies);
                for (PostReply reply : replies) {
-                       reply.setKnown(true);
+                       reply.modify().setKnown().update(Optional.<ReplyUpdated<PostReply>>absent());
                }
 
                logger.info(String.format("Sone loaded successfully: %s", sone));
@@ -1241,22 +1134,12 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                post.setKnown(true);
                eventBus.post(new MarkPostKnownEvent(post));
                touchConfiguration();
-               for (PostReply reply : getReplies(post.getId())) {
-                       markReplyKnown(reply);
+               for (PostReply reply : post.getReplies()) {
+                       reply.modify().setKnown().update(postReplyUpdated());
                }
        }
 
        /**
-        * Bookmarks the given post.
-        *
-        * @param post
-        *              The post to bookmark
-        */
-       public void bookmark(Post post) {
-               bookmarkPost(post.getId());
-       }
-
-       /**
         * Bookmarks the post with the given ID.
         *
         * @param id
@@ -1302,29 +1185,12 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        logger.log(Level.FINE, String.format("Tried to delete non-local reply: %s", reply));
                        return;
                }
+               postReplyUpdated().get().replyUpdated(reply);
                database.removePostReply(reply);
-               markReplyKnown(reply);
-               sone.removeReply(reply);
                touchConfiguration();
        }
 
        /**
-        * Marks the given reply as known, if it is currently not a known reply
-        * (according to {@link Reply#isKnown()}).
-        *
-        * @param reply
-        *              The reply to mark as known
-        */
-       public void markReplyKnown(PostReply reply) {
-               boolean previouslyKnown = reply.isKnown();
-               reply.setKnown(true);
-               eventBus.post(new MarkPostReplyKnownEvent(reply));
-               if (!previouslyKnown) {
-                       touchConfiguration();
-               }
-       }
-
-       /**
         * Creates a new image.
         *
         * @param sone
@@ -1352,7 +1218,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *
         * @param image
         *              The image to delete
-        * @see #deleteTemporaryImage(TemporaryImage)
+        * @see #deleteTemporaryImage(String)
         */
        public void deleteImage(Image image) {
                checkNotNull(image, "image must not be null");
@@ -1380,17 +1246,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        }
 
        /**
-        * Deletes the given temporary image.
-        *
-        * @param temporaryImage
-        *              The temporary image to delete
-        */
-       public void deleteTemporaryImage(TemporaryImage temporaryImage) {
-               checkNotNull(temporaryImage, "temporaryImage must not be null");
-               deleteTemporaryImage(temporaryImage.getId());
-       }
-
-       /**
         * Deletes the temporary image with the given ID.
         *
         * @param imageId
@@ -1419,7 +1274,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        // SERVICE METHODS
        //
 
-       /** Starts the core. */
        @Override
        public void serviceStart() {
                loadConfiguration();
@@ -1430,7 +1284,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                database.start();
        }
 
-       /** {@inheritDoc} */
        @Override
        public void serviceRun() {
                long lastSaved = System.currentTimeMillis();
@@ -1447,7 +1300,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                }
        }
 
-       /** Stops the core. */
        @Override
        public void serviceStop() {
                localElementTicker.shutdownNow();
@@ -1794,6 +1646,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                OwnIdentity ownIdentity = ownIdentityAddedEvent.ownIdentity();
                logger.log(Level.FINEST, String.format("Adding OwnIdentity: %s", ownIdentity));
                if (ownIdentity.hasContext("Sone")) {
+                       database.storeIdentity(ownIdentity);
                        addLocalSone(ownIdentity);
                }
        }
@@ -1822,6 +1675,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                Identity identity = identityAddedEvent.identity();
                logger.log(Level.FINEST, String.format("Adding Identity: %s", identity));
                trustedIdentities.put(identityAddedEvent.ownIdentity(), identity);
+               database.storeIdentity(identity);
                addRemoteSone(identity);
        }
 
@@ -1834,14 +1688,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        @Subscribe
        public void identityUpdated(IdentityUpdatedEvent identityUpdatedEvent) {
                final Identity identity = identityUpdatedEvent.identity();
+               database.storeIdentity(identity);
                soneDownloaders.execute(new Runnable() {
 
                        @Override
                        @SuppressWarnings("synthetic-access")
                        public void run() {
                                Optional<Sone> sone = getRemoteSone(identity.getId());
-                               sone.get().setIdentity(identity);
-                               sone.get().setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.get().getLatestEdition()));
+                               sone.get().modify().setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.get().getLatestEdition())).update();
                                soneDownloader.addSone(sone.get());
                                soneDownloader.fetchSone(sone.get());
                        }
@@ -1941,7 +1795,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                                 */
                                                @Override
                                                public void run() {
-                                                       markReplyKnown(postReply);
+                                                       postReplyUpdated().get().replyUpdated(postReply);
                                                }
                                        }, 10, TimeUnit.SECONDS);
                                }
@@ -1949,6 +1803,15 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                });
        }
 
+       public Optional<ReplyUpdated<PostReply>> postReplyUpdated() {
+               return Optional.<ReplyUpdated<PostReply>>of(new ReplyUpdated<PostReply>() {
+                       @Override
+                       public void replyUpdated(PostReply reply) {
+                               eventBus.post(new MarkPostReplyKnownEvent(reply));
+                       }
+               });
+       }
+
        public Optional<ImageCreated> imageCreated() {
                return Optional.<ImageCreated>of(new ImageCreated() {
                        @Override