From da609f721e54691f27113e877a19637bd332abc3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 18 Jan 2013 11:14:13 +0100 Subject: [PATCH] Add post builder. --- pom.xml | 5 + .../java/net/pterodactylus/sone/core/Core.java | 51 ++++--- .../pterodactylus/sone/core/SoneDownloader.java | 9 +- .../java/net/pterodactylus/sone/data/Post.java | 36 ----- .../net/pterodactylus/sone/data/PostBuilder.java | 141 +++++++++++++++++++ .../sone/data/PostBuilderFactory.java | 34 +++++ .../sone/data/impl/DefaultPostBuilderFactory.java | 39 ++++++ .../sone/data/impl/PostBuilderImpl.java | 151 +++++++++++++++++++++ .../net/pterodactylus/sone/data/impl/PostImpl.java | 28 ++-- .../net/pterodactylus/sone/main/SonePlugin.java | 3 + 10 files changed, 429 insertions(+), 68 deletions(-) create mode 100644 src/main/java/net/pterodactylus/sone/data/PostBuilder.java create mode 100644 src/main/java/net/pterodactylus/sone/data/PostBuilderFactory.java create mode 100644 src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java create mode 100644 src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java diff --git a/pom.xml b/pom.xml index 6743885..d4637ff 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,11 @@ guava 14.0-rc1 + + commons-lang + commons-lang + 2.6 + diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 1ebdcc8..2a1ef8e 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -56,6 +56,8 @@ import net.pterodactylus.sone.data.Album; 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; @@ -64,7 +66,6 @@ import net.pterodactylus.sone.data.Sone; 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; @@ -168,6 +169,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider /** All known Sones. */ private final Set knownSones = new HashSet(); + /** The post builder. */ + private final PostBuilderFactory postBuilderFactory; + /** All posts. */ private final Map posts = new HashMap(); @@ -215,9 +219,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * 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; @@ -227,6 +233,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider this.updateChecker = new UpdateChecker(eventBus, freenetInterface); this.webOfTrustUpdater = webOfTrustUpdater; this.eventBus = eventBus; + this.postBuilderFactory = postBuilderFactory; } // @@ -511,14 +518,12 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } /** - * 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(); } /** @@ -1098,16 +1103,18 @@ public class Core extends AbstractService implements SoneProvider, PostProvider List 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); } } } @@ -1300,11 +1307,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider 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. */ @@ -1526,10 +1533,12 @@ public class Core extends AbstractService implements SoneProvider, PostProvider 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); } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java index 70a193a..36b672c 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java @@ -31,6 +31,7 @@ import net.pterodactylus.sone.data.Album; 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; @@ -372,11 +373,13 @@ public class SoneDownloader extends AbstractService { 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)); diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index 3486bed..160bb32 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -68,15 +68,6 @@ public interface Post { 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} @@ -84,15 +75,6 @@ public interface Post { 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) @@ -100,15 +82,6 @@ public interface Post { 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 @@ -116,15 +89,6 @@ public interface 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 diff --git a/src/main/java/net/pterodactylus/sone/data/PostBuilder.java b/src/main/java/net/pterodactylus/sone/data/PostBuilder.java new file mode 100644 index 0000000..00f7354 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/PostBuilder.java @@ -0,0 +1,141 @@ +/* + * 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 . + */ + +package net.pterodactylus.sone.data; + +/** + * Builder for {@link Post} objects. + *

+ * A {@link Post} consists of the following elements: + *

    + *
  • an ID,
  • + *
  • a {@link Sone sender},
  • + *
  • an optional {@link Sone recipient},
  • + *
  • a time,
  • + *
  • and a text.
  • + *
+ * 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 David ‘Bombe’ Roden + */ +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. + *

+ * The following conditions must be met in order for this builder to be + * configured correctly: + *

    + *
  • Exactly one of {@link #randomId()} or {@link #withId(String)} must + * have been called.
  • + *
  • The {@link #from(Sone) sender} must not be {@code null}.
  • + *
  • Exactly one of {@link #currentTime()} or {@link #withTime(long)} must + * have been called.
  • + *
  • The {@link #withText(String) text} must not be {@code null} and must + * contain something other than whitespace.
  • + *
  • 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}.
  • + *
+ * + * @return A new post + * @throws IllegalStateException + * if this builder’s configuration is not valid + */ + public Post build() throws IllegalStateException; + +} diff --git a/src/main/java/net/pterodactylus/sone/data/PostBuilderFactory.java b/src/main/java/net/pterodactylus/sone/data/PostBuilderFactory.java new file mode 100644 index 0000000..845cdf6 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/PostBuilderFactory.java @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +package net.pterodactylus.sone.data; + +/** + * Factory for {@link PostBuilder}s. + * + * @author David ‘Bombe’ Roden + */ +public interface PostBuilderFactory { + + /** + * Creates a new post builder. + * + * @return A new post builder + */ + public PostBuilder newPostBuilder(); + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java new file mode 100644 index 0000000..402b99a --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class DefaultPostBuilderFactory implements PostBuilderFactory { + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder newPostBuilder() { + return new PostBuilderImpl(); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java new file mode 100644 index 0000000..d407133 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java @@ -0,0 +1,151 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java index f45292b..2cc0b00 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java @@ -124,9 +124,12 @@ public class PostImpl implements Post { } /** - * {@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; @@ -141,9 +144,12 @@ public class PostImpl implements Post { } /** - * {@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; @@ -160,9 +166,12 @@ public class PostImpl implements Post { } /** - * {@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; @@ -177,9 +186,12 @@ public class PostImpl implements Post { } /** - * {@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; diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index 9909034..1080818 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -25,6 +25,8 @@ import java.util.logging.Logger; 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; @@ -217,6 +219,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr 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 -- 2.7.4