From: David ‘Bombe’ Roden Date: Fri, 25 Jan 2013 06:23:10 +0000 (+0100) Subject: Merge branch 'less-critical' into run X-Git-Tag: 0.8.5^2~3^2~58 X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=8503021c00d63885288c507d9930fefabd5d7678;hp=59dd0e394fdc50b0d6a62fe29b85a52f8d9d9d21;p=Sone.git Merge branch 'less-critical' into run --- 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 a51247e..33e57c7 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -56,7 +56,11 @@ 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.PostReplyBuilder; +import net.pterodactylus.sone.data.PostReplyBuilderFactory; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Reply; @@ -64,8 +68,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; import net.pterodactylus.sone.freenet.wot.Identity; @@ -85,9 +87,13 @@ import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.thread.NamedThreadFactory; +import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Collections2; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Ordering; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; @@ -99,7 +105,7 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Core extends AbstractService implements SoneProvider, PostProvider { +public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider { /** The logger. */ private static final Logger logger = Logging.getLogger(Core.class); @@ -168,12 +174,18 @@ 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(); /** All known posts. */ private final Set knownPosts = new HashSet(); + /** The post reply builder factory. */ + private final PostReplyBuilderFactory postReplyBuilderFactory; + /** All replies. */ private final Map replies = new HashMap(); @@ -215,9 +227,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * The WebOfTrust updater * @param eventBus * The event bus + * @param postBuilderFactory + * The post builder + * @param postReplyBuilderFactory + * The post reply builder factory */ @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, PostReplyBuilderFactory postReplyBuilderFactory) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; @@ -227,6 +243,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider this.updateChecker = new UpdateChecker(eventBus, freenetInterface); this.webOfTrustUpdater = webOfTrustUpdater; this.eventBus = eventBus; + this.postBuilderFactory = postBuilderFactory; + this.postReplyBuilderFactory = postReplyBuilderFactory; } // @@ -511,35 +529,21 @@ 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(); } /** - * Returns the post with the given ID, optionally creating a new post. - * - * @param postId - * The ID of the post to get - * @param create - * {@code true} it create a new post if no post with the given ID - * exists, {@code false} to return {@code null} - * @return The post, or {@code null} if there is no such post + * {@inheritDoc} */ @Override - public Post getPost(String postId, boolean create) { + public Optional getPost(String postId) { synchronized (posts) { - Post post = posts.get(postId); - if ((post == null) && create) { - post = new PostImpl(postId); - posts.put(postId, post); - } - return post; + return Optional.fromNullable(posts.get(postId)); } } @@ -565,47 +569,42 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } /** - * Returns the reply with the given ID. If there is no reply with the given - * ID yet, a new one is created, unless {@code create} is false in which - * case {@code null} is returned. + * Returns a post reply builder. * - * @param replyId - * The ID of the reply to get - * @param create - * {@code true} to always return a {@link Reply}, {@code false} - * to return {@code null} if no reply can be found - * @return The reply, or {@code null} if there is no such reply + * @return A new post reply builder */ - public PostReply getPostReply(String replyId, boolean create) { + public PostReplyBuilder postReplyBuilder() { + return postReplyBuilderFactory.newPostReplyBuilder(); + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getPostReply(String replyId) { synchronized (replies) { - PostReply reply = replies.get(replyId); - if (create && (reply == null)) { - reply = new PostReplyImpl(replyId); - replies.put(replyId, reply); - } - return reply; + return Optional.fromNullable(replies.get(replyId)); } } /** - * Returns all replies for the given post, order ascending by time. - * - * @param post - * The post to get all replies for - * @return All replies for the given post + * {@inheritDoc} */ - public List getReplies(Post post) { - Set sones = getSones(); - List replies = new ArrayList(); - for (Sone sone : sones) { - for (PostReply reply : sone.getReplies()) { - if (reply.getPost().equals(post)) { - replies.add(reply); - } + @Override + public List getReplies(final Post post) { + return Ordering.from(Reply.TIME_COMPARATOR).sortedCopy(FluentIterable.from(getSones()).transformAndConcat(new Function>() { + + @Override + public Iterable apply(Sone sone) { + return sone.getReplies(); } - } - Collections.sort(replies, Reply.TIME_COMPARATOR); - return replies; + }).filter(new Predicate() { + + @Override + public boolean apply(PostReply reply) { + return post.equals(reply.getPost()); + } + })); } /** @@ -677,9 +676,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider Set posts = new HashSet(); synchronized (bookmarkedPosts) { for (String bookmarkedPostId : bookmarkedPosts) { - Post post = getPost(bookmarkedPostId, false); - if (post != null) { - posts.add(post); + Optional post = getPost(bookmarkedPostId); + if (!post.isPresent()) { + posts.add(post.get()); } } } @@ -878,7 +877,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider synchronized (sones) { final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity); boolean newSone = sone.getRequestUri() == null; - sone.setRequestUri(getSoneUri(identity.getRequestUri())); + sone.setRequestUri(SoneUri.create(identity.getRequestUri())); sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)); if (newSone) { synchronized (knownSones) { @@ -1110,16 +1109,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); } } } @@ -1312,11 +1313,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 = postBuilder().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. */ @@ -1334,7 +1335,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider logger.log(Level.WARNING, "Invalid reply found, aborting load!"); return; } - replies.add(getPostReply(replyId, true).setSone(sone).setPost(getPost(postId)).setTime(replyTime).setText(replyText)); + PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder(); + postReplyBuilder.withId(replyId).from(sone).to(postId).withTime(replyTime).withText(replyText); + replies.add(postReplyBuilder.build()); } /* load post likes. */ @@ -1538,10 +1541,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); } @@ -1657,30 +1662,15 @@ public class Core extends AbstractService implements SoneProvider, PostProvider * @return The created reply */ public PostReply createReply(Sone sone, Post post, String text) { - return createReply(sone, post, System.currentTimeMillis(), text); - } - - /** - * Creates a new reply. - * - * @param sone - * The Sone that creates the reply - * @param post - * The post that this reply refers to - * @param time - * The time of the reply - * @param text - * The text of the reply - * @return The created reply - */ - public PostReply createReply(Sone sone, Post post, long time, String text) { checkNotNull(text, "text must not be null"); checkArgument(text.trim().length() > 0, "text must not be empty"); if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone)); return null; } - final PostReply reply = new PostReplyImpl(sone, post, System.currentTimeMillis(), text.trim()); + PostReplyBuilder postReplyBuilder = postReplyBuilderFactory.newPostReplyBuilder(); + postReplyBuilder.randomId().from(sone).to(post.getId()).currentTime().withText(text.trim()); + final PostReply reply = postReplyBuilder.build(); synchronized (replies) { replies.put(reply.getId(), reply); } @@ -2018,7 +2008,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider for (PostReply reply : sone.getReplies()) { String replyPrefix = sonePrefix + "/Replies/" + replyCounter++; configuration.getStringValue(replyPrefix + "/ID").setValue(reply.getId()); - configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPost().getId()); + configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPostId()); configuration.getLongValue(replyPrefix + "/Time").setValue(reply.getTime()); configuration.getStringValue(replyPrefix + "/Text").setValue(reply.getText()); } @@ -2320,23 +2310,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider } /** - * Generate a Sone URI from the given URI and latest edition. - * - * @param uriString - * The URI to derive the Sone URI from - * @return The derived URI - */ - private static FreenetURI getSoneUri(String uriString) { - try { - FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]); - return uri; - } catch (MalformedURLException mue1) { - logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString), mue1); - return null; - } - } - - /** * Notifies the core that a new {@link OwnIdentity} was added. * * @param ownIdentityAddedEvent diff --git a/src/main/java/net/pterodactylus/sone/core/PostProvider.java b/src/main/java/net/pterodactylus/sone/core/PostProvider.java index c73ad65..19a0e5d 100644 --- a/src/main/java/net/pterodactylus/sone/core/PostProvider.java +++ b/src/main/java/net/pterodactylus/sone/core/PostProvider.java @@ -19,6 +19,8 @@ package net.pterodactylus.sone.core; import net.pterodactylus.sone.data.Post; +import com.google.common.base.Optional; + /** * Interface for objects that can provide {@link Post}s by their ID. * @@ -27,17 +29,12 @@ import net.pterodactylus.sone.data.Post; public interface PostProvider { /** - * Returns the post with the given ID, if it exists. If it does not exist - * and {@code create} is {@code false}, {@code null} is returned; otherwise, - * a new post with the given ID is created and returned. + * Returns the post with the given ID. * * @param postId * The ID of the post to return - * @param create - * {@code true} to create a new post if no post with the given ID - * exists, {@code false} to return {@code null} instead * @return The post with the given ID, or {@code null} */ - public Post getPost(String postId, boolean create); + public Optional getPost(String postId); } diff --git a/src/main/java/net/pterodactylus/sone/core/PostReplyProvider.java b/src/main/java/net/pterodactylus/sone/core/PostReplyProvider.java new file mode 100644 index 0000000..5decdc7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/PostReplyProvider.java @@ -0,0 +1,52 @@ +/* + * Sone - PostReplyProvider.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.core; + +import java.util.List; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.PostReply; + +import com.google.common.base.Optional; + +/** + * Interface for objects that can provide {@link PostReply}s. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyProvider { + + /** + * Returns the reply with the given ID. + * + * @param id + * The ID of the reply to get + * @return The reply, or {@code null} if there is no such reply + */ + public Optional getPostReply(String id); + + /** + * Returns all replies for the given post, order ascending by time. + * + * @param post + * The post to get all replies for + * @return All replies for the given post + */ + public List getReplies(Post 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..cf85d03 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java @@ -31,7 +31,9 @@ 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.PostReplyBuilder; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; @@ -372,11 +374,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)); @@ -403,7 +407,10 @@ public class SoneDownloader extends AbstractService { return null; } try { - replies.add(core.getPostReply(replyId, true).setSone(sone).setPost(core.getPost(replyPostId)).setTime(Long.parseLong(replyTime)).setText(replyText)); + PostReplyBuilder postReplyBuilder = core.postReplyBuilder(); + /* TODO - parse time correctly. */ + postReplyBuilder.withId(replyId).from(sone).to(replyPostId).withTime(Long.parseLong(replyTime)).withText(replyText); + replies.add(postReplyBuilder.build()); } catch (NumberFormatException nfe1) { /* TODO - mark Sone as bad. */ logger.log(Level.WARNING, String.format("Downloaded reply for Sone %s with invalid time: %s", sone, replyTime)); diff --git a/src/main/java/net/pterodactylus/sone/core/SoneUri.java b/src/main/java/net/pterodactylus/sone/core/SoneUri.java new file mode 100644 index 0000000..6c5bf16 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/SoneUri.java @@ -0,0 +1,55 @@ +/* + * Sone - SoneUri.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.core; + +import java.net.MalformedURLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.pterodactylus.util.logging.Logging; +import freenet.keys.FreenetURI; + +/** + * Helper class that creates {@link FreenetURI}s for Sone to insert to and + * request from. + * + * @author David ‘Bombe’ Roden + */ +public class SoneUri { + + /** The logger. */ + private static final Logger logger = Logging.getLogger(SoneUri.class); + + /** + * Generate a Sone URI from the given URI. + * + * @param uri + * The URI to derive the Sone URI from + * @return The derived URI + */ + public static FreenetURI create(String uri) { + try { + return new FreenetURI(uri).setDocName("Sone").setMetaString(new String[0]); + } catch (MalformedURLException mue1) { + /* this should never happen. */ + logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uri), mue1); + return null; + } + } + +} 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/PostReply.java b/src/main/java/net/pterodactylus/sone/data/PostReply.java index a4d9ef3..1dc5f44 100644 --- a/src/main/java/net/pterodactylus/sone/data/PostReply.java +++ b/src/main/java/net/pterodactylus/sone/data/PostReply.java @@ -17,6 +17,9 @@ package net.pterodactylus.sone.data; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; + /** * A reply is like a {@link Post} but can never be posted on its own, it always * refers to another {@link Post}. @@ -26,19 +29,38 @@ package net.pterodactylus.sone.data; public interface PostReply extends Reply { /** + * Filter that selects {@link PostReply}s that have a + * {@link Optional#isPresent() present} {@link #getPost() post}. + */ + public static final Predicate HAS_POST_FILTER = new Predicate() { + + @Override + public boolean apply(PostReply postReply) { + return postReply.getPost().isPresent(); + } + }; + + /** + * Returns the ID of the post this reply refers to. + * + * @return The ID of the post this reply refers to + */ + public String getPostId(); + + /** * Returns the post this reply refers to. * * @return The post this reply refers to */ - public Post getPost(); + public Optional getPost(); /** * Sets the post this reply refers to. * - * @param post - * The post this reply refers to + * @param postId + * The ID of the post to reply to * @return This reply */ - public PostReply setPost(Post post); + public PostReply setPost(String postId); } diff --git a/src/main/java/net/pterodactylus/sone/data/PostReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/PostReplyBuilder.java new file mode 100644 index 0000000..1ba7fd5 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/PostReplyBuilder.java @@ -0,0 +1,59 @@ +/* + * Sone - PostReplyBuilder.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 a {@link PostReply} object. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyBuilder extends ReplyBuilder { + + /** + * Configures this builder to set the given post as post the created reply + * refers to. + * + * @param postId + * The ID of the post the reply refers to + * @return This builder + */ + public PostReplyBuilder to(String postId); + + /** + * Verifies the configuration of this builder and creates a new post reply. + *

