Fix loading of local Sones without posts and replies.
[Sone.git] / src / main / java / net / pterodactylus / sone / database / memory / MemoryDatabase.java
index 83a886f..561f14f 100644 (file)
@@ -50,6 +50,7 @@ import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound
 import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Client;
 import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.data.LocalSone;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.PostReply;
 import net.pterodactylus.sone.data.Profile;
@@ -110,15 +111,6 @@ public class MemoryDatabase extends AbstractService implements Database {
        private final Map<String, Sone> allSones = new HashMap<String, Sone>();
        private final Map<String, String> lastInsertFingerprints = new HashMap<String, String>();
 
-       /** All posts by their ID. */
-       private final Map<String, Post> allPosts = new HashMap<String, Post>();
-
-       /** All posts by their Sones. */
-       private final Multimap<String, Post> sonePosts = HashMultimap.create();
-
-       /** Whether posts are known. */
-       private final Set<String> knownPosts = new HashSet<String>();
-
        /** All post replies by their ID. */
        private final Map<String, PostReply> allPostReplies = new HashMap<String, PostReply>();
 
@@ -140,6 +132,8 @@ public class MemoryDatabase extends AbstractService implements Database {
        private final Map<String, Image> allImages = new HashMap<String, Image>();
        private final Multimap<String, Image> soneImages = HashMultimap.create();
 
+       private final MemorySoneDatabase soneDatabase;
+       private final MemoryPostDatabase postDatabase;
        private final MemoryBookmarkDatabase memoryBookmarkDatabase;
        private final MemoryFriendDatabase memoryFriendDatabase;
 
@@ -156,6 +150,8 @@ public class MemoryDatabase extends AbstractService implements Database {
                this.soneProvider = soneProvider;
                this.configuration = configuration;
                this.configurationLoader = new ConfigurationLoader(configuration);
+               soneDatabase = new MemorySoneDatabase(configurationLoader);
+               postDatabase = new MemoryPostDatabase(this, configurationLoader);
                memoryBookmarkDatabase =
                                new MemoryBookmarkDatabase(this, configurationLoader);
                memoryFriendDatabase = new MemoryFriendDatabase(configurationLoader);
@@ -165,54 +161,77 @@ public class MemoryDatabase extends AbstractService implements Database {
        // DATABASE METHODS
        //
 
+       @Override
+       public Optional<LocalSone> getLocalSone(String localSoneId) {
+               lock.readLock().lock();
+               try {
+                       if (!localSones.contains(localSoneId)) {
+                               return Optional.absent();
+                       }
+                       return Optional.of((LocalSone) allSones.get(localSoneId));
+               } finally {
+                       lock.readLock().unlock();
+               }
+       }
 
        @Override
-       public Sone registerLocalSone(OwnIdentity ownIdentity) {
-               final Sone localSone = loadLocalSone(ownIdentity);
+       public LocalSone registerLocalSone(OwnIdentity ownIdentity) {
+               final LocalSone localSone = loadLocalSone(ownIdentity);
                localSones.add(ownIdentity.getId());
                return localSone;
        }
 
-       private Sone loadLocalSone(OwnIdentity ownIdentity) {
-               Sone localSone = newSoneBuilder().local().from(ownIdentity).build();
+       private LocalSone loadLocalSone(OwnIdentity ownIdentity) {
+               final SoneBuilder soneBuilder = newSoneBuilder().from(ownIdentity).using(
+                               new Client("Sone", SonePlugin.VERSION.toString()));
+
+               loadElements(soneBuilder, ownIdentity.getId());
+
+               LocalSone localSone = soneBuilder.buildLocal();
+               loadSone(localSone);
+               localSone.setKnown(true);
                localSone.setLatestEdition(
                                Optional.fromNullable(
                                                Longs.tryParse(ownIdentity.getProperty(LATEST_EDITION_PROPERTY)))
                                .or(0L));
-               localSone.setClient(new Client("Sone", SonePlugin.VERSION.toString()));
-               localSone.setKnown(true);
-
-               loadSone(localSone);
                return localSone;
        }
 
-       public void loadSone(Sone sone) {
-               long soneTime = configurationLoader.getLocalSoneTime(sone.getId());
-               if (soneTime == -1) {
-                       return;
-               }
-
-               /* load profile. */
-               ConfigurationSoneParser configurationSoneParser = new ConfigurationSoneParser(configuration, sone);
-               Profile profile = configurationSoneParser.parseProfile();
+       private void loadElements(SoneBuilder soneBuilder, String soneId) {
+               ConfigurationSoneParser configurationSoneParser = new ConfigurationSoneParser(configuration, soneId);
 
-               /* load posts. */
-               Collection<Post> posts;
                try {
-                       posts = configurationSoneParser.parsePosts(this);
+                       Set<Post> posts = configurationSoneParser.parsePosts(this);
+                       soneBuilder.withPosts(posts);
+                       for (Post post : posts) {
+                               post.setKnown(true);
+                       }
                } catch (InvalidPostFound ipf) {
                        logger.log(Level.WARNING, "Invalid post found, aborting load!");
                        return;
                }
 
-               /* load replies. */
-               Collection<PostReply> postReplies;
                try {
-                       postReplies = configurationSoneParser.parsePostReplies(this);
+                       Set<PostReply> postReplies = configurationSoneParser.parsePostReplies(this);
+                       soneBuilder.withPostReplies(postReplies);
+                       for (PostReply reply : postReplies) {
+                               reply.setKnown(true);
+                       }
                } catch (InvalidPostReplyFound iprf) {
                        logger.log(Level.WARNING, "Invalid reply found, aborting load!");
                        return;
                }
+       }
+
+       private void loadSone(LocalSone sone) {
+               long soneTime = configurationLoader.getLocalSoneTime(sone.getId());
+               if (soneTime == -1) {
+                       return;
+               }
+
+               /* load profile. */
+               ConfigurationSoneParser configurationSoneParser = new ConfigurationSoneParser(configuration, sone.getId());
+               Profile profile = configurationSoneParser.parseProfile(sone);
 
                /* load post likes. */
                Set<String> likedPostIds = configurationSoneParser.parseLikedPostIds();
@@ -223,7 +242,7 @@ public class MemoryDatabase extends AbstractService implements Database {
                /* load albums. */
                List<Album> topLevelAlbums;
                try {
-                       topLevelAlbums = configurationSoneParser.parseTopLevelAlbums(this);
+                       topLevelAlbums = configurationSoneParser.parseTopLevelAlbums(this, sone);
                } catch (InvalidAlbumFound iaf) {
                        logger.log(Level.WARNING, "Invalid album found, aborting load!");
                        return;
@@ -235,7 +254,7 @@ public class MemoryDatabase extends AbstractService implements Database {
 
                /* load images. */
                try {
-                       configurationSoneParser.parseImages(this);
+                       configurationSoneParser.parseImages(this, sone);
                } catch (InvalidImageFound iif) {
                        logger.log(WARNING, "Invalid image found, aborting load!");
                        return;
@@ -267,7 +286,7 @@ public class MemoryDatabase extends AbstractService implements Database {
                /* if we’re still here, Sone was loaded successfully. */
                lock.writeLock().lock();
                try {
-                       sone.setTime(soneTime);
+                       updateSoneTime(sone, soneTime);
                        sone.setProfile(profile);
                        sone.setLikePostIds(likedPostIds);
                        sone.setLikeReplyIds(likedReplyIds);
@@ -276,19 +295,13 @@ public class MemoryDatabase extends AbstractService implements Database {
                        lastInsertFingerprints.put(sone.getId(), lastInsertFingerprint);
 
                        allSones.put(sone.getId(), sone);
-                       storePosts(sone.getId(), posts);
-                       storePostReplies(sone.getId(), postReplies);
+                       storePosts(sone.getId(), sone.getPosts());
+                       storePostReplies(sone.getId(), sone.getReplies());
                        storeAlbums(sone.getId(), topLevelAlbums);
                        storeImages(sone.getId(), from(topLevelAlbums).transformAndConcat(Album.FLATTENER).transformAndConcat(Album.IMAGES).toList());
                } finally {
                        lock.writeLock().unlock();
                }
-               for (Post post : posts) {
-                       post.setKnown(true);
-               }
-               for (PostReply reply : postReplies) {
-                       reply.setKnown(true);
-               }
 
                logger.info(String.format("Sone loaded successfully: %s", sone));
        }
@@ -326,7 +339,6 @@ public class MemoryDatabase extends AbstractService implements Database {
        public void save() throws DatabaseException {
                lock.writeLock().lock();
                try {
-                       saveKnownPosts();
                        saveKnownPostReplies();
                        for (Sone localSone : from(localSones).transform(soneLoader()).transform(Optionals.<Sone>get())) {
                                saveSone(localSone);
@@ -456,8 +468,10 @@ public class MemoryDatabase extends AbstractService implements Database {
        /** {@inheritDocs} */
        @Override
        protected void doStart() {
+               soneDatabase.start();
+               memoryFriendDatabase.start();
+               postDatabase.start();
                memoryBookmarkDatabase.start();
-               loadKnownPosts();
                loadKnownPostReplies();
                notifyStarted();
        }
@@ -466,6 +480,9 @@ public class MemoryDatabase extends AbstractService implements Database {
        @Override
        protected void doStop() {
                try {
+                       soneDatabase.stop();
+                       memoryFriendDatabase.stop();
+                       postDatabase.stop();
                        memoryBookmarkDatabase.stop();
                        save();
                        notifyStopped();
@@ -495,11 +512,23 @@ public class MemoryDatabase extends AbstractService implements Database {
                }
        }
 
+       @Override
+       public boolean isSoneKnown(Sone sone) {
+               return soneDatabase.isKnownSone(sone.getId());
+       }
+
+       @Override
+       public void setSoneKnown(Sone sone) {
+               soneDatabase.setSoneKnown(sone.getId());
+       }
+
+       @Override
+       public void updateSoneTime(Sone sone, long soneTime) {
+               soneDatabase.updateSoneTime(sone.getId(), soneTime);
+       }
+
        private void storePosts(String soneId, Collection<Post> posts) {
-               sonePosts.putAll(soneId, posts);
-               for (Post post : posts) {
-                       allPosts.put(post.getId(), post);
-               }
+               postDatabase.storePosts(soneId, posts);
        }
 
        private void storePostReplies(String soneId, Collection<PostReply> postReplies) {
@@ -528,10 +557,7 @@ public class MemoryDatabase extends AbstractService implements Database {
                lock.writeLock().lock();
                try {
                        allSones.remove(sone.getId());
-                       Collection<Post> removedPosts = sonePosts.removeAll(sone.getId());
-                       for (Post removedPost : removedPosts) {
-                               allPosts.remove(removedPost.getId());
-                       }
+                       postDatabase.removePostsFor(sone.getId());
                        Collection<PostReply> removedPostReplies =
                                        sonePostReplies.removeAll(sone.getId());
                        for (PostReply removedPostReply : removedPostReplies) {
@@ -583,10 +609,16 @@ public class MemoryDatabase extends AbstractService implements Database {
        }
 
        @Override
-       public Collection<Sone> getLocalSones() {
+       public Collection<LocalSone> getLocalSones() {
                lock.readLock().lock();
                try {
-                       return from(allSones.values()).filter(LOCAL_SONE_FILTER).toSet();
+                       return from(allSones.values()).filter(LOCAL_SONE_FILTER).transform(new Function<Sone, LocalSone>() {
+                               @Override
+                               public LocalSone apply(Sone sone) {
+                                       // FIXME – Sones will not always implement LocalSone
+                                       return (LocalSone) sone;
+                               }
+                       }).toSet();
                } finally {
                        lock.readLock().unlock();
                }
@@ -604,7 +636,7 @@ public class MemoryDatabase extends AbstractService implements Database {
        }
 
        @Override
-       public Collection<String> getFriends(Sone localSone) {
+       public Collection<String> getFriends(LocalSone localSone) {
                if (!localSone.isLocal()) {
                        return Collections.emptySet();
                }
@@ -612,7 +644,12 @@ public class MemoryDatabase extends AbstractService implements Database {
        }
 
        @Override
-       public boolean isFriend(Sone localSone, String friendSoneId) {
+       public Optional<Long> getSoneFollowingTime(String remoteSoneId) {
+               return memoryFriendDatabase.getSoneFollowingTime(remoteSoneId);
+       }
+
+       @Override
+       public boolean isFriend(LocalSone localSone, String friendSoneId) {
                if (!localSone.isLocal()) {
                        return false;
                }
@@ -620,18 +657,12 @@ public class MemoryDatabase extends AbstractService implements Database {
        }
 
        @Override
-       public void addFriend(Sone localSone, String friendSoneId) {
-               if (!localSone.isLocal()) {
-                       return;
-               }
+       public void addFriend(LocalSone localSone, String friendSoneId) {
                memoryFriendDatabase.addFriend(localSone.getId(), friendSoneId);
        }
 
        @Override
-       public void removeFriend(Sone localSone, String friendSoneId) {
-               if (!localSone.isLocal()) {
-                       return;
-               }
+       public void removeFriend(LocalSone localSone, String friendSoneId) {
                memoryFriendDatabase.removeFriend(localSone.getId(), friendSoneId);
        }
 
@@ -642,12 +673,7 @@ public class MemoryDatabase extends AbstractService implements Database {
        /** {@inheritDocs} */
        @Override
        public Optional<Post> getPost(String postId) {
-               lock.readLock().lock();
-               try {
-                       return fromNullable(allPosts.get(postId));
-               } finally {
-                       lock.readLock().unlock();
-               }
+               return postDatabase.getPost(postId);
        }
 
        /** {@inheritDocs} */
@@ -659,17 +685,7 @@ public class MemoryDatabase extends AbstractService implements Database {
        /** {@inheritDocs} */
        @Override
        public Collection<Post> getDirectedPosts(final String recipientId) {
-               lock.readLock().lock();
-               try {
-                       return from(sonePosts.values()).filter(new Predicate<Post>() {
-                               @Override
-                               public boolean apply(Post post) {
-                                       return post.getRecipientId().asSet().contains(recipientId);
-                               }
-                       }).toSet();
-               } finally {
-                       lock.readLock().unlock();
-               }
+               return postDatabase.getDirectedPosts(recipientId);
        }
 
        //
@@ -690,27 +706,14 @@ public class MemoryDatabase extends AbstractService implements Database {
        @Override
        public void storePost(Post post) {
                checkNotNull(post, "post must not be null");
-               lock.writeLock().lock();
-               try {
-                       allPosts.put(post.getId(), post);
-                       getPostsFrom(post.getSone().getId()).add(post);
-               } finally {
-                       lock.writeLock().unlock();
-               }
+               postDatabase.storePost(post);
        }
 
        /** {@inheritDocs} */
        @Override
        public void removePost(Post post) {
                checkNotNull(post, "post must not be null");
-               lock.writeLock().lock();
-               try {
-                       allPosts.remove(post.getId());
-                       getPostsFrom(post.getSone().getId()).remove(post);
-                       post.getSone().removePost(post);
-               } finally {
-                       lock.writeLock().unlock();
-               }
+               postDatabase.removePost(post.getId());
        }
 
        //
@@ -911,12 +914,7 @@ public class MemoryDatabase extends AbstractService implements Database {
         * @return {@code true} if the post is known, {@code false} otherwise
         */
        boolean isPostKnown(Post post) {
-               lock.readLock().lock();
-               try {
-                       return knownPosts.contains(post.getId());
-               } finally {
-                       lock.readLock().unlock();
-               }
+               return postDatabase.isPostKnown(post.getId());
        }
 
        /**
@@ -928,16 +926,7 @@ public class MemoryDatabase extends AbstractService implements Database {
         *              {@code true} if the post is known, {@code false} otherwise
         */
        void setPostKnown(Post post, boolean known) {
-               lock.writeLock().lock();
-               try {
-                       if (known) {
-                               knownPosts.add(post.getId());
-                       } else {
-                               knownPosts.remove(post.getId());
-                       }
-               } finally {
-                       lock.writeLock().unlock();
-               }
+               postDatabase.setPostKnown(post.getId(), known);
        }
 
        /**
@@ -991,46 +980,7 @@ public class MemoryDatabase extends AbstractService implements Database {
         * @return All posts
         */
        private Collection<Post> getPostsFrom(String soneId) {
-               lock.readLock().lock();
-               try {
-                       return sonePosts.get(soneId);
-               } finally {
-                       lock.readLock().unlock();
-               }
-       }
-
-       /** Loads the known posts. */
-       private void loadKnownPosts() {
-               Set<String> knownPosts = configurationLoader.loadKnownPosts();
-               lock.writeLock().lock();
-               try {
-                       this.knownPosts.clear();
-                       this.knownPosts.addAll(knownPosts);
-               } finally {
-                       lock.writeLock().unlock();
-               }
-       }
-
-       /**
-        * Saves the known posts to the configuration.
-        *
-        * @throws DatabaseException
-        *              if a configuration error occurs
-        */
-       private void saveKnownPosts() throws DatabaseException {
-               lock.readLock().lock();
-               try {
-                       int postCounter = 0;
-                       for (String knownPostId : knownPosts) {
-                               configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").setValue(
-                                               knownPostId);
-                       }
-                       configuration.getStringValue("KnownPosts/" + postCounter + "/ID").setValue(null);
-               } catch (ConfigurationException ce1) {
-                       throw new DatabaseException("Could not save database.", ce1);
-               } finally {
-                       lock.readLock().unlock();
-               }
+               return postDatabase.getPostsFrom(soneId);
        }
 
        /** Loads the known post replies. */