<artifactId>guava</artifactId>
<version>14.0-rc1</version>
</dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.6</version>
+ </dependency>
</dependencies>
<repositories>
<repository>
import net.pterodactylus.sone.data.Client;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostBuilder;
+import net.pterodactylus.sone.data.PostBuilderFactory;
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.ShowCustomAvatars;
import net.pterodactylus.sone.data.Sone.SoneStatus;
import net.pterodactylus.sone.data.TemporaryImage;
-import net.pterodactylus.sone.data.impl.PostImpl;
import net.pterodactylus.sone.data.impl.PostReplyImpl;
import net.pterodactylus.sone.fcp.FcpInterface;
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
/** All known Sones. */
private final Set<String> knownSones = new HashSet<String>();
+ /** The post builder. */
+ private final PostBuilderFactory postBuilderFactory;
+
/** All posts. */
private final Map<String, Post> posts = new HashMap<String, Post>();
* The WebOfTrust updater
* @param eventBus
* The event bus
+ * @param postBuilderFactory
+ * The post builder
*/
@Inject
- public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus) {
+ public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, PostBuilderFactory postBuilderFactory) {
super("Sone Core");
this.configuration = configuration;
this.freenetInterface = freenetInterface;
this.updateChecker = new UpdateChecker(eventBus, freenetInterface);
this.webOfTrustUpdater = webOfTrustUpdater;
this.eventBus = eventBus;
+ this.postBuilderFactory = postBuilderFactory;
}
//
}
/**
- * Returns the post with the given ID.
+ * Returns a post builder.
*
- * @param postId
- * The ID of the post to get
- * @return The post with the given ID, or a new post with the given ID
+ * @return A new post builder
*/
- public Post getPost(String postId) {
- return getPost(postId, true);
+ public PostBuilder postBuilder() {
+ return postBuilderFactory.newPostBuilder();
}
/**
List<Post> storedPosts = storedSone.getPosts();
synchronized (knownPosts) {
for (Post post : sone.getPosts()) {
- post.setSone(storedSone).setKnown(knownPosts.contains(post.getId()));
- if (!storedPosts.contains(post)) {
- if (post.getTime() < getSoneFollowingTime(sone)) {
- knownPosts.add(post.getId());
- post.setKnown(true);
- } else if (!knownPosts.contains(post.getId())) {
- eventBus.post(new NewPostFoundEvent(post));
+ PostBuilder postBuilder = postBuilderFactory.newPostBuilder();
+ postBuilder.copyPost(post).from(storedSone);
+ Post newPost = postBuilder.build().setKnown(knownPosts.contains(post.getId()));
+ if (!storedPosts.contains(newPost)) {
+ if (newPost.getTime() < getSoneFollowingTime(sone)) {
+ knownPosts.add(newPost.getId());
+ newPost.setKnown(true);
+ } else if (!knownPosts.contains(newPost.getId())) {
+ eventBus.post(new NewPostFoundEvent(newPost));
}
}
- posts.put(post.getId(), post);
+ posts.put(newPost.getId(), newPost);
}
}
}
logger.log(Level.WARNING, "Invalid post found, aborting load!");
return;
}
- Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText);
+ PostBuilder postBuilder = postBuilderFactory.newPostBuilder().withId(postId).from(sone).withTime(postTime).withText(postText);
if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
- post.setRecipient(getSone(postRecipientId));
+ postBuilder.to(getSone(postRecipientId));
}
- posts.add(post);
+ posts.add(postBuilder.build());
}
/* load replies. */
logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone));
return null;
}
- final Post post = new PostImpl(sone, time, text.trim());
+ PostBuilder postBuilder = postBuilderFactory.newPostBuilder();
+ postBuilder.from(sone).randomId().withTime(time).withText(text.trim());
if (recipient != null) {
- post.setRecipient(recipient);
+ postBuilder.to(recipient);
}
+ final Post post = postBuilder.build();
synchronized (posts) {
posts.put(post.getId(), post);
}
import net.pterodactylus.sone.data.Client;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostBuilder;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
return null;
}
try {
- Post post = core.getPost(postId).setSone(sone).setTime(Long.parseLong(postTime)).setText(postText);
+ PostBuilder postBuilder = core.postBuilder();
+ /* TODO - parse time correctly. */
+ postBuilder.withId(postId).from(sone).withTime(Long.parseLong(postTime)).withText(postText);
if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
- post.setRecipient(core.getSone(postRecipientId));
+ postBuilder.to(core.getSone(postRecipientId));
}
- posts.add(post);
+ posts.add(postBuilder.build());
} catch (NumberFormatException nfe1) {
/* TODO - mark Sone as bad. */
logger.log(Level.WARNING, String.format("Downloaded post for Sone %s with invalid time: %s", sone, postTime));
public Sone getSone();
/**
- * Sets the Sone of this post.
- *
- * @param sone
- * The Sone of this post
- * @return This post (for method chaining)
- */
- public Post setSone(Sone sone);
-
- /**
* Returns the recipient of this post, if any.
*
* @return The recipient of this post, or {@code null}
public Sone getRecipient();
/**
- * Sets the recipient of this post.
- *
- * @param recipient
- * The recipient of this post, or {@code null}
- * @return This post (for method chaining)
- */
- public Post setRecipient(Sone recipient);
-
- /**
* Returns the time of the post.
*
* @return The time of the post (in milliseconds since Jan 1, 1970 UTC)
public long getTime();
/**
- * Sets the time of this post.
- *
- * @param time
- * The time of this post (in milliseconds since Jan 1, 1970 UTC)
- * @return This post (for method chaining)
- */
- public Post setTime(long time);
-
- /**
* Returns the text of the post.
*
* @return The text of the post
public String getText();
/**
- * Sets the text of this post.
- *
- * @param text
- * The text of this post
- * @return This post (for method chaining)
- */
- public Post setText(String text);
-
- /**
* Returns whether this post is known.
*
* @return {@code true} if this post is known, {@code false} otherwise
--- /dev/null
+/*
+ * Sone - PostBuilder.java - Copyright © 2013 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data;
+
+/**
+ * Builder for {@link Post} objects.
+ * <p>
+ * A {@link Post} consists of the following elements:
+ * <ul>
+ * <li>an ID,</li>
+ * <li>a {@link Sone sender},</li>
+ * <li>an optional {@link Sone recipient},</li>
+ * <li>a time,</li>
+ * <li>and a text.</li>
+ * </ul>
+ * Except for the recipient, all this elements have to be configured on this
+ * builder. For the ID you have the possibility to configure either a random ID
+ * (which should be used for new posts) or a custom ID you specify (for creating
+ * an existing post). For the time you can use the current time (again, for
+ * creating new posts) or the given time (for loading posts). It is an error to
+ * specify both ways for either the ID or the time.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface PostBuilder {
+
+ /**
+ * Copies all attributes of the given post to this post builder.
+ *
+ * @param post
+ * The post whose attributes to copy into this builder
+ * @return This builder
+ * @throws NullPointerException
+ * if {@code post} is {@code null}
+ */
+ public PostBuilder copyPost(Post post) throws NullPointerException;
+
+ /**
+ * Configures this builder to use the given Sone as sender of the new post.
+ *
+ * @param sender
+ * The sender of the post
+ * @return This post builder
+ */
+ public PostBuilder from(Sone sender);
+
+ /**
+ * Configures this builder to use a random ID for the new post. If this
+ * method is used, {@link #withId(String)} must not be used.
+ *
+ * @return This post builder
+ */
+ public PostBuilder randomId();
+
+ /**
+ * Configures this builder to use the given ID as ID for the new post. If
+ * this method is used, {@link #randomId()} must not be used.
+ *
+ * @param id
+ * The ID to use for the post
+ * @return This post builder
+ */
+ public PostBuilder withId(String id);
+
+ /**
+ * Configures this builder to use the current time when creating the post.
+ * If this method is used, {@link #withTime(long)} must not be used.
+ *
+ * @return This post builder
+ */
+ public PostBuilder currentTime();
+
+ /**
+ * Configures the builder to use the given time as time for the new post. If
+ * this method is used, {@link #currentTime()} must not be used.
+ *
+ * @param time
+ * The time to use for the post
+ * @return This post builder
+ */
+ public PostBuilder withTime(long time);
+
+ /**
+ * Configures the builder to use the given text for the new post.
+ *
+ * @param text
+ * The text to use for the post
+ * @return This post builder
+ */
+ public PostBuilder withText(String text);
+
+ /**
+ * Configures the builder to use the given {@link Sone} as recipient for the
+ * post.
+ *
+ * @param recipient
+ * The recipient of the post
+ * @return This post builder
+ */
+ public PostBuilder to(Sone recipient);
+
+ /**
+ * Verifies this builder’s configuration and creates a new post.
+ * <p>
+ * The following conditions must be met in order for this builder to be
+ * configured correctly:
+ * <ul>
+ * <li>Exactly one of {@link #randomId()} or {@link #withId(String)} must
+ * have been called.</li>
+ * <li>The {@link #from(Sone) sender} must not be {@code null}.</li>
+ * <li>Exactly one of {@link #currentTime()} or {@link #withTime(long)} must
+ * have been called.</li>
+ * <li>The {@link #withText(String) text} must not be {@code null} and must
+ * contain something other than whitespace.</li>
+ * <li>The {@link #to(Sone) recipient} must either not have been set, or it
+ * must have been set to a {@link Sone} other than {@link #from(Sone) the
+ * sender}.</li>
+ * </ul>
+ *
+ * @return A new post
+ * @throws IllegalStateException
+ * if this builder’s configuration is not valid
+ */
+ public Post build() throws IllegalStateException;
+
+}
--- /dev/null
+/*
+ * Sone - PostBuilderFactory.java - Copyright © 2013 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data;
+
+/**
+ * Factory for {@link PostBuilder}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface PostBuilderFactory {
+
+ /**
+ * Creates a new post builder.
+ *
+ * @return A new post builder
+ */
+ public PostBuilder newPostBuilder();
+
+}
--- /dev/null
+/*
+ * Sone - DefaultPostBuilderFactory.java - Copyright © 2013 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data.impl;
+
+import net.pterodactylus.sone.data.PostBuilder;
+import net.pterodactylus.sone.data.PostBuilderFactory;
+
+/**
+ * {@link PostBuilderFactory} implementation that creates
+ * {@link PostBuilderImpl}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DefaultPostBuilderFactory implements PostBuilderFactory {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder newPostBuilder() {
+ return new PostBuilderImpl();
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PostBuilderImpl.java - Copyright © 2013 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.data.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.UUID;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostBuilder;
+import net.pterodactylus.sone.data.Sone;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * {@link PostBuilder} implementation that creates {@link PostImpl} objects.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PostBuilderImpl implements PostBuilder {
+
+ /** Wether to create a post with a random ID. */
+ private boolean randomId;
+
+ /** The ID of the post. */
+ private String id;
+
+ /** The sender of the post. */
+ private Sone sender;
+
+ /** Whether to use the current time when creating the post. */
+ private boolean currentTime;
+
+ /** The time of the post. */
+ private long time;
+
+ /** The text of the post. */
+ private String text;
+
+ /** The (optional) recipient of the post. */
+ private Sone recipient;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder copyPost(Post post) {
+ this.randomId = false;
+ this.id = post.getId();
+ this.sender = post.getSone();
+ this.currentTime = false;
+ this.time = post.getTime();
+ this.text = post.getText();
+ this.recipient = post.getRecipient();
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder randomId() {
+ randomId = true;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder withId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder from(Sone sender) {
+ this.sender = sender;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder currentTime() {
+ currentTime = true;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder withTime(long time) {
+ this.time = time;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder withText(String text) {
+ this.text = text;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostBuilder to(Sone recipient) {
+ this.recipient = recipient;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Post build() {
+ checkState(!randomId && (id == null), "neither random ID or custom ID set");
+ checkState(randomId && (id != null), "both random ID and custom ID set");
+ checkState(sender != null, "sender must not be null");
+ checkState(!currentTime && (time == 0), "neither current time or custom time set");
+ checkState(currentTime && (time != 0), "both current time and custom time set");
+ checkState(!StringUtils.isBlank(text), "text must not be empty");
+ checkState((recipient == null) || !recipient.equals(sender), "sender and recipient must not be the same");
+ return new PostImpl(randomId ? UUID.randomUUID().toString() : id, sender, currentTime ? System.currentTimeMillis() : time, text).setRecipient(recipient);
+ }
+
+}
}
/**
- * {@inheritDoc}
+ * Sets the Sone of this post.
+ *
+ * @param sone
+ * The Sone of this post
+ * @return This post (for method chaining)
*/
- @Override
public PostImpl setSone(Sone sone) {
this.sone = sone;
return this;
}
/**
- * {@inheritDoc}
+ * Sets the recipient of this post.
+ *
+ * @param recipient
+ * The recipient of this post, or {@code null}
+ * @return This post (for method chaining)
*/
- @Override
public PostImpl setRecipient(Sone recipient) {
if (!sone.equals(recipient)) {
this.recipient = recipient;
}
/**
- * {@inheritDoc}
+ * Sets the time of this post.
+ *
+ * @param time
+ * The time of this post (in milliseconds since Jan 1, 1970 UTC)
+ * @return This post (for method chaining)
*/
- @Override
public PostImpl setTime(long time) {
this.time = time;
return this;
}
/**
- * {@inheritDoc}
+ * Sets the text of this post.
+ *
+ * @param text
+ * The text of this post
+ * @return This post (for method chaining)
*/
- @Override
public PostImpl setText(String text) {
this.text = text;
return this;
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.core.FreenetInterface;
import net.pterodactylus.sone.core.WebOfTrustUpdater;
+import net.pterodactylus.sone.data.PostBuilderFactory;
+import net.pterodactylus.sone.data.impl.DefaultPostBuilderFactory;
import net.pterodactylus.sone.fcp.FcpInterface;
import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
import net.pterodactylus.sone.freenet.plugin.PluginConnector;
bind(String.class).annotatedWith(Names.named("WebOfTrustContext")).toInstance("Sone");
bind(SonePlugin.class).toInstance(SonePlugin.this);
bind(FcpInterface.class).in(Singleton.class);
+ bind(PostBuilderFactory.class).to(DefaultPostBuilderFactory.class).in(Singleton.class);
bindListener(Matchers.any(), new TypeListener() {
@Override