Move post-related database functionality into its own class.
[Sone.git] / src / main / java / net / pterodactylus / sone / database / memory / MemoryPostDatabase.java
1 package net.pterodactylus.sone.database.memory;
2
3 import java.util.Collection;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.concurrent.locks.ReadWriteLock;
9 import java.util.concurrent.locks.ReentrantReadWriteLock;
10
11 import net.pterodactylus.sone.data.Post;
12 import net.pterodactylus.sone.database.DatabaseException;
13 import net.pterodactylus.sone.utils.Optionals;
14
15 import com.google.common.base.Function;
16 import com.google.common.base.Optional;
17 import com.google.common.collect.FluentIterable;
18 import com.google.common.collect.HashMultimap;
19 import com.google.common.collect.Multimap;
20
21 /**
22  * Groups {@link Post}-related database functions.
23  *
24  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
25  */
26 class MemoryPostDatabase {
27
28         private final ReadWriteLock lock = new ReentrantReadWriteLock();
29         private final MemoryDatabase database;
30         private final ConfigurationLoader configurationLoader;
31         private final Multimap<String, String> sonePosts = HashMultimap.create();
32         private final Map<String, String> postSones = new HashMap<String, String>();
33         private final Map<String, Long> postTimes = new HashMap<String, Long>();
34         private final Map<String, String> postRecipients = new HashMap<String, String>();
35         private final Multimap<String, String> recipientPosts = HashMultimap.create();
36         private final Map<String, String> postTexts = new HashMap<String, String>();
37         private final Set<String> knownPosts = new HashSet<String>();
38
39         MemoryPostDatabase(MemoryDatabase database, ConfigurationLoader configurationLoader) {
40                 this.database = database;
41                 this.configurationLoader = configurationLoader;
42         }
43
44         void start() {
45                 loadKnownPosts();
46         }
47
48         void stop() {
49                 saveKnownPosts();
50         }
51
52         Optional<Post> getPost(String postId) {
53                 lock.readLock().lock();
54                 try {
55                         if (!postSones.containsKey(postId)) {
56                                 return Optional.absent();
57                         }
58                         String sender = postSones.get(postId);
59                         String recipientId = postRecipients.get(postId);
60                         long time = postTimes.get(postId);
61                         String text = postTexts.get(postId);
62                         return Optional.<Post>of(
63                                         new MemoryPost(database, database, postId, sender, recipientId, time, text));
64                 } finally {
65                         lock.readLock().unlock();
66                 }
67         }
68
69         Collection<Post> getPostsFrom(String soneId) {
70                 lock.readLock().lock();
71                 try {
72                         return FluentIterable.from(sonePosts.get(soneId))
73                                         .transform(postLoader)
74                                         .transform(Optionals.<Post>get())
75                                         .toList();
76                 } finally {
77                         lock.readLock().unlock();
78                 }
79         }
80
81         Collection<Post> getDirectedPosts(String recipientId) {
82                 lock.readLock().lock();
83                 try {
84                         return FluentIterable.from(recipientPosts.get(recipientId))
85                                         .transform(postLoader)
86                                         .transform(Optionals.<Post>get())
87                                         .toList();
88                 } finally {
89                         lock.readLock().unlock();
90                 }
91         }
92
93         void storePosts(String soneId, Collection<Post> posts) {
94                 lock.writeLock().lock();
95                 try {
96                         removePostsFor(soneId);
97                         for (Post post : posts) {
98                                 storePost(post);
99                         }
100                 } finally {
101                         lock.writeLock().unlock();
102                 }
103         }
104
105         void storePost(Post post) {
106                 String soneId = post.getSone().getId();
107                 final String postId = post.getId();
108                 sonePosts.put(soneId, postId);
109                 postSones.put(postId, soneId);
110                 postRecipients.put(postId, post.getRecipientId().orNull());
111                 if (post.getRecipientId().isPresent()) {
112                         recipientPosts.put(post.getRecipientId().get(), postId);
113                 }
114                 postTimes.put(postId, post.getTime());
115                 postTexts.put(postId, post.getText());
116         }
117
118         void removePostsFor(String soneId) {
119                 lock.writeLock().lock();
120                 try {
121                         for (String postId : sonePosts.removeAll(soneId)) {
122                                 removePost(postId);
123                         }
124                 } finally {
125                         lock.writeLock().unlock();
126                 }
127         }
128
129         void removePost(String postId) {
130                 lock.writeLock().lock();
131                 try {
132                         postSones.remove(postId);
133                         String recipient = postRecipients.remove(postId);
134                         if (recipient != null) {
135                                 recipientPosts.remove(recipient, postId);
136                         }
137                         postTimes.remove(postId);
138                         postTexts.remove(postId);
139                 } finally {
140                         lock.writeLock().unlock();
141                 }
142         }
143
144         boolean isPostKnown(String postId) {
145                 lock.readLock().lock();
146                 try {
147                         return knownPosts.contains(postId);
148                 } finally {
149                         lock.readLock().unlock();
150                 }
151         }
152
153         void setPostKnown(String postId, boolean known) {
154                 lock.writeLock().lock();
155                 try {
156                         if (known) {
157                                 knownPosts.add(postId);
158                         } else {
159                                 knownPosts.remove(postId);
160                         }
161                         saveKnownPosts();
162                 } finally {
163                         lock.writeLock().unlock();
164                 }
165         }
166
167         private void loadKnownPosts() {
168                 Set<String> knownPosts = configurationLoader.loadKnownPosts();
169                 lock.writeLock().lock();
170                 try {
171                         this.knownPosts.clear();
172                         this.knownPosts.addAll(knownPosts);
173                 } finally {
174                         lock.writeLock().unlock();
175                 }
176         }
177
178         private void saveKnownPosts() {
179                 lock.readLock().lock();
180                 try {
181                         configurationLoader.saveKnownPosts(knownPosts);
182                 } finally {
183                         lock.readLock().unlock();
184                 }
185         }
186
187         private Function<String, Optional<Post>> postLoader = new Function<String, Optional<Post>>() {
188                 @Override
189                 public Optional<Post> apply(String input) {
190                         return getPost(input);
191                 }
192         };
193
194 }