2 * Sone - AbstractSoneCommand.java - Copyright © 2011–2013 David Roden
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.
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.
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/>.
18 package net.pterodactylus.sone.fcp;
20 import java.util.Collection;
21 import java.util.List;
23 import net.pterodactylus.sone.core.Core;
24 import net.pterodactylus.sone.data.LocalSone;
25 import net.pterodactylus.sone.data.Post;
26 import net.pterodactylus.sone.data.PostReply;
27 import net.pterodactylus.sone.data.Profile;
28 import net.pterodactylus.sone.data.Profile.Field;
29 import net.pterodactylus.sone.data.Reply;
30 import net.pterodactylus.sone.data.Sone;
31 import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
32 import net.pterodactylus.sone.freenet.fcp.AbstractCommand;
33 import net.pterodactylus.sone.freenet.fcp.Command;
34 import net.pterodactylus.sone.freenet.fcp.FcpException;
35 import net.pterodactylus.sone.template.SoneAccessor;
37 import com.google.common.base.Optional;
38 import com.google.common.collect.Collections2;
40 import freenet.node.FSParseException;
41 import freenet.support.SimpleFieldSet;
44 * Abstract base implementation of a {@link Command} with Sone-related helper
47 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
49 public abstract class AbstractSoneCommand extends AbstractCommand {
52 private final Core core;
54 /** Whether this command needs write access. */
55 private final boolean writeAccess;
58 * Creates a new abstract Sone FCP command.
63 protected AbstractSoneCommand(Core core) {
68 * Creates a new abstract Sone FCP command.
73 * {@code true} if this command requires write access,
74 * {@code false} otherwise
76 protected AbstractSoneCommand(Core core, boolean writeAccess) {
78 this.writeAccess = writeAccess;
86 * Returns the Sone core.
88 * @return The Sone core
90 protected Core getCore() {
95 * Returns whether this command requires write access.
97 * @return {@code true} if this command require write access, {@code false}
100 public boolean requiresWriteAccess() {
109 * Encodes text in a way that makes it possible for the text to be stored in
110 * a {@link SimpleFieldSet}. Backslashes, CR, and LF are prepended with a
115 * @return The encoded text
117 protected static String encodeString(String text) {
118 return text.replaceAll("\\\\", "\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r");
122 * Returns a Sone whose ID is a parameter in the given simple field set.
124 * @param simpleFieldSet
125 * The simple field set containing the ID of the Sone
126 * @param parameterName
127 * The name under which the Sone ID is stored in the simple field
130 * @throws FcpException
131 * if there is no Sone ID stored under the given parameter name,
132 * or if the Sone ID is invalid
134 protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
135 return getSone(simpleFieldSet, parameterName, true).get();
139 * Returns a Sone whose ID is a parameter in the given simple field set.
141 * @param simpleFieldSet
142 * The simple field set containing the ID of the Sone
143 * @param parameterName
144 * The name under which the Sone ID is stored in the simple field
147 * @throws FcpException
148 * if there is no Sone ID stored under the given parameter name,
149 * or if the Sone ID is invalid
151 protected LocalSone getLocalSone(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
152 return getLocalSone(simpleFieldSet, parameterName, true).get();
156 * Returns a Sone whose ID is a parameter in the given simple field set.
158 * @param simpleFieldSet
159 * The simple field set containing the ID of the Sone
160 * @param parameterName
161 * The name under which the Sone ID is stored in the simple field
164 * {@code true} if a valid Sone ID is required, {@code false}
166 * @return The Sone, or {@code null} if {@code mandatory} is {@code false}
167 * and the Sone ID is invalid
168 * @throws FcpException
169 * if there is no Sone ID stored under the given parameter name,
170 * or if {@code mandatory} is {@code true} and the Sone ID is
173 protected Optional<Sone> getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean mandatory) throws FcpException {
174 String soneId = simpleFieldSet.get(parameterName);
175 if (mandatory && (soneId == null)) {
176 throw new FcpException("Could not load Sone ID from “" + parameterName + "”.");
178 Optional<Sone> sone = core.getSone(soneId);
179 if ((mandatory && !sone.isPresent()) || (mandatory && sone.isPresent())) {
180 throw new FcpException("Could not load Sone from “" + soneId + "”.");
186 * Returns a Sone whose ID is a parameter in the given simple field set.
188 * @param simpleFieldSet
189 * The simple field set containing the ID of the Sone
190 * @param parameterName
191 * The name under which the Sone ID is stored in the simple field
194 * {@code true} if a valid Sone ID is required, {@code false}
196 * @return The Sone, or {@code null} if {@code mandatory} is {@code false}
197 * and the Sone ID is invalid
198 * @throws FcpException
199 * if there is no Sone ID stored under the given parameter name,
200 * or if {@code mandatory} is {@code true} and the Sone ID is
203 protected Optional<LocalSone> getLocalSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean mandatory) throws FcpException {
204 String soneId = simpleFieldSet.get(parameterName);
205 if (mandatory && (soneId == null)) {
206 throw new FcpException("Could not load Sone ID from “" + parameterName + "”.");
208 Optional<LocalSone> sone = core.getLocalSone(soneId);
209 if ((mandatory && !sone.isPresent()) || (mandatory && sone.isPresent() && !sone.get().isLocal())) {
210 throw new FcpException("Could not load Sone from “" + soneId + "”.");
216 * Returns a post whose ID is a parameter in the given simple field set.
218 * @param simpleFieldSet
219 * The simple field set containing the ID of the post
220 * @param parameterName
221 * The name under which the post ID is stored in the simple field
224 * @throws FcpException
225 * if there is no post ID stored under the given parameter name,
226 * or if the post ID is invalid
228 protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
230 String postId = simpleFieldSet.getString(parameterName);
231 Optional<Post> post = core.getPost(postId);
232 if (!post.isPresent()) {
233 throw new FcpException("Could not load post from “" + postId + "”.");
236 } catch (FSParseException fspe1) {
237 throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1);
242 * Returns a reply whose ID is a parameter in the given simple field set.
244 * @param simpleFieldSet
245 * The simple field set containing the ID of the reply
246 * @param parameterName
247 * The name under which the reply ID is stored in the simple
250 * @throws FcpException
251 * if there is no reply ID stored under the given parameter
252 * name, or if the reply ID is invalid
254 protected PostReply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
256 String replyId = simpleFieldSet.getString(parameterName);
257 Optional<PostReply> reply = core.getPostReply(replyId);
258 if (!reply.isPresent()) {
259 throw new FcpException("Could not load reply from “" + replyId + "”.");
262 } catch (FSParseException fspe1) {
263 throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1);
268 * Creates a simple field set from the given Sone, including {@link Profile}
274 * The prefix for the field names (may be empty but not {@code
277 * An optional local Sone that is used for Sone-specific data,
278 * such as if the Sone is followed by the local Sone
279 * @return The simple field set containing the given Sone
281 protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Optional<LocalSone> localSone) {
282 SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
284 soneBuilder.put(prefix + "Name", sone.getName());
285 soneBuilder.put(prefix + "NiceName", SoneAccessor.getNiceName(sone));
286 soneBuilder.put(prefix + "LastUpdated", sone.getTime());
287 if (localSone.isPresent()) {
288 soneBuilder.put(prefix + "Followed", String.valueOf(localSone.get().hasFriend(sone.getId())));
290 Profile profile = sone.getProfile();
291 soneBuilder.put(prefix + "Field.Count", profile.getFields().size());
293 for (Field field : profile.getFields()) {
294 soneBuilder.put(prefix + "Field." + fieldIndex + ".Name", field.getName());
295 soneBuilder.put(prefix + "Field." + fieldIndex + ".Value", field.getValue());
299 return soneBuilder.get();
303 * Creates a simple field set from the given collection of Sones.
306 * The Sones to encode
308 * The prefix for the field names (may be empty but not
310 * @return The simple field set containing the given Sones
312 protected static SimpleFieldSet encodeSones(Collection<? extends Sone> sones, String prefix) {
313 SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
316 soneBuilder.put(prefix + "Count", sones.size());
317 for (Sone sone : sones) {
318 String sonePrefix = prefix + soneIndex++ + ".";
319 soneBuilder.put(sonePrefix + "ID", sone.getId());
320 soneBuilder.put(sonePrefix + "Name", sone.getName());
321 soneBuilder.put(sonePrefix + "NiceName", SoneAccessor.getNiceName(sone));
322 soneBuilder.put(sonePrefix + "Time", sone.getTime());
325 return soneBuilder.get();
329 * Creates a simple field set from the given post.
334 * The prefix for the field names (may be empty but not
336 * @param includeReplies
337 * {@code true} to include replies, {@code false} to not include
339 * @return The simple field set containing the post
341 protected SimpleFieldSet encodePost(Post post, String prefix, boolean includeReplies) {
342 SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
344 postBuilder.put(prefix + "ID", post.getId());
345 postBuilder.put(prefix + "Sone", post.getSone().getId());
346 if (post.getRecipientId().isPresent()) {
347 postBuilder.put(prefix + "Recipient", post.getRecipientId().get());
349 postBuilder.put(prefix + "Time", post.getTime());
350 postBuilder.put(prefix + "Text", encodeString(post.getText()));
351 postBuilder.put(encodeLikes(core.getLikes(post), prefix + "Likes."));
353 if (includeReplies) {
354 List<PostReply> replies = core.getReplies(post.getId());
355 postBuilder.put(encodeReplies(replies, prefix));
358 return postBuilder.get();
362 * Creates a simple field set from the given collection of posts.
365 * The posts to encode
367 * The prefix for the field names (may be empty but not
369 * @param includeReplies
370 * {@code true} to include the replies, {@code false} to not
371 * include the replies
372 * @return The simple field set containing the posts
374 protected SimpleFieldSet encodePosts(Collection<? extends Post> posts, String prefix, boolean includeReplies) {
375 SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
378 postBuilder.put(prefix + "Count", posts.size());
379 for (Post post : posts) {
380 String postPrefix = prefix + postIndex++;
381 postBuilder.put(encodePost(post, postPrefix + ".", includeReplies));
382 if (includeReplies) {
383 postBuilder.put(encodeReplies(Collections2.filter(core.getReplies(post.getId()), Reply.FUTURE_REPLY_FILTER), postPrefix + "."));
387 return postBuilder.get();
391 * Creates a simple field set from the given collection of replies.
394 * The replies to encode
396 * The prefix for the field names (may be empty, but not
398 * @return The simple field set containing the replies
400 protected static SimpleFieldSet encodeReplies(Collection<? extends PostReply> replies, String prefix) {
401 SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder();
404 replyBuilder.put(prefix + "Replies.Count", replies.size());
405 for (PostReply reply : replies) {
406 String replyPrefix = prefix + "Replies." + replyIndex++ + ".";
407 replyBuilder.put(replyPrefix + "ID", reply.getId());
408 replyBuilder.put(replyPrefix + "Sone", reply.getSone().getId());
409 replyBuilder.put(replyPrefix + "Time", reply.getTime());
410 replyBuilder.put(replyPrefix + "Text", encodeString(reply.getText()));
413 return replyBuilder.get();
417 * Creates a simple field set from the given collection of Sones that like
423 * The prefix for the field names (may be empty but not
425 * @return The simple field set containing the likes
427 protected static SimpleFieldSet encodeLikes(Collection<? extends Sone> likes, String prefix) {
428 SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder();
431 likesBuilder.put(prefix + "Count", likes.size());
432 for (Sone sone : likes) {
433 String sonePrefix = prefix + likeIndex++ + ".";
434 likesBuilder.put(sonePrefix + "ID", sone.getId());
437 return likesBuilder.get();
448 public String toString() {
449 return getClass().getName() + "[writeAccess=" + writeAccess + "]";