+ * The following conditions must be met in order for the configuration to be + * considered valid: + *

    + *
  • 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(String) post} have been set.
  • + *
+ * + * @return The created post reply + * @throws IllegalStateException + * if this builder’s configuration is not valid + */ + public PostReply build() throws IllegalStateException; + +} diff --git a/src/main/java/net/pterodactylus/sone/data/PostReplyBuilderFactory.java b/src/main/java/net/pterodactylus/sone/data/PostReplyBuilderFactory.java new file mode 100644 index 0000000..17a92f3 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/PostReplyBuilderFactory.java @@ -0,0 +1,34 @@ +/* + * Sone - PostReplyBuilderFactory.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 PostReplyBuilder}s. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyBuilderFactory { + + /** + * Creates a new post reply builder. + * + * @return A new post reply builder + */ + public PostReplyBuilder newPostReplyBuilder(); + +} diff --git a/src/main/java/net/pterodactylus/sone/data/ReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/ReplyBuilder.java new file mode 100644 index 0000000..53f3153 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/ReplyBuilder.java @@ -0,0 +1,85 @@ +/* + * Sone - ReplyBuilder.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; + +/** + * Methods that all reply builders need to implement in order to be able to + * create any kind of {@link Reply}. + * + * @param + * The type of the builder + * @author David ‘Bombe’ Roden + */ +public interface ReplyBuilder> { + + /** + * Configures this builder to use a random ID when creating the reply. If + * this method is used, {@link #withId(String)} must not be used. + * + * @return This builder + */ + public B randomId(); + + /** + * Configures this builder to use the given ID when creating the reply. If + * this method is used, {@link #randomId()} must not be used. + * + * @param id + * The ID of the reply + * @return This builder + */ + public B withId(String id); + + /** + * Configures this builder to use the given {@link Sone} as sender of the + * reply. + * + * @param sender + * The sender of the reply + * @return This builder + */ + public B from(Sone sender); + + /** + * Configures this builder to use the current time when creating the reply. + * If this method is used, {@link #withTime(long)} must not be used. + * + * @return This builder + */ + public B currentTime(); + + /** + * Configures this builder to use the given time when creating the reply. If + * this method is used, {@link #currentTime()} must not be used. + * + * @param time + * The time of the reply + * @return This builder + */ + public B withTime(long time); + + /** + * Configures this builder to use the given text when creating the reply. + * + * @param text + * The text of the reply + * @return This builder + */ + public B withText(String text); + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java new file mode 100644 index 0000000..e04e854 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java @@ -0,0 +1,110 @@ +/* + * Sone - ReplyBuilder.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.ReplyBuilder; +import net.pterodactylus.sone.data.Sone; + +/** + * Abstract implementation of a {@link ReplyBuilder}. + * + * @param + * The interface implemented and exposed by the builder + * @author David ‘Bombe’ Roden + */ +public class AbstractReplyBuilder> implements ReplyBuilder { + + /** Whether to use a random ID for the reply. */ + protected boolean randomId; + + /** The ID of the reply. */ + protected String id; + + /** The sender of the reply. */ + protected Sone sender; + + /** Whether to use the current time when creating the reply. */ + protected boolean currentTime; + + /** The time of the reply. */ + protected long time; + + /** The text of the reply. */ + protected String text; + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B randomId() { + this.randomId = true; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B withId(String id) { + this.id = id; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B from(Sone sender) { + this.sender = sender; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B currentTime() { + this.currentTime = true; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B withTime(long time) { + this.time = time; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B withText(String text) { + this.text = text; + return (B) this; + } + +} 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/DefaultPostReplyBuilderFactory.java b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostReplyBuilderFactory.java new file mode 100644 index 0000000..80bc3c7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostReplyBuilderFactory.java @@ -0,0 +1,55 @@ +/* + * Sone - DefaultPostReplyBuilderFactory.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.core.PostProvider; +import net.pterodactylus.sone.data.PostReplyBuilder; +import net.pterodactylus.sone.data.PostReplyBuilderFactory; + +import com.google.inject.Inject; + +/** + * {@link PostReplyBuilderFactory} that creates {@link PostReplyBuilderImpl}s. + * + * @author David ‘Bombe’ Roden + */ +public class DefaultPostReplyBuilderFactory implements PostReplyBuilderFactory { + + /** The post provider. */ + private final PostProvider postProvider; + + /** + * Creates a new default post reply builder factory. + * + * @param postProvider + * The post provider + */ + @Inject + public DefaultPostReplyBuilderFactory(PostProvider postProvider) { + this.postProvider = postProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public PostReplyBuilder newPostReplyBuilder() { + return new PostReplyBuilderImpl(postProvider); + } + +} 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..967de9d --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java @@ -0,0 +1,149 @@ +/* + * 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)) || (!randomId && (id != null)), "exactly one of random ID or custom ID must be set"); + checkState(sender != null, "sender must not be null"); + checkState((currentTime && (time == 0)) || (!currentTime && (time > 0)), "one of current time or custom time must be 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/data/impl/PostReplyBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyBuilderImpl.java new file mode 100644 index 0000000..18ce200 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyBuilderImpl.java @@ -0,0 +1,82 @@ +/* + * Sone - PostReplyBuilderImpl.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.core.PostProvider; +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.data.PostReplyBuilder; + +import org.apache.commons.lang.StringUtils; + +/** + * {@link PostReplyBuilder} implementation that creates {@link PostReplyImpl} + * objects. + * + * @author David ‘Bombe’ Roden + */ +public class PostReplyBuilderImpl extends AbstractReplyBuilder implements PostReplyBuilder { + + /** The post builder. */ + private final PostProvider postProvider; + + /** The ID of the post the created reply refers to. */ + private String postId; + + /** + * Creates a new post reply builder. + * + * @param postProvider + * The post provider + */ + public PostReplyBuilderImpl(PostProvider postProvider) { + this.postProvider = postProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public PostReplyBuilder to(String postId) { + this.postId = postId; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostReply build() { + checkState((randomId && (id == null)) || (!randomId && (id != null)), "either random ID nor custom ID must be set"); + checkState(sender != null, "sender must not be null"); + checkState((currentTime && (time == 0)) || (!currentTime && (time >= 0)), "either current time or custom time must be set"); + checkState(!StringUtils.isBlank(text), "text must not be empty"); + checkState(postId != null, "post must not be null"); + + /* create new post reply. */ + PostReplyImpl postReplyImpl = new PostReplyImpl(postProvider, randomId ? UUID.randomUUID().toString() : id); + postReplyImpl.setSone(sender); + postReplyImpl.setPost(postId); + postReplyImpl.setTime(currentTime ? System.currentTimeMillis() : time); + postReplyImpl.setText(text); + return postReplyImpl; + } +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java index 0e66a8f..fdedaa0 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java @@ -17,11 +17,11 @@ package net.pterodactylus.sone.data.impl; -import java.util.UUID; - +import net.pterodactylus.sone.core.PostProvider; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; -import net.pterodactylus.sone.data.Sone; + +import com.google.common.base.Optional; /** * Simple {@link PostReply} implementation. @@ -30,90 +30,56 @@ import net.pterodactylus.sone.data.Sone; */ public class PostReplyImpl extends ReplyImpl implements PostReply { + /** The post provider. */ + private final PostProvider postProvider; + /** The Post this reply refers to. */ - private volatile Post post; + private volatile String postId; /** * Creates a new reply. * + * @param postProvider + * The post provider * @param id * The ID of the reply */ - public PostReplyImpl(String id) { - this(id, null, null, 0, null); - } - - /** - * Creates a new reply. - * - * @param sone - * The sone that posted the reply - * @param post - * The post to reply to - * @param text - * The text of the reply - */ - public PostReplyImpl(Sone sone, Post post, String text) { - this(sone, post, System.currentTimeMillis(), text); + public PostReplyImpl(PostProvider postProvider, String id) { + super(id); + this.postProvider = postProvider; + this.postId = postId; } - /** - * Creates a new reply- - * - * @param sone - * The sone that posted the reply - * @param post - * The post to reply to - * @param time - * The time of the reply - * @param text - * The text of the reply - */ - public PostReplyImpl(Sone sone, Post post, long time, String text) { - this(UUID.randomUUID().toString(), sone, post, time, text); - } + // + // ACCESSORS + // /** - * Creates a new reply- - * - * @param sone - * The sone that posted the reply - * @param id - * The ID of the reply - * @param post - * The post to reply to - * @param time - * The time of the reply - * @param text - * The text of the reply + * {@inheritDocs} */ - public PostReplyImpl(String id, Sone sone, Post post, long time, String text) { - super(id, sone, time, text); - this.post = post; + @Override + public String getPostId() { + return postId; } - // - // ACCESSORS - // - /** * {@inheritDoc} */ @Override - public Post getPost() { - return post; + public Optional getPost() { + return postProvider.getPost(postId); } /** * Sets the post this reply refers to. * - * @param post - * The post this reply refers to + * @param postId + * The ID of the post to reply to * @return This reply (for method chaining) */ @Override - public PostReply setPost(Post post) { - this.post = post; + public PostReply setPost(String postId) { + this.postId = postId; return this; } diff --git a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java index a483b01..368f49a 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java @@ -33,6 +33,7 @@ import net.pterodactylus.sone.freenet.fcp.Command; import net.pterodactylus.sone.freenet.fcp.FcpException; import net.pterodactylus.sone.template.SoneAccessor; +import com.google.common.base.Optional; import com.google.common.collect.Collections2; import freenet.node.FSParseException; @@ -185,11 +186,11 @@ public abstract class AbstractSoneCommand extends AbstractCommand { protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { try { String postId = simpleFieldSet.getString(parameterName); - Post post = core.getPost(postId, false); - if (post == null) { + Optional post = core.getPost(postId); + if (!post.isPresent()) { throw new FcpException("Could not load post from “" + postId + "”."); } - return post; + return post.get(); } catch (FSParseException fspe1) { throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1); } @@ -211,11 +212,11 @@ public abstract class AbstractSoneCommand extends AbstractCommand { protected PostReply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { try { String replyId = simpleFieldSet.getString(parameterName); - PostReply reply = core.getPostReply(replyId, false); - if (reply == null) { + Optional reply = core.getPostReply(replyId); + if (!reply.isPresent()) { throw new FcpException("Could not load reply from “" + replyId + "”."); } - return reply; + return reply.get(); } catch (FSParseException fspe1) { throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1); } diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index 3ea818f..eb2e434 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -24,7 +24,12 @@ import java.util.logging.Logger; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.FreenetInterface; +import net.pterodactylus.sone.core.PostProvider; import net.pterodactylus.sone.core.WebOfTrustUpdater; +import net.pterodactylus.sone.data.PostBuilderFactory; +import net.pterodactylus.sone.data.PostReplyBuilderFactory; +import net.pterodactylus.sone.data.impl.DefaultPostBuilderFactory; +import net.pterodactylus.sone.data.impl.DefaultPostReplyBuilderFactory; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend; import net.pterodactylus.sone.freenet.plugin.PluginConnector; @@ -218,6 +223,9 @@ 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); + bind(PostReplyBuilderFactory.class).to(DefaultPostReplyBuilderFactory.class).in(Singleton.class); + bind(PostProvider.class).to(Core.class).in(Singleton.class); bindListener(Matchers.any(), new TypeListener() { @Override diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java index 1881a1b..dbf09a3 100644 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java +++ b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java @@ -31,6 +31,8 @@ import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.util.notify.Notification; +import com.google.common.base.Optional; + /** * Filter for {@link ListNotification}s. * @@ -282,11 +284,11 @@ public class ListNotificationFilters { */ public static boolean isReplyVisible(Sone sone, PostReply reply) { checkNotNull(reply, "reply must not be null"); - Post post = reply.getPost(); - if (post == null) { + Optional post = reply.getPost(); + if (!post.isPresent()) { return false; } - if (!isPostVisible(sone, post)) { + if (!isPostVisible(sone, post.get())) { return false; } if (reply.getTime() > System.currentTimeMillis()) { diff --git a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java index 4806cee..bcf73d8 100644 --- a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java @@ -29,6 +29,8 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.util.template.Filter; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; + /** * {@link Filter} implementation that groups replies by the post the are in * reply to, returning a map with the post as key and the list of replies as @@ -48,17 +50,21 @@ public class ReplyGroupFilter implements Filter { Map> postSones = new HashMap>(); Map> postReplies = new HashMap>(); for (PostReply reply : allReplies) { - Post post = reply.getPost(); - Set sones = postSones.get(post); + /* + * All replies from a new-reply notification have posts, + * ListNotificationFilters takes care of that. + */ + Optional post = reply.getPost(); + Set sones = postSones.get(post.get()); if (sones == null) { sones = new HashSet(); - postSones.put(post, sones); + postSones.put(post.get(), sones); } sones.add(reply.getSone()); - Set replies = postReplies.get(post); + Set replies = postReplies.get(post.get()); if (replies == null) { replies = new HashSet(); - postReplies.put(post, replies); + postReplies.put(post.get(), replies); } replies.add(reply); } diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index 5c59912..6e84c70 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -26,6 +26,8 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.PostProvider; import net.pterodactylus.sone.core.SoneProvider; import net.pterodactylus.sone.data.Post; @@ -258,9 +260,9 @@ public class SoneTextParser implements Parser { if (linkType == LinkType.POST) { if (line.length() >= (7 + 36)) { String postId = line.substring(7, 43); - Post post = postProvider.getPost(postId, false); - if ((post != null) && (post.getSone() != null)) { - parts.add(new PostPart(post)); + Optional post = postProvider.getPost(postId); + if (post.isPresent()) { + parts.add(new PostPart(post.get())); } else { parts.add(new PlainTextPart(line.substring(0, 43))); } diff --git a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java index e6a5735..c8979c1 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.text.TextFilter; @@ -58,7 +60,10 @@ public class CreateReplyPage extends SoneTemplatePage { String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim(); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); if (request.getMethod() == Method.POST) { - Post post = webInterface.getCore().getPost(postId); + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { + throw new RedirectException("noPermission.html"); + } if (text.length() > 0) { String senderId = request.getHttpRequest().getPartAsStringFailsafe("sender", 43); Sone sender = webInterface.getCore().getLocalSone(senderId, false); @@ -66,7 +71,7 @@ public class CreateReplyPage extends SoneTemplatePage { sender = getCurrentSone(request.getToadletContext()); } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - webInterface.getCore().createReply(sender, post, text); + webInterface.getCore().createReply(sender, post.get(), text); throw new RedirectException(returnPage); } templateContext.set("errorTextEmpty", true); diff --git a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java index 1363ac9..6ef86f0 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -55,19 +57,22 @@ public class DeletePostPage extends SoneTemplatePage { if (request.getMethod() == Method.GET) { String postId = request.getHttpRequest().getParam("post"); String returnPage = request.getHttpRequest().getParam("returnPage"); - Post post = webInterface.getCore().getPost(postId); - templateContext.set("post", post); + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { + throw new RedirectException("noPermission.html"); + } + templateContext.set("post", post.get()); templateContext.set("returnPage", returnPage); return; } else if (request.getMethod() == Method.POST) { String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); - Post post = webInterface.getCore().getPost(postId); - if (!post.getSone().isLocal()) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent() || !post.get().getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getHttpRequest().isPartSet("confirmDelete")) { - webInterface.getCore().deletePost(post); + webInterface.getCore().deletePost(post.get()); throw new RedirectException(returnPage); } else if (request.getHttpRequest().isPartSet("abortDelete")) { throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java index 8c07716..f7ca132 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java @@ -23,6 +23,8 @@ import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.web.Method; +import com.google.common.base.Optional; + /** * This page lets the user delete a reply. * @@ -53,14 +55,14 @@ public class DeleteReplyPage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36); - PostReply reply = webInterface.getCore().getPostReply(replyId, false); + Optional reply = webInterface.getCore().getPostReply(replyId); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); if (request.getMethod() == Method.POST) { - if (!reply.getSone().isLocal()) { + if (!reply.get().getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getHttpRequest().isPartSet("confirmDelete")) { - webInterface.getCore().deleteReply(reply); + webInterface.getCore().deleteReply(reply.get()); throw new RedirectException(returnPage); } else if (request.getHttpRequest().isPartSet("abortDelete")) { throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java b/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java index d552b91..f183ff4 100644 --- a/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java +++ b/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java @@ -27,6 +27,8 @@ import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; + /** * Page that lets the user mark a number of {@link Sone}s, {@link Post}s, or * {@link Reply Replie}s as known. @@ -65,17 +67,17 @@ public class MarkAsKnownPage extends SoneTemplatePage { for (StringTokenizer idTokenizer = new StringTokenizer(ids); idTokenizer.hasMoreTokens();) { String id = idTokenizer.nextToken(); if (type.equals("post")) { - Post post = webInterface.getCore().getPost(id, false); - if (post == null) { + Optional post = webInterface.getCore().getPost(id); + if (!post.isPresent()) { continue; } - webInterface.getCore().markPostKnown(post); + webInterface.getCore().markPostKnown(post.get()); } else if (type.equals("reply")) { - PostReply reply = webInterface.getCore().getPostReply(id, false); - if (reply == null) { + Optional reply = webInterface.getCore().getPostReply(id); + if (!reply.isPresent()) { continue; } - webInterface.getCore().markReplyKnown(reply); + webInterface.getCore().markReplyKnown(reply.get()); } else if (type.equals("sone")) { Sone sone = webInterface.getCore().getSone(id, false); if (sone == null) { diff --git a/src/main/java/net/pterodactylus/sone/web/NewPage.java b/src/main/java/net/pterodactylus/sone/web/NewPage.java index 2d4ec55..22d27c8 100644 --- a/src/main/java/net/pterodactylus/sone/web/NewPage.java +++ b/src/main/java/net/pterodactylus/sone/web/NewPage.java @@ -67,7 +67,7 @@ public class NewPage extends SoneTemplatePage { /* collect new elements from notifications. */ Set posts = new HashSet(webInterface.getNewPosts()); for (PostReply reply : webInterface.getNewReplies()) { - posts.add(reply.getPost()); + posts.add(reply.getPost().get()); } /* filter and sort them. */ diff --git a/src/main/java/net/pterodactylus/sone/web/SearchPage.java b/src/main/java/net/pterodactylus/sone/web/SearchPage.java index 1bca8cd..6cb2c0c 100644 --- a/src/main/java/net/pterodactylus/sone/web/SearchPage.java +++ b/src/main/java/net/pterodactylus/sone/web/SearchPage.java @@ -44,6 +44,7 @@ import net.pterodactylus.util.text.StringEscaper; import net.pterodactylus.util.text.TextException; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -322,7 +323,7 @@ public class SearchPage extends SoneTemplatePage { */ private String getPostId(String phrase) { String postId = phrase.startsWith("post://") ? phrase.substring(7) : phrase; - return (webInterface.getCore().getPost(postId, false) != null) ? postId : null; + return (webInterface.getCore().getPost(postId) != null) ? postId : null; } /** @@ -336,7 +337,11 @@ public class SearchPage extends SoneTemplatePage { */ private String getReplyPostId(String phrase) { String replyId = phrase.startsWith("reply://") ? phrase.substring(8) : phrase; - return (webInterface.getCore().getPostReply(replyId, false) != null) ? webInterface.getCore().getPostReply(replyId, false).getPost().getId() : null; + Optional postReply = webInterface.getCore().getPostReply(replyId); + if (!postReply.isPresent()) { + return null; + } + return postReply.get().getPostId(); } /** diff --git a/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java b/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java index aaf013d..50719ee 100644 --- a/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java @@ -19,6 +19,8 @@ package net.pterodactylus.sone.web; import java.net.URI; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.template.SoneAccessor; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -54,11 +56,11 @@ public class ViewPostPage extends SoneTemplatePage { @Override protected String getPageTitle(FreenetRequest request) { String postId = request.getHttpRequest().getParam("post"); - Post post = webInterface.getCore().getPost(postId, false); + Optional post = webInterface.getCore().getPost(postId); String title = ""; - if ((post != null) && (post.getSone() != null)) { - title = post.getText().substring(0, Math.min(20, post.getText().length())) + "…"; - title += " - " + SoneAccessor.getNiceName(post.getSone()) + " - "; + if (post.isPresent()) { + title = post.get().getText().substring(0, Math.min(20, post.get().getText().length())) + "…"; + title += " - " + SoneAccessor.getNiceName(post.get().getSone()) + " - "; } title += webInterface.getL10n().getString("Page.ViewPost.Title"); return title; @@ -72,8 +74,8 @@ public class ViewPostPage extends SoneTemplatePage { super.processTemplate(request, templateContext); String postId = request.getHttpRequest().getParam("post"); boolean raw = request.getHttpRequest().getParam("raw").equals("true"); - Post post = webInterface.getCore().getPost(postId); - templateContext.set("post", post); + Optional post = webInterface.getCore().getPost(postId); + templateContext.set("post", post.get()); templateContext.set("raw", raw); } diff --git a/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java b/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java index dec4fe2..5572bca 100644 --- a/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java @@ -36,6 +36,8 @@ import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; + /** * Lets the user browser another Sone. * @@ -95,11 +97,11 @@ public class ViewSonePage extends SoneTemplatePage { Set replies = sone.getReplies(); final Map> repliedPosts = new HashMap>(); for (PostReply reply : replies) { - Post post = reply.getPost(); - if (repliedPosts.containsKey(post) || sone.equals(post.getSone()) || (sone.equals(post.getRecipient()))) { + Optional post = reply.getPost(); + if (!post.isPresent() || repliedPosts.containsKey(post.get()) || sone.equals(post.get().getSone()) || (sone.equals(post.get().getRecipient()))) { continue; } - repliedPosts.put(post, webInterface.getCore().getReplies(post)); + repliedPosts.put(post.get(), webInterface.getCore().getReplies(post.get())); } List posts = new ArrayList(repliedPosts.keySet()); Collections.sort(posts, new Comparator() { diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index d9a9dfb..3bb11d5 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -857,8 +857,8 @@ public class WebInterface { } if (!hasFirstStartNotification()) { notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification); - if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal && (reply.getPost().getSone() != null) && (reply.getTime() <= System.currentTimeMillis())) { - mentionNotification.add(reply.getPost()); + if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal && reply.getPost().isPresent() && (reply.getTime() <= System.currentTimeMillis())) { + mentionNotification.add(reply.getPost().get()); notificationManager.addNotification(mentionNotification); } } else { @@ -900,7 +900,7 @@ public class WebInterface { public void markReplyKnown(MarkPostReplyKnownEvent markPostReplyKnownEvent) { newReplyNotification.remove(markPostReplyKnownEvent.postReply()); localReplyNotification.remove(markPostReplyKnownEvent.postReply()); - mentionNotification.remove(markPostReplyKnownEvent.postReply().getPost()); + mentionNotification.remove(markPostReplyKnownEvent.postReply().getPost().get()); } /** @@ -940,11 +940,11 @@ public class WebInterface { localReplyNotification.remove(reply); if (!getMentionedSones(reply.getText()).isEmpty()) { boolean isMentioned = false; - for (PostReply existingReply : getCore().getReplies(reply.getPost())) { + for (PostReply existingReply : getCore().getReplies(reply.getPost().get())) { isMentioned |= !reply.isKnown() && !getMentionedSones(existingReply.getText()).isEmpty(); } if (!isMentioned) { - mentionNotification.remove(reply.getPost()); + mentionNotification.remove(reply.getPost().get()); } } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java index 164ec19..8a35185 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Sone; @@ -58,12 +60,12 @@ public class CreateReplyAjaxPage extends JsonPage { if (sender == null) { sender = getCurrentSone(request.getToadletContext()); } - Post post = webInterface.getCore().getPost(postId); - if ((post == null) || (post.getSone() == null)) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { return createErrorJsonObject("invalid-post-id"); } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - PostReply reply = webInterface.getCore().createReply(sender, post, text); + PostReply reply = webInterface.getCore().createReply(sender, post.get(), text); return createSuccessJsonObject().put("reply", reply.getId()).put("sone", sender.getId()); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java index a8c36d5..71b0016 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -49,14 +51,14 @@ public class DeletePostAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String postId = request.getHttpRequest().getParam("post"); - Post post = webInterface.getCore().getPost(postId, false); - if ((post == null) || (post.getSone() == null)) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { return createErrorJsonObject("invalid-post-id"); } - if (!post.getSone().isLocal()) { + if (!post.get().getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } - webInterface.getCore().deletePost(post); + webInterface.getCore().deletePost(post.get()); return createSuccessJsonObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java index 67bf901..2aa217d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -49,14 +51,14 @@ public class DeleteReplyAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String replyId = request.getHttpRequest().getParam("reply"); - PostReply reply = webInterface.getCore().getPostReply(replyId, false); - if (reply == null) { + Optional reply = webInterface.getCore().getPostReply(replyId); + if (!reply.isPresent()) { return createErrorJsonObject("invalid-reply-id"); } - if (!reply.getSone().isLocal()) { + if (!reply.get().getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } - webInterface.getCore().deleteReply(reply); + webInterface.getCore().deleteReply(reply.get()); return createSuccessJsonObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java index c1c532b..1a5286f 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java @@ -31,6 +31,8 @@ import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonArray; import net.pterodactylus.util.json.JsonObject; +import com.google.common.base.Optional; + /** * AJAX page that retrieves the number of “likes” a {@link Post} has. * @@ -63,12 +65,18 @@ public class GetLikesAjaxPage extends JsonPage { return createErrorJsonObject("invalid-" + type + "-id"); } if ("post".equals(type)) { - Post post = webInterface.getCore().getPost(id); - Set sones = webInterface.getCore().getLikes(post); + Optional post = webInterface.getCore().getPost(id); + if (!post.isPresent()) { + return createErrorJsonObject("invalid-post-id"); + } + Set sones = webInterface.getCore().getLikes(post.get()); return createSuccessJsonObject().put("likes", sones.size()).put("sones", getSones(sones)); } else if ("reply".equals(type)) { - PostReply reply = webInterface.getCore().getPostReply(id, false); - Set sones = webInterface.getCore().getLikes(reply); + Optional reply = webInterface.getCore().getPostReply(id); + if (!reply.isPresent()) { + return createErrorJsonObject("invalid-reply-id"); + } + Set sones = webInterface.getCore().getLikes(reply.get()); return createSuccessJsonObject().put("likes", sones.size()).put("sones", getSones(sones)); } return createErrorJsonObject("invalid-type"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java index ae813ee..e32573d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java @@ -19,6 +19,8 @@ package net.pterodactylus.sone.web.ajax; import java.io.StringWriter; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -59,11 +61,11 @@ public class GetPostAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String postId = request.getHttpRequest().getParam("post"); - Post post = webInterface.getCore().getPost(postId, false); - if (post == null) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { return createErrorJsonObject("invalid-post-id"); } - return createSuccessJsonObject().put("post", createJsonPost(request, post, getCurrentSone(request.getToadletContext()))); + return createSuccessJsonObject().put("post", createJsonPost(request, post.get(), getCurrentSone(request.getToadletContext()))); } /** diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java index 99a7220..b617608 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java @@ -19,6 +19,8 @@ package net.pterodactylus.sone.web.ajax; import java.io.StringWriter; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -62,11 +64,11 @@ public class GetReplyAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String replyId = request.getHttpRequest().getParam("reply"); - PostReply reply = webInterface.getCore().getPostReply(replyId, false); - if ((reply == null) || (reply.getSone() == null)) { + Optional reply = webInterface.getCore().getPostReply(replyId); + if (!reply.isPresent()) { return createErrorJsonObject("invalid-reply-id"); } - return createSuccessJsonObject().put("reply", createJsonReply(request, reply, getCurrentSone(request.getToadletContext()))); + return createSuccessJsonObject().put("reply", createJsonReply(request, reply.get(), getCurrentSone(request.getToadletContext()))); } /** @@ -95,7 +97,7 @@ public class GetReplyAjaxPage extends JsonPage { private JsonObject createJsonReply(FreenetRequest request, PostReply reply, Sone currentSone) { JsonObject jsonReply = new JsonObject(); jsonReply.put("id", reply.getId()); - jsonReply.put("postId", reply.getPost().getId()); + jsonReply.put("postId", reply.getPostId()); jsonReply.put("soneId", reply.getSone().getId()); jsonReply.put("time", reply.getTime()); StringWriter stringWriter = new StringWriter(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java index 8657a6a..6caa2fd 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java @@ -122,20 +122,14 @@ public class GetStatusAjaxPage extends JsonPage { }); } /* remove replies to unknown posts. */ - newReplies = Collections2.filter(newReplies, new Predicate() { - - @Override - public boolean apply(PostReply reply) { - return (reply.getPost() != null) && (reply.getPost().getSone() != null); - } - }); + newReplies = Collections2.filter(newReplies, PostReply.HAS_POST_FILTER); JsonArray jsonReplies = new JsonArray(); for (PostReply reply : newReplies) { JsonObject jsonReply = new JsonObject(); jsonReply.put("id", reply.getId()); jsonReply.put("sone", reply.getSone().getId()); - jsonReply.put("post", reply.getPost().getId()); - jsonReply.put("postSone", reply.getPost().getSone().getId()); + jsonReply.put("post", reply.getPostId()); + jsonReply.put("postSone", reply.getPost().get().getSone().getId()); jsonReplies.add(jsonReply); } return createSuccessJsonObject().put("loggedIn", currentSone != null).put("options", createJsonOptions(currentSone)).put("sones", jsonSones).put("notificationHash", notifications.hashCode()).put("newPosts", jsonPosts).put("newReplies", jsonReplies); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java index a7f02c9..4f54571 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java @@ -28,6 +28,8 @@ import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; +import com.google.common.base.Optional; + /** * Ajax page that returns a formatted, relative timestamp for replies or posts. * @@ -58,16 +60,16 @@ public class GetTimesAjaxPage extends JsonPage { if (allIds.length() > 0) { String[] ids = allIds.split(","); for (String id : ids) { - Post post = webInterface.getCore().getPost(id, false); - if (post == null) { + Optional post = webInterface.getCore().getPost(id); + if (!post.isPresent()) { continue; } JsonObject postTime = new JsonObject(); - Time time = getTime(post.getTime()); + Time time = getTime(post.get().getTime()); postTime.put("timeText", time.getText()); postTime.put("refreshTime", TimeUnit.MILLISECONDS.toSeconds(time.getRefresh())); synchronized (dateFormat) { - postTime.put("tooltip", dateFormat.format(new Date(post.getTime()))); + postTime.put("tooltip", dateFormat.format(new Date(post.get().getTime()))); } postTimes.put(id, postTime); } @@ -77,16 +79,16 @@ public class GetTimesAjaxPage extends JsonPage { if (allIds.length() > 0) { String[] ids = allIds.split(","); for (String id : ids) { - PostReply reply = webInterface.getCore().getPostReply(id, false); - if (reply == null) { + Optional reply = webInterface.getCore().getPostReply(id); + if (!reply.isPresent()) { continue; } JsonObject replyTime = new JsonObject(); - Time time = getTime(reply.getTime()); + Time time = getTime(reply.get().getTime()); replyTime.put("timeText", time.getText()); replyTime.put("refreshTime", TimeUnit.MILLISECONDS.toSeconds(time.getRefresh())); synchronized (dateFormat) { - replyTime.put("tooltip", dateFormat.format(new Date(reply.getTime()))); + replyTime.put("tooltip", dateFormat.format(new Date(reply.get().getTime()))); } replyTimes.put(id, replyTime); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java index a193d75..997c9dc 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java @@ -26,6 +26,8 @@ import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; +import com.google.common.base.Optional; + /** * AJAX page that lets the user mark a number of {@link Sone}s, {@link Post}s, * or {@link Reply}s as known. @@ -57,17 +59,17 @@ public class MarkAsKnownAjaxPage extends JsonPage { Core core = webInterface.getCore(); for (String id : ids) { if (type.equals("post")) { - Post post = core.getPost(id, false); - if (post == null) { + Optional post = core.getPost(id); + if (!post.isPresent()) { continue; } - core.markPostKnown(post); + core.markPostKnown(post.get()); } else if (type.equals("reply")) { - PostReply reply = core.getPostReply(id, false); - if (reply == null) { + Optional reply = core.getPostReply(id); + if (!reply.isPresent()) { continue; } - core.markReplyKnown(reply); + core.markReplyKnown(reply.get()); } else if (type.equals("sone")) { Sone sone = core.getSone(id, false); if (sone == null) {