X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdatabase%2Fmemory%2FMemoryDatabase.java;h=bace9d9fbe592535f8d6aecc6ba991917fc75a4c;hb=9d9b901b7173a9b8221a8b830ece31bc5ee36578;hp=34c819dd9cb722003d8bd049c22975099e2ff96f;hpb=ed19f10e7931fb0b09c042c6070e37dffb23ceac;p=Sone.git diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java index 34c819d..bace9d9 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java @@ -19,6 +19,11 @@ package net.pterodactylus.sone.database.memory; import static com.google.common.base.Optional.fromNullable; 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 java.util.Collections.emptyList; +import static java.util.logging.Logger.getLogger; +import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER; import java.util.ArrayList; import java.util.Collection; @@ -29,64 +34,58 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; +import java.util.logging.Logger; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; -import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.impl.DefaultSoneBuilder; import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.database.DatabaseException; -import net.pterodactylus.sone.database.PostBuilder; -import net.pterodactylus.sone.database.PostDatabase; -import net.pterodactylus.sone.database.PostReplyBuilder; -import net.pterodactylus.sone.database.SoneProvider; +import net.pterodactylus.sone.database.SoneBuilder; +import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; +import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; import com.google.common.collect.ListMultimap; +import com.google.common.collect.SetMultimap; import com.google.common.collect.SortedSetMultimap; import com.google.common.collect.TreeMultimap; import com.google.common.util.concurrent.AbstractService; import com.google.inject.Inject; /** - * Memory-based {@link PostDatabase} implementation. + * Memory-based {@link Database} implementation. * * @author David ‘Bombe’ Roden */ public class MemoryDatabase extends AbstractService implements Database { + private static final Logger logger = getLogger(MemoryDatabase.class.getName()); + /** The lock. */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); - /** The Sone provider. */ - private final SoneProvider soneProvider; - /** The configuration. */ private final Configuration configuration; - /** All posts by their ID. */ - private final Map allPosts = new HashMap(); - - /** All posts by their Sones. */ - private final Map> sonePosts = new HashMap>(); - - /** All posts by their recipient. */ - private final Map> recipientPosts = new HashMap>(); - - /** Whether posts are known. */ - private final Set knownPosts = new HashSet(); + private final Map sones = new HashMap(); + private final MemoryIdentityDatabase memoryIdentityDatabase; + private final MemoryPostDatabase memoryPostDatabase; /** All post replies by their ID. */ private final Map allPostReplies = new HashMap(); + private final SetMultimap likedPostRepliesBySone = HashMultimap.create(); + private final SetMultimap postReplyLikingSones = HashMultimap.create(); /** Replies sorted by Sone. */ private final SortedSetMultimap sonePostReplies = TreeMultimap.create(new Comparator() { @@ -98,7 +97,13 @@ public class MemoryDatabase extends AbstractService implements Database { }, PostReply.TIME_COMPARATOR); /** Replies by post. */ - private final Map> postReplies = new HashMap>(); + private final SortedSetMultimap postReplies = TreeMultimap.create(new Comparator() { + + @Override + public int compare(String leftString, String rightString) { + return leftString.compareTo(rightString); + } + }, PostReply.TIME_COMPARATOR); /** Whether post replies are known. */ private final Set knownPostReplies = new HashSet(); @@ -112,195 +117,220 @@ public class MemoryDatabase extends AbstractService implements Database { /** * Creates a new memory database. * - * @param soneProvider - * The Sone provider * @param configuration * The configuration for loading and saving elements */ @Inject - public MemoryDatabase(SoneProvider soneProvider, Configuration configuration) { - this.soneProvider = soneProvider; + public MemoryDatabase(Configuration configuration) { this.configuration = configuration; - } - - // - // DATABASE METHODS - // - - /** - * Saves the database. - * - * @throws DatabaseException - * if an error occurs while saving - */ - @Override - public void save() throws DatabaseException { - saveKnownPosts(); - saveKnownPostReplies(); + memoryPostDatabase = new MemoryPostDatabase(this, lock, configuration); + memoryIdentityDatabase = new MemoryIdentityDatabase(lock); } // // SERVICE METHODS // - /** {@inheritDocs} */ @Override protected void doStart() { - loadKnownPosts(); + memoryPostDatabase.start(); loadKnownPostReplies(); notifyStarted(); } - /** {@inheritDocs} */ @Override protected void doStop() { try { - save(); - notifyStopped(); + memoryPostDatabase.stop(); } catch (DatabaseException de1) { - notifyFailed(de1); + logger.log(Level.WARNING, "Could not stop post database!", de1); } + notifyStopped(); } - // - // POSTPROVIDER METHODS - // + @Override + public Optional getIdentity(String identityId) { + return memoryIdentityDatabase.getIdentity(identityId); + } - /** {@inheritDocs} */ @Override - public Optional getPost(String postId) { + public void storeIdentity(Identity identitiy) { + memoryIdentityDatabase.storeIdentity(identitiy); + } + + @Override + public Function> getSone() { + return new Function>() { + @Override + public Optional apply(String soneId) { + return (soneId == null) ? Optional.absent() : getSone(soneId); + } + }; + } + + @Override + public Optional getSone(String soneId) { lock.readLock().lock(); try { - return fromNullable(allPosts.get(postId)); + return fromNullable(sones.get(soneId)); } finally { lock.readLock().unlock(); } } - /** {@inheritDocs} */ @Override - public Collection getPosts(String soneId) { - return new HashSet(getPostsFrom(soneId)); + public Collection getSones() { + lock.readLock().lock(); + try { + return Collections.unmodifiableCollection(sones.values()); + } finally { + lock.readLock().unlock(); + } } - /** {@inheritDocs} */ @Override - public Collection getDirectedPosts(String recipientId) { + public Collection getLocalSones() { + lock.readLock().lock(); + try { + return from(getSones()).filter(LOCAL_SONE_FILTER).toSet(); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getRemoteSones() { lock.readLock().lock(); try { - Collection posts = recipientPosts.get(recipientId); - return (posts == null) ? Collections.emptySet() : new HashSet(posts); + return from(getSones()).filter(not(LOCAL_SONE_FILTER)).toSet(); } finally { lock.readLock().unlock(); } } + @Override + public void storeSone(Sone sone) { + lock.writeLock().lock(); + try { + sones.put(sone.getId(), sone); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public SoneBuilder newSoneBuilder() { + return new DefaultSoneBuilder(this) { + @Override + public Sone build(Optional soneCreated) throws IllegalStateException { + Sone sone = super.build(soneCreated); + lock.writeLock().lock(); + try { + sones.put(sone.getId(), sone); + } finally { + lock.writeLock().unlock(); + } + return sone; + } + }; + } + // - // POSTBUILDERFACTORY METHODS + // POSTPROVIDER METHODS // - /** {@inheritDocs} */ @Override - public PostBuilder newPostBuilder() { - return new MemoryPostBuilder(this); + public Function> getPost() { + return memoryPostDatabase.getPost(); + } + + @Override + public Optional getPost(String postId) { + return memoryPostDatabase.getPost(postId); + } + + @Override + public Collection getPosts(String soneId) { + return memoryPostDatabase.getPosts(soneId); + } + + @Override + public Collection getDirectedPosts(String recipientId) { + return memoryPostDatabase.getDirectedPosts(recipientId); + } + + /** + * Returns whether the given post is known. + * + * @param post + * The post + * @return {@code true} if the post is known, {@code false} otherwise + */ + @Override + public boolean isPostKnown(Post post) { + return memoryPostDatabase.isPostKnown(post); + } + + /** + * Sets whether the given post is known. + * + * @param post + * The post + */ + @Override + public void setPostKnown(Post post) { + memoryPostDatabase.setPostKnown(post); + } + + @Override + public void likePost(Post post, Sone localSone) { + memoryPostDatabase.likePost(post, localSone); + } + + @Override + public void unlikePost(Post post, Sone localSone) { + memoryPostDatabase.unlikePost(post, localSone); + } + + public boolean isLiked(Post post, Sone sone) { + return memoryPostDatabase.isLiked(post, sone); + } + + @Override + public Set getLikes(Post post) { + return memoryPostDatabase.getLikes(post); } // // POSTSTORE METHODS // - /** {@inheritDocs} */ @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); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).add(post); - } - } finally { - lock.writeLock().unlock(); - } + memoryPostDatabase.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); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).remove(post); - } - post.getSone().removePost(post); - } finally { - lock.writeLock().unlock(); - } + memoryPostDatabase.removePost(post); } - /** {@inheritDocs} */ @Override public void storePosts(Sone sone, Collection posts) throws IllegalArgumentException { - checkNotNull(sone, "sone must not be null"); /* verify that all posts are from the same Sone. */ - for (Post post : posts) { - if (!sone.equals(post.getSone())) { - throw new IllegalArgumentException(String.format("Post from different Sone found: %s", post)); - } - } - lock.writeLock().lock(); - try { - /* remove all posts by the Sone. */ - getPostsFrom(sone.getId()).clear(); - for (Post post : posts) { - allPosts.remove(post.getId()); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).remove(post); - } - } - - /* add new posts. */ - getPostsFrom(sone.getId()).addAll(posts); - for (Post post : posts) { - allPosts.put(post.getId(), post); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).add(post); - } - } - } finally { - lock.writeLock().unlock(); - } + memoryPostDatabase.storePosts(sone, posts); } - /** {@inheritDocs} */ @Override public void removePosts(Sone sone) { - checkNotNull(sone, "sone must not be null"); - lock.writeLock().lock(); - try { - /* remove all posts by the Sone. */ - getPostsFrom(sone.getId()).clear(); - for (Post post : sone.getPosts()) { - allPosts.remove(post.getId()); - if (post.getRecipientId().isPresent()) { - getPostsTo(post.getRecipientId().get()).remove(post); - } - } - } finally { - lock.writeLock().unlock(); - } + memoryPostDatabase.removePosts(sone); } // // POSTREPLYPROVIDER METHODS // - /** {@inheritDocs} */ @Override public Optional getPostReply(String id) { lock.readLock().lock(); @@ -311,13 +341,12 @@ public class MemoryDatabase extends AbstractService implements Database { } } - /** {@inheritDocs} */ @Override public List getReplies(String postId) { lock.readLock().lock(); try { if (!postReplies.containsKey(postId)) { - return Collections.emptyList(); + return emptyList(); } return new ArrayList(postReplies.get(postId)); } finally { @@ -325,39 +354,90 @@ public class MemoryDatabase extends AbstractService implements Database { } } - // - // POSTREPLYBUILDERFACTORY METHODS - // + @Override + public void likePostReply(PostReply postReply, Sone localSone) { + lock.writeLock().lock(); + try { + likedPostRepliesBySone.put(localSone.getId(), postReply.getId()); + postReplyLikingSones.put(postReply.getId(), localSone.getId()); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void unlikePostReply(PostReply postReply, Sone localSone) { + lock.writeLock().lock(); + try { + likedPostRepliesBySone.remove(localSone.getId(), postReply.getId()); + postReplyLikingSones.remove(postReply.getId(), localSone.getId()); + } finally { + lock.writeLock().unlock(); + } + } - /** {@inheritDocs} */ @Override - public PostReplyBuilder newPostReplyBuilder() { - return new MemoryPostReplyBuilder(this, soneProvider); + public boolean isLiked(PostReply postReply, Sone sone) { + lock.readLock().lock(); + try { + return postReplyLikingSones.containsEntry(postReply.getId(), sone.getId()); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Set getLikes(PostReply postReply) { + lock.readLock().lock(); + try { + return from(postReplyLikingSones.get(postReply.getId())).transform(getSone()).transformAndConcat(this.unwrap()).toSet(); + } finally { + lock.readLock().unlock(); + } } // // POSTREPLYSTORE METHODS // - /** {@inheritDocs} */ + /** + * Returns whether the given post reply is known. + * + * @param postReply + * The post reply + * @return {@code true} if the given post reply is known, {@code false} + * otherwise + */ + public boolean isPostReplyKnown(PostReply postReply) { + lock.readLock().lock(); + try { + return knownPostReplies.contains(postReply.getId()); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public void setPostReplyKnown(PostReply postReply) { + lock.writeLock().lock(); + try { + knownPostReplies.add(postReply.getId()); + } finally { + lock.writeLock().unlock(); + } + } + @Override public void storePostReply(PostReply postReply) { lock.writeLock().lock(); try { allPostReplies.put(postReply.getId(), postReply); - if (postReplies.containsKey(postReply.getPostId())) { - postReplies.get(postReply.getPostId()).add(postReply); - } else { - TreeSet replies = new TreeSet(Reply.TIME_COMPARATOR); - replies.add(postReply); - postReplies.put(postReply.getPostId(), replies); - } + postReplies.put(postReply.getPostId(), postReply); } finally { lock.writeLock().unlock(); } } - /** {@inheritDocs} */ @Override public void storePostReplies(Sone sone, Collection postReplies) { checkNotNull(sone, "sone must not be null"); @@ -377,37 +457,24 @@ public class MemoryDatabase extends AbstractService implements Database { for (PostReply postReply : postReplies) { allPostReplies.put(postReply.getId(), postReply); sonePostReplies.put(postReply.getSone().getId(), postReply); - if (this.postReplies.containsKey(postReply.getPostId())) { - this.postReplies.get(postReply.getPostId()).add(postReply); - } else { - TreeSet replies = new TreeSet(Reply.TIME_COMPARATOR); - replies.add(postReply); - this.postReplies.put(postReply.getPostId(), replies); - } + this.postReplies.put(postReply.getPostId(), postReply); } } finally { lock.writeLock().unlock(); } } - /** {@inheritDocs} */ @Override public void removePostReply(PostReply postReply) { lock.writeLock().lock(); try { allPostReplies.remove(postReply.getId()); - if (postReplies.containsKey(postReply.getPostId())) { - postReplies.get(postReply.getPostId()).remove(postReply); - if (postReplies.get(postReply.getPostId()).isEmpty()) { - postReplies.remove(postReply.getPostId()); - } - } + postReplies.remove(postReply.getPostId(), postReply); } finally { lock.writeLock().unlock(); } } - /** {@inheritDocs} */ @Override public void removePostReplies(Sone sone) { checkNotNull(sone, "sone must not be null"); @@ -436,182 +503,102 @@ public class MemoryDatabase extends AbstractService implements Database { } } - // - // ALBUMSTORE METHODS - // - @Override - public void storeAlbum(Album album) { - lock.writeLock().lock(); + public List getAlbums(Album parent) { + lock.readLock().lock(); try { - allAlbums.put(album.getId(), album); - albumChildren.put(album.getParent().getId(), album.getId()); + return from(albumChildren.get(parent.getId())).transformAndConcat(getAlbum()).toList(); } finally { - lock.writeLock().unlock(); + lock.readLock().unlock(); } } @Override - public void removeAlbum(Album album) { + public void moveUp(Album album) { lock.writeLock().lock(); try { - allAlbums.remove(album.getId()); - albumChildren.remove(album.getParent().getId(), album.getId()); + List albums = albumChildren.get(album.getParent().getId()); + int currentIndex = albums.indexOf(album.getId()); + if (currentIndex == 0) { + return; + } + albums.remove(album.getId()); + albums.add(currentIndex - 1, album.getId()); } finally { lock.writeLock().unlock(); } } - // - // IMAGEPROVIDER METHODS - // - @Override - public Optional getImage(String imageId) { - lock.readLock().lock(); + public void moveDown(Album album) { + lock.writeLock().lock(); try { - return fromNullable(allImages.get(imageId)); + List albums = albumChildren.get(album.getParent().getId()); + int currentIndex = albums.indexOf(album.getId()); + if (currentIndex == (albums.size() - 1)) { + return; + } + albums.remove(album.getId()); + albums.add(currentIndex + 1, album.getId()); } finally { - lock.readLock().unlock(); + lock.writeLock().unlock(); } } // - // IMAGESTORE METHODS + // ALBUMSTORE METHODS // @Override - public void storeImage(Image image) { + public void storeAlbum(Album album) { lock.writeLock().lock(); try { - allImages.put(image.getId(), image); - albumImages.put(image.getAlbum().getId(), image.getId()); + allAlbums.put(album.getId(), album); + if (!album.isRoot()) { + albumChildren.put(album.getParent().getId(), album.getId()); + } } finally { lock.writeLock().unlock(); } } @Override - public void removeImage(Image image) { + public void removeAlbum(Album album) { lock.writeLock().lock(); try { - allImages.remove(image.getId()); - albumImages.remove(image.getAlbum().getId(), image.getId()); + allAlbums.remove(album.getId()); + albumChildren.remove(album.getParent().getId(), album.getId()); } finally { lock.writeLock().unlock(); } } // - // PACKAGE-PRIVATE METHODS + // IMAGEPROVIDER METHODS // - /** - * Returns whether the given post is known. - * - * @param post - * The post - * @return {@code true} if the post is known, {@code false} otherwise - */ - boolean isPostKnown(Post post) { + @Override + public Optional getImage(String imageId) { lock.readLock().lock(); try { - return knownPosts.contains(post.getId()); + return fromNullable(allImages.get(imageId)); } finally { lock.readLock().unlock(); } } - /** - * Sets whether the given post is known. - * - * @param post - * The post - * @param known - * {@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(); - } - } - - /** - * Returns whether the given post reply is known. - * - * @param postReply - * The post reply - * @return {@code true} if the given post reply is known, {@code false} - * otherwise - */ - boolean isPostReplyKnown(PostReply postReply) { + @Override + public List getImages(Album parent) { lock.readLock().lock(); try { - return knownPostReplies.contains(postReply.getId()); + return from(albumImages.get(parent.getId())).transformAndConcat(getImage()).toList(); } finally { lock.readLock().unlock(); } } - /** - * Sets whether the given post reply is known. - * - * @param postReply - * The post reply - * @param known - * {@code true} if the post reply is known, {@code false} otherwise - */ - void setPostReplyKnown(PostReply postReply, boolean known) { - lock.writeLock().lock(); - try { - if (known) { - knownPostReplies.add(postReply.getId()); - } else { - knownPostReplies.remove(postReply.getId()); - } - } finally { - lock.writeLock().unlock(); - } - } - - void moveUp(Album album) { - lock.writeLock().lock(); - try { - List albums = albumChildren.get(album.getParent().getId()); - int currentIndex = albums.indexOf(album.getId()); - if (currentIndex == 0) { - return; - } - albums.remove(album.getId()); - albums.add(currentIndex - 1, album.getId()); - } finally { - lock.writeLock().unlock(); - } - } - - void moveDown(Album album) { - lock.writeLock().lock(); - try { - List albums = albumChildren.get(album.getParent().getId()); - int currentIndex = albums.indexOf(album.getId()); - if (currentIndex == (albums.size() - 1)) { - return; - } - albums.remove(album.getId()); - albums.add(currentIndex + 1, album.getId()); - } finally { - lock.writeLock().unlock(); - } - } - - void moveUp(Image image) { + @Override + public void moveUp(Image image) { lock.writeLock().lock(); try { List images = albumImages.get(image.getAlbum().getId()); @@ -626,7 +613,8 @@ public class MemoryDatabase extends AbstractService implements Database { } } - void moveDown(Image image) { + @Override + public void moveDown(Image image) { lock.writeLock().lock(); try { List images = albumChildren.get(image.getAlbum().getId()); @@ -642,108 +630,34 @@ public class MemoryDatabase extends AbstractService implements Database { } // - // PRIVATE METHODS + // IMAGESTORE METHODS // - /** - * Gets all posts for the given Sone, creating a new collection if there is - * none yet. - * - * @param soneId - * The ID of the Sone to get the posts for - * @return All posts - */ - private Collection getPostsFrom(String soneId) { - Collection posts = null; - lock.readLock().lock(); - try { - posts = sonePosts.get(soneId); - } finally { - lock.readLock().unlock(); - } - if (posts != null) { - return posts; - } - - posts = new HashSet(); - lock.writeLock().lock(); - try { - sonePosts.put(soneId, posts); - } finally { - lock.writeLock().unlock(); - } - - return posts; - } - - /** - * Gets all posts that are directed the given Sone, creating a new collection - * if there is none yet. - * - * @param recipientId - * The ID of the Sone to get the posts for - * @return All posts - */ - private Collection getPostsTo(String recipientId) { - Collection posts = null; - lock.readLock().lock(); - try { - posts = recipientPosts.get(recipientId); - } finally { - lock.readLock().unlock(); - } - if (posts != null) { - return posts; - } - - posts = new HashSet(); + @Override + public void storeImage(Image image) { lock.writeLock().lock(); try { - recipientPosts.put(recipientId, posts); + allImages.put(image.getId(), image); + albumImages.put(image.getAlbum().getId(), image.getId()); } finally { lock.writeLock().unlock(); } - - return posts; } - /** Loads the known posts. */ - private void loadKnownPosts() { + @Override + public void removeImage(Image image) { lock.writeLock().lock(); try { - int postCounter = 0; - while (true) { - String knownPostId = configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").getValue(null); - if (knownPostId == null) { - break; - } - knownPosts.add(knownPostId); - } + allImages.remove(image.getId()); + albumImages.remove(image.getAlbum().getId(), image.getId()); } 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(); - } - } + // + // PRIVATE METHODS + // /** * Returns all replies by the given Sone. @@ -802,4 +716,31 @@ public class MemoryDatabase extends AbstractService implements Database { } } + private Function> getAlbum() { + return new Function>() { + @Override + public Iterable apply(String input) { + return (input == null) ? Collections.emptyList() : getAlbum(input).asSet(); + } + }; + } + + private Function> getImage() { + return new Function>() { + @Override + public Iterable apply(String input) { + return (input == null) ? Collections.emptyList() : getImage(input).asSet(); + } + }; + } + + static Function, Iterable> unwrap() { + return new Function, Iterable>() { + @Override + public Iterable apply(Optional input) { + return (input == null) ? Collections.emptyList() : input.asSet(); + } + }; + } + }