X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Ffcp%2FAbstractSoneCommand.java;h=98a43ab95734e851b647d6d862f4aa2ff713f63d;hp=7cc4b0fd24f6cc1aa4e8f3b759b1026e71601448;hb=ffd92ca2374c0b2218e583d02e0bdd24b8c110ae;hpb=3de13c41f62532117c638c09edbd328ccf6764c5 diff --git a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java index 7cc4b0f..98a43ab 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java @@ -1,5 +1,5 @@ /* - * Sone - FcpInterface.java - Copyright © 2011 David Roden + * Sone - AbstractSoneCommand.java - Copyright © 2011–2016 David Roden * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,19 +18,25 @@ package net.pterodactylus.sone.fcp; import java.util.Collection; +import java.util.List; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.Reply; +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.data.Profile; +import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder; import net.pterodactylus.sone.freenet.fcp.AbstractCommand; import net.pterodactylus.sone.freenet.fcp.Command; import net.pterodactylus.sone.freenet.fcp.FcpException; import net.pterodactylus.sone.template.SoneAccessor; + import freenet.node.FSParseException; import freenet.support.SimpleFieldSet; +import com.google.common.base.Optional; + /** * Abstract base implementation of a {@link Command} with Sone-related helper * methods. @@ -42,6 +48,9 @@ public abstract class AbstractSoneCommand extends AbstractCommand { /** The Sone core. */ private final Core core; + /** Whether this command needs write access. */ + private final boolean writeAccess; + /** * Creates a new abstract Sone FCP command. * @@ -49,7 +58,21 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * The Sone core */ protected AbstractSoneCommand(Core core) { + this(core, false); + } + + /** + * Creates a new abstract Sone FCP command. + * + * @param core + * The Sone core + * @param writeAccess + * {@code true} if this command requires write access, + * {@code false} otherwise + */ + protected AbstractSoneCommand(Core core, boolean writeAccess) { this.core = core; + this.writeAccess = writeAccess; } // @@ -65,11 +88,34 @@ public abstract class AbstractSoneCommand extends AbstractCommand { return core; } + /** + * Returns whether this command requires write access. + * + * @return {@code true} if this command require write access, {@code false} + * otherwise + */ + public boolean requiresWriteAccess() { + return writeAccess; + } + // // PROTECTED METHODS // /** + * Encodes text in a way that makes it possible for the text to be stored in + * a {@link SimpleFieldSet}. Backslashes, CR, and LF are prepended with a + * backslash. + * + * @param text + * The text to encode + * @return The encoded text + */ + protected static String encodeString(String text) { + return text.replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r"); + } + + /** * Returns a Sone whose ID is a parameter in the given simple field set. * * @param simpleFieldSet @@ -77,68 +123,216 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * @param parameterName * The name under which the Sone ID is stored in the simple field * set + * @param localOnly + * {@code true} to only return local Sones, {@code false} to + * return any Sones * @return The Sone * @throws FcpException * if there is no Sone ID stored under the given parameter name, * or if the Sone ID is invalid */ - protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { + protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly) throws FcpException { + return getSone(simpleFieldSet, parameterName, localOnly, true).get(); + } + + /** + * Returns a Sone whose ID is a parameter in the given simple field set. + * + * @param simpleFieldSet + * The simple field set containing the ID of the Sone + * @param parameterName + * The name under which the Sone ID is stored in the simple field + * set + * @param localOnly + * {@code true} to only return local Sones, {@code false} to + * return any Sones + * @param mandatory + * {@code true} if a valid Sone ID is required, {@code false} + * otherwise + * @return The Sone, or {@code null} if {@code mandatory} is {@code false} + * and the Sone ID is invalid + * @throws FcpException + * if there is no Sone ID stored under the given parameter name, + * or if {@code mandatory} is {@code true} and the Sone ID is + * invalid + */ + protected Optional getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly, boolean mandatory) throws FcpException { + String soneId = simpleFieldSet.get(parameterName); + if (mandatory && (soneId == null)) { + throw new FcpException("Could not load Sone ID from “" + parameterName + "”."); + } + Optional sone = core.getSone(soneId); + if ((mandatory && !sone.isPresent()) || (sone.isPresent() && localOnly && !sone.get().isLocal())) { + throw new FcpException("Could not load Sone from “" + soneId + "”."); + } + return sone; + } + + /** + * Returns a post whose ID is a parameter in the given simple field set. + * + * @param simpleFieldSet + * The simple field set containing the ID of the post + * @param parameterName + * The name under which the post ID is stored in the simple field + * set + * @return The post + * @throws FcpException + * if there is no post ID stored under the given parameter name, + * or if the post ID is invalid + */ + protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { try { - String soneId = simpleFieldSet.getString(parameterName); - Sone sone = core.getSone(soneId, false); - if (sone == null) { - throw new FcpException("Could not load Sone from “" + soneId + "”."); + String postId = simpleFieldSet.getString(parameterName); + Optional post = core.getPost(postId); + if (!post.isPresent()) { + throw new FcpException("Could not load post from “" + postId + "”."); } - return sone; + return post.get(); } catch (FSParseException fspe1) { - throw new FcpException("Could not load Sone ID from “" + parameterName + "”.", fspe1); + throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1); } } /** + * Returns a reply whose ID is a parameter in the given simple field set. + * + * @param simpleFieldSet + * The simple field set containing the ID of the reply + * @param parameterName + * The name under which the reply ID is stored in the simple + * field set + * @return The reply + * @throws FcpException + * if there is no reply ID stored under the given parameter + * name, or if the reply ID is invalid + */ + protected PostReply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { + try { + String replyId = simpleFieldSet.getString(parameterName); + Optional reply = core.getPostReply(replyId); + if (!reply.isPresent()) { + throw new FcpException("Could not load reply from “" + replyId + "”."); + } + return reply.get(); + } catch (FSParseException fspe1) { + throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1); + } + } + + /** + * Creates a simple field set from the given Sone, including {@link Profile} + * information. + * + * @param sone + * The Sone to encode + * @param prefix + * The prefix for the field names (may be empty but not {@code + * null}) + * @param localSone + * An optional local Sone that is used for Sone-specific data, + * such as if the Sone is followed by the local Sone + * @return The simple field set containing the given Sone + */ + protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Optional localSone) { + SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder(); + + soneBuilder.put(prefix + "ID", sone.getId()); + soneBuilder.put(prefix + "Name", sone.getName()); + soneBuilder.put(prefix + "NiceName", SoneAccessor.getNiceName(sone)); + soneBuilder.put(prefix + "LastUpdated", sone.getTime()); + if (localSone.isPresent()) { + soneBuilder.put(prefix + "Followed", String.valueOf(localSone.get().hasFriend(sone.getId()))); + } + Profile profile = sone.getProfile(); + soneBuilder.put(prefix + "Field.Count", profile.getFields().size()); + int fieldIndex = 0; + for (Field field : profile.getFields()) { + soneBuilder.put(prefix + "Field." + fieldIndex + ".Name", field.getName()); + soneBuilder.put(prefix + "Field." + fieldIndex + ".Value", field.getValue()); + ++fieldIndex; + } + + return soneBuilder.get(); + } + + /** * Creates a simple field set from the given collection of Sones. * * @param sones * The Sones to encode + * @param prefix + * The prefix for the field names (may be empty but not + * {@code null}) * @return The simple field set containing the given Sones */ - protected SimpleFieldSet encodeSones(Collection sones) { + protected static SimpleFieldSet encodeSones(Collection sones, String prefix) { SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder(); int soneIndex = 0; - soneBuilder.put("Sones.Count", sones.size()); + soneBuilder.put(prefix + "Count", sones.size()); for (Sone sone : sones) { - String sonePrefix = "Sones." + soneIndex++; - soneBuilder.put(sonePrefix + ".ID", sone.getId()); - soneBuilder.put(sonePrefix + ".Name", sone.getName()); - soneBuilder.put(sonePrefix + ".NiceName", SoneAccessor.getNiceName(sone)); - soneBuilder.put(sonePrefix + ".Time", sone.getTime()); + String sonePrefix = prefix + soneIndex++ + "."; + soneBuilder.put(encodeSone(sone, sonePrefix, Optional.absent())); } return soneBuilder.get(); } /** + * Creates a simple field set from the given post. + * + * @param post + * The post to encode + * @param prefix + * The prefix for the field names (may be empty but not + * {@code null}) + * @param includeReplies + * {@code true} to include replies, {@code false} to not include + * replies + * @return The simple field set containing the post + */ + protected SimpleFieldSet encodePost(Post post, String prefix, boolean includeReplies) { + SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder(); + + postBuilder.put(prefix + "ID", post.getId()); + postBuilder.put(prefix + "Sone", post.getSone().getId()); + if (post.getRecipientId().isPresent()) { + postBuilder.put(prefix + "Recipient", post.getRecipientId().get()); + } + postBuilder.put(prefix + "Time", post.getTime()); + postBuilder.put(prefix + "Text", encodeString(post.getText())); + postBuilder.put(encodeLikes(core.getLikes(post), prefix + "Likes.")); + + if (includeReplies) { + List replies = core.getReplies(post.getId()); + postBuilder.put(encodeReplies(replies, prefix)); + } + + return postBuilder.get(); + } + + /** * Creates a simple field set from the given collection of posts. * * @param posts * The posts to encode + * @param prefix + * The prefix for the field names (may be empty but not + * {@code null}) + * @param includeReplies + * {@code true} to include the replies, {@code false} to not + * include the replies * @return The simple field set containing the posts */ - public SimpleFieldSet encodePosts(Collection posts) { + protected SimpleFieldSet encodePosts(Collection posts, String prefix, boolean includeReplies) { SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder(); int postIndex = 0; - postBuilder.put("Posts.Count", posts.size()); + postBuilder.put(prefix + "Count", posts.size()); for (Post post : posts) { - String postPrefix = "Posts." + postIndex++; - postBuilder.put(postPrefix + ".ID", post.getId()); - postBuilder.put(postPrefix + ".Sone", post.getSone().getId()); - if (post.getRecipient() != null) { - postBuilder.put(postPrefix + ".Recipient", post.getRecipient().getId()); - } - postBuilder.put(postPrefix + ".Time", post.getTime()); - postBuilder.put(postPrefix + ".Text", post.getText()); + String postPrefix = prefix + postIndex++; + postBuilder.put(encodePost(post, postPrefix + ".", includeReplies)); } return postBuilder.get(); @@ -154,20 +348,57 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * {@code null}) * @return The simple field set containing the replies */ - public SimpleFieldSet encodeReplies(Collection replies, String prefix) { + protected SimpleFieldSet encodeReplies(Collection replies, String prefix) { SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder(); int replyIndex = 0; replyBuilder.put(prefix + "Replies.Count", replies.size()); - for (Reply reply : replies) { - String replyPrefix = prefix + "Replies." + replyIndex++; - replyBuilder.put(replyPrefix + ".ID", reply.getId()); - replyBuilder.put(replyPrefix + ".Sone", reply.getSone().getId()); - replyBuilder.put(replyPrefix + ".Time", reply.getTime()); - replyBuilder.put(replyPrefix + ".Text", reply.getText()); + for (PostReply reply : replies) { + String replyPrefix = prefix + "Replies." + replyIndex++ + "."; + replyBuilder.put(replyPrefix + "ID", reply.getId()); + replyBuilder.put(replyPrefix + "Sone", reply.getSone().getId()); + replyBuilder.put(replyPrefix + "Time", reply.getTime()); + replyBuilder.put(replyPrefix + "Text", encodeString(reply.getText())); + replyBuilder.put(encodeLikes(core.getLikes(reply), replyPrefix + "Likes.")); } return replyBuilder.get(); } + /** + * Creates a simple field set from the given collection of Sones that like + * an element. + * + * @param likes + * The liking Sones + * @param prefix + * The prefix for the field names (may be empty but not + * {@code null}) + * @return The simple field set containing the likes + */ + protected static SimpleFieldSet encodeLikes(Collection likes, String prefix) { + SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder(); + + int likeIndex = 0; + likesBuilder.put(prefix + "Count", likes.size()); + for (Sone sone : likes) { + String sonePrefix = prefix + likeIndex++ + "."; + likesBuilder.put(sonePrefix + "ID", sone.getId()); + } + + return likesBuilder.get(); + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return getClass().getName() + "[writeAccess=" + writeAccess + "]"; + } + }