Make it possible to load an optional Sone from the parameters.
[Sone.git] / src / main / java / net / pterodactylus / sone / fcp / AbstractSoneCommand.java
1 /*
2  * Sone - FcpInterface.java - Copyright © 2011 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.fcp;
19
20 import java.util.Collection;
21 import java.util.List;
22
23 import net.pterodactylus.sone.core.Core;
24 import net.pterodactylus.sone.data.Post;
25 import net.pterodactylus.sone.data.Reply;
26 import net.pterodactylus.sone.data.Sone;
27 import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
28 import net.pterodactylus.sone.freenet.fcp.AbstractCommand;
29 import net.pterodactylus.sone.freenet.fcp.Command;
30 import net.pterodactylus.sone.freenet.fcp.FcpException;
31 import net.pterodactylus.sone.template.SoneAccessor;
32 import net.pterodactylus.util.filter.Filters;
33 import freenet.node.FSParseException;
34 import freenet.support.SimpleFieldSet;
35
36 /**
37  * Abstract base implementation of a {@link Command} with Sone-related helper
38  * methods.
39  *
40  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
41  */
42 public abstract class AbstractSoneCommand extends AbstractCommand {
43
44         /** The Sone core. */
45         private final Core core;
46
47         /** Whether this command needs write access. */
48         private final boolean writeAccess;
49
50         /**
51          * Creates a new abstract Sone FCP command.
52          *
53          * @param core
54          *            The Sone core
55          */
56         protected AbstractSoneCommand(Core core) {
57                 this(core, false);
58         }
59
60         /**
61          * Creates a new abstract Sone FCP command.
62          *
63          * @param core
64          *            The Sone core
65          * @param writeAccess
66          *            {@code true} if this command requires write access,
67          *            {@code false} otherwise
68          */
69         protected AbstractSoneCommand(Core core, boolean writeAccess) {
70                 this.core = core;
71                 this.writeAccess = writeAccess;
72         }
73
74         //
75         // ACCESSORS
76         //
77
78         /**
79          * Returns the Sone core.
80          *
81          * @return The Sone core
82          */
83         protected Core getCore() {
84                 return core;
85         }
86
87         /**
88          * Returns whether this command requires write access.
89          *
90          * @return {@code true} if this command require write access, {@code false}
91          *         otherwise
92          */
93         public boolean requiresWriteAccess() {
94                 return writeAccess;
95         }
96
97         //
98         // PROTECTED METHODS
99         //
100
101         /**
102          * Encodes text in a way that makes it possible for the text to be stored in
103          * a {@link SimpleFieldSet}. Backslashes, CR, and LF are prepended with a
104          * backslash.
105          *
106          * @param text
107          *            The text to encode
108          * @return The encoded text
109          */
110         protected String encodeString(String text) {
111                 return text.replaceAll("\\\\", "\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r");
112         }
113
114         /**
115          * Returns a Sone whose ID is a parameter in the given simple field set.
116          *
117          * @param simpleFieldSet
118          *            The simple field set containing the ID of the Sone
119          * @param parameterName
120          *            The name under which the Sone ID is stored in the simple field
121          *            set
122          * @param localOnly
123          *            {@code true} to only return local Sones, {@code false} to
124          *            return any Sones
125          * @return The Sone
126          * @throws FcpException
127          *             if there is no Sone ID stored under the given parameter name,
128          *             or if the Sone ID is invalid
129          */
130         protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly) throws FcpException {
131                 return getSone(simpleFieldSet, parameterName, localOnly, true);
132         }
133
134         /**
135          * Returns a Sone whose ID is a parameter in the given simple field set.
136          *
137          * @param simpleFieldSet
138          *            The simple field set containing the ID of the Sone
139          * @param parameterName
140          *            The name under which the Sone ID is stored in the simple field
141          *            set
142          * @param localOnly
143          *            {@code true} to only return local Sones, {@code false} to
144          *            return any Sones
145          * @param mandatory
146          *            {@code true} if a valid Sone ID is required, {@code false}
147          *            otherwise
148          * @return The Sone, or {@code null} if {@code mandatory} is {@code false}
149          *         and the Sone ID is invalid
150          * @throws FcpException
151          *             if there is no Sone ID stored under the given parameter name,
152          *             or if {@code mandatory} is {@code true} and the Sone ID is
153          *             invalid
154          */
155         protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly, boolean mandatory) throws FcpException {
156                 String soneId = simpleFieldSet.get(parameterName);
157                 if (mandatory && (soneId == null)) {
158                         throw new FcpException("Could not load Sone ID from “" + parameterName + "”.");
159                 }
160                 Sone sone = localOnly ? core.getLocalSone(soneId, false) : core.getSone(soneId, false);
161                 if (mandatory && (sone == null)) {
162                         throw new FcpException("Could not load Sone from “" + soneId + "”.");
163                 }
164                 return sone;
165         }
166
167         /**
168          * Returns a post whose ID is a parameter in the given simple field set.
169          *
170          * @param simpleFieldSet
171          *            The simple field set containing the ID of the post
172          * @param parameterName
173          *            The name under which the post ID is stored in the simple field
174          *            set
175          * @return The post
176          * @throws FcpException
177          *             if there is no post ID stored under the given parameter name,
178          *             or if the post ID is invalid
179          */
180         protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
181                 try {
182                         String postId = simpleFieldSet.getString(parameterName);
183                         Post post = core.getPost(postId, false);
184                         if (post == null) {
185                                 throw new FcpException("Could not load post from “" + postId + "”.");
186                         }
187                         return post;
188                 } catch (FSParseException fspe1) {
189                         throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1);
190                 }
191         }
192
193         /**
194          * Returns a reply whose ID is a parameter in the given simple field set.
195          *
196          * @param simpleFieldSet
197          *            The simple field set containing the ID of the reply
198          * @param parameterName
199          *            The name under which the reply ID is stored in the simple
200          *            field set
201          * @return The reply
202          * @throws FcpException
203          *             if there is no reply ID stored under the given parameter
204          *             name, or if the reply ID is invalid
205          */
206         protected Reply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
207                 try {
208                         String replyId = simpleFieldSet.getString(parameterName);
209                         Reply reply = core.getReply(replyId, false);
210                         if (reply == null) {
211                                 throw new FcpException("Could not load reply from “" + replyId + "”.");
212                         }
213                         return reply;
214                 } catch (FSParseException fspe1) {
215                         throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1);
216                 }
217         }
218
219         /**
220          * Creates a simple field set from the given collection of Sones.
221          *
222          * @param sones
223          *            The Sones to encode
224          * @param prefix
225          *            The prefix for the field names (may be empty but not
226          *            {@code null})
227          * @return The simple field set containing the given Sones
228          */
229         protected SimpleFieldSet encodeSones(Collection<? extends Sone> sones, String prefix) {
230                 SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
231
232                 int soneIndex = 0;
233                 soneBuilder.put(prefix + "Count", sones.size());
234                 for (Sone sone : sones) {
235                         String sonePrefix = prefix + soneIndex++ + ".";
236                         soneBuilder.put(sonePrefix + "ID", sone.getId());
237                         soneBuilder.put(sonePrefix + "Name", sone.getName());
238                         soneBuilder.put(sonePrefix + "NiceName", SoneAccessor.getNiceName(sone));
239                         soneBuilder.put(sonePrefix + "Time", sone.getTime());
240                 }
241
242                 return soneBuilder.get();
243         }
244
245         /**
246          * Creates a simple field set from the given post.
247          *
248          * @param post
249          *            The post to encode
250          * @param prefix
251          *            The prefix for the field names (may be empty but not
252          *            {@code null})
253          * @param includeReplies
254          *            {@code true} to include replies, {@code false} to not include
255          *            replies
256          * @return The simple field set containing the post
257          */
258         protected SimpleFieldSet encodePost(Post post, String prefix, boolean includeReplies) {
259                 SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
260
261                 postBuilder.put(prefix + "ID", post.getId());
262                 postBuilder.put(prefix + "Sone", post.getSone().getId());
263                 if (post.getRecipient() != null) {
264                         postBuilder.put(prefix + "Recipient", post.getRecipient().getId());
265                 }
266                 postBuilder.put(prefix + "Time", post.getTime());
267                 postBuilder.put(prefix + "Text", encodeString(post.getText()));
268                 postBuilder.put(encodeLikes(core.getLikes(post), prefix + "Likes."));
269
270                 if (includeReplies) {
271                         List<Reply> replies = core.getReplies(post);
272                         postBuilder.put(encodeReplies(replies, prefix));
273                 }
274
275                 return postBuilder.get();
276         }
277
278         /**
279          * Creates a simple field set from the given collection of posts.
280          *
281          * @param posts
282          *            The posts to encode
283          * @param prefix
284          *            The prefix for the field names (may be empty but not
285          *            {@code null})
286          * @param includeReplies
287          *            {@code true} to include the replies, {@code false} to not
288          *            include the replies
289          * @return The simple field set containing the posts
290          */
291         protected SimpleFieldSet encodePosts(Collection<? extends Post> posts, String prefix, boolean includeReplies) {
292                 SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
293
294                 int postIndex = 0;
295                 postBuilder.put(prefix + "Count", posts.size());
296                 for (Post post : posts) {
297                         String postPrefix = prefix + postIndex++;
298                         postBuilder.put(encodePost(post, postPrefix + ".", includeReplies));
299                         if (includeReplies) {
300                                 postBuilder.put(encodeReplies(Filters.filteredList(core.getReplies(post), Reply.FUTURE_REPLIES_FILTER), postPrefix + "."));
301                         }
302                 }
303
304                 return postBuilder.get();
305         }
306
307         /**
308          * Creates a simple field set from the given collection of replies.
309          *
310          * @param replies
311          *            The replies to encode
312          * @param prefix
313          *            The prefix for the field names (may be empty, but not
314          *            {@code null})
315          * @return The simple field set containing the replies
316          */
317         protected SimpleFieldSet encodeReplies(Collection<? extends Reply> replies, String prefix) {
318                 SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder();
319
320                 int replyIndex = 0;
321                 replyBuilder.put(prefix + "Replies.Count", replies.size());
322                 for (Reply reply : replies) {
323                         String replyPrefix = prefix + "Replies." + replyIndex++ + ".";
324                         replyBuilder.put(replyPrefix + "ID", reply.getId());
325                         replyBuilder.put(replyPrefix + "Sone", reply.getSone().getId());
326                         replyBuilder.put(replyPrefix + "Time", reply.getTime());
327                         replyBuilder.put(replyPrefix + "Text", encodeString(reply.getText()));
328                 }
329
330                 return replyBuilder.get();
331         }
332
333         /**
334          * Creates a simple field set from the given collection of Sones that like
335          * an element.
336          *
337          * @param likes
338          *            The liking Sones
339          * @param prefix
340          *            The prefix for the field names (may be empty but not
341          *            {@code null})
342          * @return The simple field set containing the likes
343          */
344         protected SimpleFieldSet encodeLikes(Collection<? extends Sone> likes, String prefix) {
345                 SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder();
346
347                 int likeIndex = 0;
348                 likesBuilder.put(prefix + "Count", likes.size());
349                 for (Sone sone : likes) {
350                         String sonePrefix = prefix + likeIndex++ + ".";
351                         likesBuilder.put(sonePrefix + "ID", sone.getId());
352                 }
353
354                 return likesBuilder.get();
355         }
356
357         //
358         // OBJECT METHODS
359         //
360
361         /**
362          * {@inheritDoc}
363          */
364         @Override
365         public String toString() {
366                 return getClass().getName() + "[writeAccess=" + writeAccess + "]";
367         }
368
369 }