From: David ‘Bombe’ Roden Date: Sun, 13 Oct 2013 20:34:00 +0000 (+0200) Subject: Rename SoneImpl to DefaultSone, implement SoneProvider in MemoryDatabase. X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=2f995a29d8731dc3b3d20e78fd9382be542292ff;p=Sone.git Rename SoneImpl to DefaultSone, implement SoneProvider in MemoryDatabase. --- diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 5dc733b..e755414 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -19,7 +19,10 @@ package net.pterodactylus.sone.core; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.FluentIterable.from; import static net.pterodactylus.sone.data.Identified.GET_ID; +import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER; import java.net.MalformedURLException; import java.util.Collection; @@ -62,7 +65,6 @@ import net.pterodactylus.sone.data.Reply; 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.SoneImpl; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.database.DatabaseException; @@ -95,7 +97,6 @@ import freenet.keys.FreenetURI; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; -import com.google.common.collect.FluentIterable; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -352,7 +353,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, @Override public Collection getLocalSones() { synchronized (sones) { - return FluentIterable.from(sones.values()).filter(new Predicate() { + return from(sones.values()).filter(new Predicate() { @Override public boolean apply(Sone sone) { @@ -367,31 +368,17 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * * @param id * The ID of the Sone - * @param create - * {@code true} to create a new Sone if none exists, {@code false} to return - * null if none exists * @return The Sone with the given ID, or {@code null} */ - public Sone getLocalSone(String id, boolean create) { - synchronized (sones) { - Sone sone = sones.get(id); - if ((sone == null) && create) { - sone = new SoneImpl(id, true); - sones.put(id, sone); - } - if ((sone != null) && !sone.isLocal()) { - sone = new SoneImpl(id, true); - sones.put(id, sone); - } - return sone; - } + public Optional getLocalSone(String id) { + return from(database.getSone(id).asSet()).firstMatch(LOCAL_SONE_FILTER); } /** {@inheritDocs} */ @Override public Collection getRemoteSones() { synchronized (sones) { - return FluentIterable.from(sones.values()).filter(new Predicate() { + return from(sones.values()).filter(new Predicate() { @Override public boolean apply(Sone sone) { @@ -406,20 +393,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * * @param id * The ID of the remote Sone to get - * @param create - * {@code true} to always create a Sone, {@code false} to return {@code null} - * if no Sone with the given ID exists * @return The Sone with the given ID */ - public Sone getRemoteSone(String id, boolean create) { - synchronized (sones) { - Sone sone = sones.get(id); - if ((sone == null) && create && (id != null) && (id.length() == 43)) { - sone = new SoneImpl(id, false); - sones.put(id, sone); - } - return sone; - } + public Optional getRemoteSone(String id) { + return from(database.getSone(id).asSet()).firstMatch(not(LOCAL_SONE_FILTER)); } /** @@ -663,7 +640,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, synchronized (sones) { final Sone sone; try { - sone = getLocalSone(ownIdentity.getId(), true).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); + sone = database.newSoneBuilder().by(ownIdentity).local().build().setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); } catch (MalformedURLException mue1) { logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1); return null; @@ -720,7 +697,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return null; } synchronized (sones) { - final Sone sone = getRemoteSone(identity.getId(), true); + final Sone sone = database.newSoneBuilder().by(identity).build(); if (sone.isLocal()) { return sone; } @@ -1716,7 +1693,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); /* save albums. first, collect in a flat structure, top-level first. */ - List albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList(); + List albums = from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList(); int albumCounter = 0; for (Album album : albums) { @@ -1998,14 +1975,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, @Override @SuppressWarnings("synthetic-access") public void run() { - Sone sone = getRemoteSone(identity.getId(), false); - if (sone.isLocal()) { - return; - } - sone.setIdentity(identity); - sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.getLatestEdition())); - soneDownloader.addSone(sone); - soneDownloader.fetchSone(sone); + Optional sone = getRemoteSone(identity.getId()); + sone.get().setIdentity(identity); + sone.get().setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.get().getLatestEdition())); + soneDownloader.addSone(sone.get()); + soneDownloader.fetchSone(sone.get()); } }); } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java index 5621f4b..34ca521 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java @@ -34,7 +34,7 @@ import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; -import net.pterodactylus.sone.data.SoneImpl; +import net.pterodactylus.sone.data.impl.DefaultSone; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostReplyBuilder; import net.pterodactylus.util.io.Closer; @@ -240,7 +240,7 @@ public class SoneDownloader extends AbstractService { return null; } - Sone sone = new SoneImpl(originalSone.getId(), originalSone.isLocal()).setIdentity(originalSone.getIdentity()); + Sone sone = new DefaultSone(originalSone.getId(), originalSone.isLocal()).setIdentity(originalSone.getIdentity()); SimpleXML soneXml; try { diff --git a/src/main/java/net/pterodactylus/sone/data/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/SoneImpl.java deleted file mode 100644 index 1f7ee1a..0000000 --- a/src/main/java/net/pterodactylus/sone/data/SoneImpl.java +++ /dev/null @@ -1,750 +0,0 @@ -/* - * Sone - SoneImpl.java - Copyright © 2010–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; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.pterodactylus.sone.core.Options; -import net.pterodactylus.sone.data.impl.DefaultAlbum; -import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.util.logging.Logging; - -import freenet.keys.FreenetURI; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; - -/** - * {@link Sone} implementation. - *

- * Operations that modify the Sone need to synchronize on the Sone in question. - * - * @author David ‘Bombe’ Roden - */ -public class SoneImpl implements Sone { - - /** The logger. */ - private static final Logger logger = Logging.getLogger(SoneImpl.class); - - /** The ID of this Sone. */ - private final String id; - - /** Whether the Sone is local. */ - private final boolean local; - - /** The identity of this Sone. */ - private Identity identity; - - /** The URI under which the Sone is stored in Freenet. */ - private volatile FreenetURI requestUri; - - /** The URI used to insert a new version of this Sone. */ - /* This will be null for remote Sones! */ - private volatile FreenetURI insertUri; - - /** The latest edition of the Sone. */ - private volatile long latestEdition; - - /** The time of the last inserted update. */ - private volatile long time; - - /** The status of this Sone. */ - private volatile SoneStatus status = SoneStatus.unknown; - - /** The profile of this Sone. */ - private volatile Profile profile = new Profile(this); - - /** The client used by the Sone. */ - private volatile Client client; - - /** Whether this Sone is known. */ - private volatile boolean known; - - /** All friend Sones. */ - private final Set friendSones = new CopyOnWriteArraySet(); - - /** All posts. */ - private final Set posts = new CopyOnWriteArraySet(); - - /** All replies. */ - private final Set replies = new CopyOnWriteArraySet(); - - /** The IDs of all liked posts. */ - private final Set likedPostIds = new CopyOnWriteArraySet(); - - /** The IDs of all liked replies. */ - private final Set likedReplyIds = new CopyOnWriteArraySet(); - - /** The root album containing all albums. */ - private final Album rootAlbum = new DefaultAlbum(this, null); - - /** Sone-specific options. */ - private Options options = new Options(); - - /** - * Creates a new Sone. - * - * @param id - * The ID of the Sone - * @param local - * {@code true} if the Sone is a local Sone, {@code false} otherwise - */ - public SoneImpl(String id, boolean local) { - this.id = id; - this.local = local; - } - - // - // ACCESSORS - // - - /** - * Returns the identity of this Sone. - * - * @return The identity of this Sone - */ - public String getId() { - return id; - } - - /** - * Returns the identity of this Sone. - * - * @return The identity of this Sone - */ - public Identity getIdentity() { - return identity; - } - - /** - * Sets the identity of this Sone. The {@link Identity#getId() ID} of the - * identity has to match this Sone’s {@link #getId()}. - * - * @param identity - * The identity of this Sone - * @return This Sone (for method chaining) - * @throws IllegalArgumentException - * if the ID of the identity does not match this Sone’s ID - */ - public SoneImpl setIdentity(Identity identity) throws IllegalArgumentException { - if (!identity.getId().equals(id)) { - throw new IllegalArgumentException("Identity’s ID does not match Sone’s ID!"); - } - this.identity = identity; - return this; - } - - /** - * Returns the name of this Sone. - * - * @return The name of this Sone - */ - public String getName() { - return (identity != null) ? identity.getNickname() : null; - } - - /** - * Returns whether this Sone is a local Sone. - * - * @return {@code true} if this Sone is a local Sone, {@code false} otherwise - */ - public boolean isLocal() { - return local; - } - - /** - * Returns the request URI of this Sone. - * - * @return The request URI of this Sone - */ - public FreenetURI getRequestUri() { - return (requestUri != null) ? requestUri.setSuggestedEdition(latestEdition) : null; - } - - /** - * Sets the request URI of this Sone. - * - * @param requestUri - * The request URI of this Sone - * @return This Sone (for method chaining) - */ - public Sone setRequestUri(FreenetURI requestUri) { - if (this.requestUri == null) { - this.requestUri = requestUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]); - return this; - } - if (!this.requestUri.equalsKeypair(requestUri)) { - logger.log(Level.WARNING, String.format("Request URI %s tried to overwrite %s!", requestUri, this.requestUri)); - return this; - } - return this; - } - - /** - * Returns the insert URI of this Sone. - * - * @return The insert URI of this Sone - */ - public FreenetURI getInsertUri() { - return (insertUri != null) ? insertUri.setSuggestedEdition(latestEdition) : null; - } - - /** - * Sets the insert URI of this Sone. - * - * @param insertUri - * The insert URI of this Sone - * @return This Sone (for method chaining) - */ - public Sone setInsertUri(FreenetURI insertUri) { - if (this.insertUri == null) { - this.insertUri = insertUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]); - return this; - } - if (!this.insertUri.equalsKeypair(insertUri)) { - logger.log(Level.WARNING, String.format("Request URI %s tried to overwrite %s!", insertUri, this.insertUri)); - return this; - } - return this; - } - - /** - * Returns the latest edition of this Sone. - * - * @return The latest edition of this Sone - */ - public long getLatestEdition() { - return latestEdition; - } - - /** - * Sets the latest edition of this Sone. If the given latest edition is not - * greater than the current latest edition, the latest edition of this Sone is - * not changed. - * - * @param latestEdition - * The latest edition of this Sone - */ - public void setLatestEdition(long latestEdition) { - if (!(latestEdition > this.latestEdition)) { - logger.log(Level.FINE, String.format("New latest edition %d is not greater than current latest edition %d!", latestEdition, this.latestEdition)); - return; - } - this.latestEdition = latestEdition; - } - - /** - * Return the time of the last inserted update of this Sone. - * - * @return The time of the update (in milliseconds since Jan 1, 1970 UTC) - */ - public long getTime() { - return time; - } - - /** - * Sets the time of the last inserted update of this Sone. - * - * @param time - * The time of the update (in milliseconds since Jan 1, 1970 UTC) - * @return This Sone (for method chaining) - */ - public Sone setTime(long time) { - this.time = time; - return this; - } - - /** - * Returns the status of this Sone. - * - * @return The status of this Sone - */ - public SoneStatus getStatus() { - return status; - } - - /** - * Sets the new status of this Sone. - * - * @param status - * The new status of this Sone - * @return This Sone - * @throws IllegalArgumentException - * if {@code status} is {@code null} - */ - public Sone setStatus(SoneStatus status) { - this.status = checkNotNull(status, "status must not be null"); - return this; - } - - /** - * Returns a copy of the profile. If you want to update values in the profile - * of this Sone, update the values in the returned {@link Profile} and use - * {@link #setProfile(Profile)} to change the profile in this Sone. - * - * @return A copy of the profile - */ - public Profile getProfile() { - return new Profile(profile); - } - - /** - * Sets the profile of this Sone. A copy of the given profile is stored so that - * subsequent modifications of the given profile are not reflected in this - * Sone! - * - * @param profile - * The profile to set - */ - public void setProfile(Profile profile) { - this.profile = new Profile(profile); - } - - /** - * Returns the client used by this Sone. - * - * @return The client used by this Sone, or {@code null} - */ - public Client getClient() { - return client; - } - - /** - * Sets the client used by this Sone. - * - * @param client - * The client used by this Sone, or {@code null} - * @return This Sone (for method chaining) - */ - public Sone setClient(Client client) { - this.client = client; - return this; - } - - /** - * Returns whether this Sone is known. - * - * @return {@code true} if this Sone is known, {@code false} otherwise - */ - public boolean isKnown() { - return known; - } - - /** - * Sets whether this Sone is known. - * - * @param known - * {@code true} if this Sone is known, {@code false} otherwise - * @return This Sone - */ - public Sone setKnown(boolean known) { - this.known = known; - return this; - } - - /** - * Returns all friend Sones of this Sone. - * - * @return The friend Sones of this Sone - */ - public List getFriends() { - List friends = new ArrayList(friendSones); - return friends; - } - - /** - * Returns whether this Sone has the given Sone as a friend Sone. - * - * @param friendSoneId - * The ID of the Sone to check for - * @return {@code true} if this Sone has the given Sone as a friend, {@code - * false} otherwise - */ - public boolean hasFriend(String friendSoneId) { - return friendSones.contains(friendSoneId); - } - - /** - * Adds the given Sone as a friend Sone. - * - * @param friendSone - * The friend Sone to add - * @return This Sone (for method chaining) - */ - public Sone addFriend(String friendSone) { - if (!friendSone.equals(id)) { - friendSones.add(friendSone); - } - return this; - } - - /** - * Removes the given Sone as a friend Sone. - * - * @param friendSoneId - * The ID of the friend Sone to remove - * @return This Sone (for method chaining) - */ - public Sone removeFriend(String friendSoneId) { - friendSones.remove(friendSoneId); - return this; - } - - /** - * Returns the list of posts of this Sone, sorted by time, newest first. - * - * @return All posts of this Sone - */ - public List getPosts() { - List sortedPosts; - synchronized (this) { - sortedPosts = new ArrayList(posts); - } - Collections.sort(sortedPosts, Post.TIME_COMPARATOR); - return sortedPosts; - } - - /** - * Sets all posts of this Sone at once. - * - * @param posts - * The new (and only) posts of this Sone - * @return This Sone (for method chaining) - */ - public Sone setPosts(Collection posts) { - synchronized (this) { - this.posts.clear(); - this.posts.addAll(posts); - } - return this; - } - - /** - * Adds the given post to this Sone. The post will not be added if its {@link - * Post#getSone() Sone} is not this Sone. - * - * @param post - * The post to add - */ - public void addPost(Post post) { - if (post.getSone().equals(this) && posts.add(post)) { - logger.log(Level.FINEST, String.format("Adding %s to “%s”.", post, getName())); - } - } - - /** - * Removes the given post from this Sone. - * - * @param post - * The post to remove - */ - public void removePost(Post post) { - if (post.getSone().equals(this)) { - posts.remove(post); - } - } - - /** - * Returns all replies this Sone made. - * - * @return All replies this Sone made - */ - public Set getReplies() { - return Collections.unmodifiableSet(replies); - } - - /** - * Sets all replies of this Sone at once. - * - * @param replies - * The new (and only) replies of this Sone - * @return This Sone (for method chaining) - */ - public Sone setReplies(Collection replies) { - this.replies.clear(); - this.replies.addAll(replies); - return this; - } - - /** - * Adds a reply to this Sone. If the given reply was not made by this Sone, - * nothing is added to this Sone. - * - * @param reply - * The reply to add - */ - public void addReply(PostReply reply) { - if (reply.getSone().equals(this)) { - replies.add(reply); - } - } - - /** - * Removes a reply from this Sone. - * - * @param reply - * The reply to remove - */ - public void removeReply(PostReply reply) { - if (reply.getSone().equals(this)) { - replies.remove(reply); - } - } - - /** - * Returns the IDs of all liked posts. - * - * @return All liked posts’ IDs - */ - public Set getLikedPostIds() { - return Collections.unmodifiableSet(likedPostIds); - } - - /** - * Sets the IDs of all liked posts. - * - * @param likedPostIds - * All liked posts’ IDs - * @return This Sone (for method chaining) - */ - public Sone setLikePostIds(Set likedPostIds) { - this.likedPostIds.clear(); - this.likedPostIds.addAll(likedPostIds); - return this; - } - - /** - * Checks whether the given post ID is liked by this Sone. - * - * @param postId - * The ID of the post - * @return {@code true} if this Sone likes the given post, {@code false} - * otherwise - */ - public boolean isLikedPostId(String postId) { - return likedPostIds.contains(postId); - } - - /** - * Adds the given post ID to the list of posts this Sone likes. - * - * @param postId - * The ID of the post - * @return This Sone (for method chaining) - */ - public Sone addLikedPostId(String postId) { - likedPostIds.add(postId); - return this; - } - - /** - * Removes the given post ID from the list of posts this Sone likes. - * - * @param postId - * The ID of the post - * @return This Sone (for method chaining) - */ - public Sone removeLikedPostId(String postId) { - likedPostIds.remove(postId); - return this; - } - - /** - * Returns the IDs of all liked replies. - * - * @return All liked replies’ IDs - */ - public Set getLikedReplyIds() { - return Collections.unmodifiableSet(likedReplyIds); - } - - /** - * Sets the IDs of all liked replies. - * - * @param likedReplyIds - * All liked replies’ IDs - * @return This Sone (for method chaining) - */ - public Sone setLikeReplyIds(Set likedReplyIds) { - this.likedReplyIds.clear(); - this.likedReplyIds.addAll(likedReplyIds); - return this; - } - - /** - * Checks whether the given reply ID is liked by this Sone. - * - * @param replyId - * The ID of the reply - * @return {@code true} if this Sone likes the given reply, {@code false} - * otherwise - */ - public boolean isLikedReplyId(String replyId) { - return likedReplyIds.contains(replyId); - } - - /** - * Adds the given reply ID to the list of replies this Sone likes. - * - * @param replyId - * The ID of the reply - * @return This Sone (for method chaining) - */ - public Sone addLikedReplyId(String replyId) { - likedReplyIds.add(replyId); - return this; - } - - /** - * Removes the given post ID from the list of replies this Sone likes. - * - * @param replyId - * The ID of the reply - * @return This Sone (for method chaining) - */ - public Sone removeLikedReplyId(String replyId) { - likedReplyIds.remove(replyId); - return this; - } - - /** - * Returns the root album that contains all visible albums of this Sone. - * - * @return The root album of this Sone - */ - public Album getRootAlbum() { - return rootAlbum; - } - - /** - * Returns Sone-specific options. - * - * @return The options of this Sone - */ - public Options getOptions() { - return options; - } - - /** - * Sets the options of this Sone. - * - * @param options - * The options of this Sone - */ - /* TODO - remove this method again, maybe add an option provider */ - public void setOptions(Options options) { - this.options = options; - } - - // - // FINGERPRINTABLE METHODS - // - - /** {@inheritDoc} */ - @Override - public synchronized String getFingerprint() { - Hasher hash = Hashing.sha256().newHasher(); - hash.putString(profile.getFingerprint()); - - hash.putString("Posts("); - for (Post post : getPosts()) { - hash.putString("Post(").putString(post.getId()).putString(")"); - } - hash.putString(")"); - - List replies = new ArrayList(getReplies()); - Collections.sort(replies, Reply.TIME_COMPARATOR); - hash.putString("Replies("); - for (PostReply reply : replies) { - hash.putString("Reply(").putString(reply.getId()).putString(")"); - } - hash.putString(")"); - - List likedPostIds = new ArrayList(getLikedPostIds()); - Collections.sort(likedPostIds); - hash.putString("LikedPosts("); - for (String likedPostId : likedPostIds) { - hash.putString("Post(").putString(likedPostId).putString(")"); - } - hash.putString(")"); - - List likedReplyIds = new ArrayList(getLikedReplyIds()); - Collections.sort(likedReplyIds); - hash.putString("LikedReplies("); - for (String likedReplyId : likedReplyIds) { - hash.putString("Reply(").putString(likedReplyId).putString(")"); - } - hash.putString(")"); - - hash.putString("Albums("); - for (Album album : rootAlbum.getAlbums()) { - if (!Album.NOT_EMPTY.apply(album)) { - continue; - } - hash.putString(album.getFingerprint()); - } - hash.putString(")"); - - return hash.hash().toString(); - } - - // - // INTERFACE Comparable - // - - /** {@inheritDoc} */ - @Override - public int compareTo(Sone sone) { - return NICE_NAME_COMPARATOR.compare(this, sone); - } - - // - // OBJECT METHODS - // - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return id.hashCode(); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object object) { - if (!(object instanceof Sone)) { - return false; - } - return ((Sone) object).getId().equals(id); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + "),albums(" + getRootAlbum().getAlbums().size() + ")]"; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java b/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java new file mode 100644 index 0000000..f519ffa --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java @@ -0,0 +1,754 @@ +/* + * Sone - SoneImpl.java - Copyright © 2010–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.checkNotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.pterodactylus.sone.core.Options; +import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.Client; +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.data.Profile; +import net.pterodactylus.sone.data.Reply; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.util.logging.Logging; + +import freenet.keys.FreenetURI; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + +/** + * Dumb, store-everything-in-memory {@link Sone} implementation. + * + * @author David ‘Bombe’ Roden + */ +public class DefaultSone implements Sone { + + /** The logger. */ + private static final Logger logger = Logging.getLogger(DefaultSone.class); + + /** The ID of this Sone. */ + private final String id; + + /** Whether the Sone is local. */ + private final boolean local; + + /** The identity of this Sone. */ + private Identity identity; + + /** The URI under which the Sone is stored in Freenet. */ + private volatile FreenetURI requestUri; + + /** The URI used to insert a new version of this Sone. */ + /* This will be null for remote Sones! */ + private volatile FreenetURI insertUri; + + /** The latest edition of the Sone. */ + private volatile long latestEdition; + + /** The time of the last inserted update. */ + private volatile long time; + + /** The status of this Sone. */ + private volatile SoneStatus status = SoneStatus.unknown; + + /** The profile of this Sone. */ + private volatile Profile profile = new Profile(this); + + /** The client used by the Sone. */ + private volatile Client client; + + /** Whether this Sone is known. */ + private volatile boolean known; + + /** All friend Sones. */ + private final Set friendSones = new CopyOnWriteArraySet(); + + /** All posts. */ + private final Set posts = new CopyOnWriteArraySet(); + + /** All replies. */ + private final Set replies = new CopyOnWriteArraySet(); + + /** The IDs of all liked posts. */ + private final Set likedPostIds = new CopyOnWriteArraySet(); + + /** The IDs of all liked replies. */ + private final Set likedReplyIds = new CopyOnWriteArraySet(); + + /** The root album containing all albums. */ + private final Album rootAlbum = new DefaultAlbum(this, null); + + /** Sone-specific options. */ + private Options options = new Options(); + + /** + * Creates a new Sone. + * + * @param id + * The ID of the Sone + * @param local + * {@code true} if the Sone is a local Sone, {@code false} otherwise + */ + public DefaultSone(String id, boolean local) { + this.id = id; + this.local = local; + } + + // + // ACCESSORS + // + + /** + * Returns the identity of this Sone. + * + * @return The identity of this Sone + */ + public String getId() { + return id; + } + + /** + * Returns the identity of this Sone. + * + * @return The identity of this Sone + */ + public Identity getIdentity() { + return identity; + } + + /** + * Sets the identity of this Sone. The {@link Identity#getId() ID} of the + * identity has to match this Sone’s {@link #getId()}. + * + * @param identity + * The identity of this Sone + * @return This Sone (for method chaining) + * @throws IllegalArgumentException + * if the ID of the identity does not match this Sone’s ID + */ + public DefaultSone setIdentity(Identity identity) throws IllegalArgumentException { + if (!identity.getId().equals(id)) { + throw new IllegalArgumentException("Identity’s ID does not match Sone’s ID!"); + } + this.identity = identity; + return this; + } + + /** + * Returns the name of this Sone. + * + * @return The name of this Sone + */ + public String getName() { + return (identity != null) ? identity.getNickname() : null; + } + + /** + * Returns whether this Sone is a local Sone. + * + * @return {@code true} if this Sone is a local Sone, {@code false} otherwise + */ + public boolean isLocal() { + return local; + } + + /** + * Returns the request URI of this Sone. + * + * @return The request URI of this Sone + */ + public FreenetURI getRequestUri() { + return (requestUri != null) ? requestUri.setSuggestedEdition(latestEdition) : null; + } + + /** + * Sets the request URI of this Sone. + * + * @param requestUri + * The request URI of this Sone + * @return This Sone (for method chaining) + */ + public Sone setRequestUri(FreenetURI requestUri) { + if (this.requestUri == null) { + this.requestUri = requestUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]); + return this; + } + if (!this.requestUri.equalsKeypair(requestUri)) { + logger.log(Level.WARNING, String.format("Request URI %s tried to overwrite %s!", requestUri, this.requestUri)); + return this; + } + return this; + } + + /** + * Returns the insert URI of this Sone. + * + * @return The insert URI of this Sone + */ + public FreenetURI getInsertUri() { + return (insertUri != null) ? insertUri.setSuggestedEdition(latestEdition) : null; + } + + /** + * Sets the insert URI of this Sone. + * + * @param insertUri + * The insert URI of this Sone + * @return This Sone (for method chaining) + */ + public Sone setInsertUri(FreenetURI insertUri) { + if (this.insertUri == null) { + this.insertUri = insertUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]); + return this; + } + if (!this.insertUri.equalsKeypair(insertUri)) { + logger.log(Level.WARNING, String.format("Request URI %s tried to overwrite %s!", insertUri, this.insertUri)); + return this; + } + return this; + } + + /** + * Returns the latest edition of this Sone. + * + * @return The latest edition of this Sone + */ + public long getLatestEdition() { + return latestEdition; + } + + /** + * Sets the latest edition of this Sone. If the given latest edition is not + * greater than the current latest edition, the latest edition of this Sone is + * not changed. + * + * @param latestEdition + * The latest edition of this Sone + */ + public void setLatestEdition(long latestEdition) { + if (!(latestEdition > this.latestEdition)) { + logger.log(Level.FINE, String.format("New latest edition %d is not greater than current latest edition %d!", latestEdition, this.latestEdition)); + return; + } + this.latestEdition = latestEdition; + } + + /** + * Return the time of the last inserted update of this Sone. + * + * @return The time of the update (in milliseconds since Jan 1, 1970 UTC) + */ + public long getTime() { + return time; + } + + /** + * Sets the time of the last inserted update of this Sone. + * + * @param time + * The time of the update (in milliseconds since Jan 1, 1970 UTC) + * @return This Sone (for method chaining) + */ + public Sone setTime(long time) { + this.time = time; + return this; + } + + /** + * Returns the status of this Sone. + * + * @return The status of this Sone + */ + public SoneStatus getStatus() { + return status; + } + + /** + * Sets the new status of this Sone. + * + * @param status + * The new status of this Sone + * @return This Sone + * @throws IllegalArgumentException + * if {@code status} is {@code null} + */ + public Sone setStatus(SoneStatus status) { + this.status = checkNotNull(status, "status must not be null"); + return this; + } + + /** + * Returns a copy of the profile. If you want to update values in the profile + * of this Sone, update the values in the returned {@link Profile} and use + * {@link #setProfile(Profile)} to change the profile in this Sone. + * + * @return A copy of the profile + */ + public Profile getProfile() { + return new Profile(profile); + } + + /** + * Sets the profile of this Sone. A copy of the given profile is stored so that + * subsequent modifications of the given profile are not reflected in this + * Sone! + * + * @param profile + * The profile to set + */ + public void setProfile(Profile profile) { + this.profile = new Profile(profile); + } + + /** + * Returns the client used by this Sone. + * + * @return The client used by this Sone, or {@code null} + */ + public Client getClient() { + return client; + } + + /** + * Sets the client used by this Sone. + * + * @param client + * The client used by this Sone, or {@code null} + * @return This Sone (for method chaining) + */ + public Sone setClient(Client client) { + this.client = client; + return this; + } + + /** + * Returns whether this Sone is known. + * + * @return {@code true} if this Sone is known, {@code false} otherwise + */ + public boolean isKnown() { + return known; + } + + /** + * Sets whether this Sone is known. + * + * @param known + * {@code true} if this Sone is known, {@code false} otherwise + * @return This Sone + */ + public Sone setKnown(boolean known) { + this.known = known; + return this; + } + + /** + * Returns all friend Sones of this Sone. + * + * @return The friend Sones of this Sone + */ + public List getFriends() { + List friends = new ArrayList(friendSones); + return friends; + } + + /** + * Returns whether this Sone has the given Sone as a friend Sone. + * + * @param friendSoneId + * The ID of the Sone to check for + * @return {@code true} if this Sone has the given Sone as a friend, {@code + * false} otherwise + */ + public boolean hasFriend(String friendSoneId) { + return friendSones.contains(friendSoneId); + } + + /** + * Adds the given Sone as a friend Sone. + * + * @param friendSone + * The friend Sone to add + * @return This Sone (for method chaining) + */ + public Sone addFriend(String friendSone) { + if (!friendSone.equals(id)) { + friendSones.add(friendSone); + } + return this; + } + + /** + * Removes the given Sone as a friend Sone. + * + * @param friendSoneId + * The ID of the friend Sone to remove + * @return This Sone (for method chaining) + */ + public Sone removeFriend(String friendSoneId) { + friendSones.remove(friendSoneId); + return this; + } + + /** + * Returns the list of posts of this Sone, sorted by time, newest first. + * + * @return All posts of this Sone + */ + public List getPosts() { + List sortedPosts; + synchronized (this) { + sortedPosts = new ArrayList(posts); + } + Collections.sort(sortedPosts, Post.TIME_COMPARATOR); + return sortedPosts; + } + + /** + * Sets all posts of this Sone at once. + * + * @param posts + * The new (and only) posts of this Sone + * @return This Sone (for method chaining) + */ + public Sone setPosts(Collection posts) { + synchronized (this) { + this.posts.clear(); + this.posts.addAll(posts); + } + return this; + } + + /** + * Adds the given post to this Sone. The post will not be added if its {@link + * Post#getSone() Sone} is not this Sone. + * + * @param post + * The post to add + */ + public void addPost(Post post) { + if (post.getSone().equals(this) && posts.add(post)) { + logger.log(Level.FINEST, String.format("Adding %s to “%s”.", post, getName())); + } + } + + /** + * Removes the given post from this Sone. + * + * @param post + * The post to remove + */ + public void removePost(Post post) { + if (post.getSone().equals(this)) { + posts.remove(post); + } + } + + /** + * Returns all replies this Sone made. + * + * @return All replies this Sone made + */ + public Set getReplies() { + return Collections.unmodifiableSet(replies); + } + + /** + * Sets all replies of this Sone at once. + * + * @param replies + * The new (and only) replies of this Sone + * @return This Sone (for method chaining) + */ + public Sone setReplies(Collection replies) { + this.replies.clear(); + this.replies.addAll(replies); + return this; + } + + /** + * Adds a reply to this Sone. If the given reply was not made by this Sone, + * nothing is added to this Sone. + * + * @param reply + * The reply to add + */ + public void addReply(PostReply reply) { + if (reply.getSone().equals(this)) { + replies.add(reply); + } + } + + /** + * Removes a reply from this Sone. + * + * @param reply + * The reply to remove + */ + public void removeReply(PostReply reply) { + if (reply.getSone().equals(this)) { + replies.remove(reply); + } + } + + /** + * Returns the IDs of all liked posts. + * + * @return All liked posts’ IDs + */ + public Set getLikedPostIds() { + return Collections.unmodifiableSet(likedPostIds); + } + + /** + * Sets the IDs of all liked posts. + * + * @param likedPostIds + * All liked posts’ IDs + * @return This Sone (for method chaining) + */ + public Sone setLikePostIds(Set likedPostIds) { + this.likedPostIds.clear(); + this.likedPostIds.addAll(likedPostIds); + return this; + } + + /** + * Checks whether the given post ID is liked by this Sone. + * + * @param postId + * The ID of the post + * @return {@code true} if this Sone likes the given post, {@code false} + * otherwise + */ + public boolean isLikedPostId(String postId) { + return likedPostIds.contains(postId); + } + + /** + * Adds the given post ID to the list of posts this Sone likes. + * + * @param postId + * The ID of the post + * @return This Sone (for method chaining) + */ + public Sone addLikedPostId(String postId) { + likedPostIds.add(postId); + return this; + } + + /** + * Removes the given post ID from the list of posts this Sone likes. + * + * @param postId + * The ID of the post + * @return This Sone (for method chaining) + */ + public Sone removeLikedPostId(String postId) { + likedPostIds.remove(postId); + return this; + } + + /** + * Returns the IDs of all liked replies. + * + * @return All liked replies’ IDs + */ + public Set getLikedReplyIds() { + return Collections.unmodifiableSet(likedReplyIds); + } + + /** + * Sets the IDs of all liked replies. + * + * @param likedReplyIds + * All liked replies’ IDs + * @return This Sone (for method chaining) + */ + public Sone setLikeReplyIds(Set likedReplyIds) { + this.likedReplyIds.clear(); + this.likedReplyIds.addAll(likedReplyIds); + return this; + } + + /** + * Checks whether the given reply ID is liked by this Sone. + * + * @param replyId + * The ID of the reply + * @return {@code true} if this Sone likes the given reply, {@code false} + * otherwise + */ + public boolean isLikedReplyId(String replyId) { + return likedReplyIds.contains(replyId); + } + + /** + * Adds the given reply ID to the list of replies this Sone likes. + * + * @param replyId + * The ID of the reply + * @return This Sone (for method chaining) + */ + public Sone addLikedReplyId(String replyId) { + likedReplyIds.add(replyId); + return this; + } + + /** + * Removes the given post ID from the list of replies this Sone likes. + * + * @param replyId + * The ID of the reply + * @return This Sone (for method chaining) + */ + public Sone removeLikedReplyId(String replyId) { + likedReplyIds.remove(replyId); + return this; + } + + /** + * Returns the root album that contains all visible albums of this Sone. + * + * @return The root album of this Sone + */ + public Album getRootAlbum() { + return rootAlbum; + } + + /** + * Returns Sone-specific options. + * + * @return The options of this Sone + */ + public Options getOptions() { + return options; + } + + /** + * Sets the options of this Sone. + * + * @param options + * The options of this Sone + */ + /* TODO - remove this method again, maybe add an option provider */ + public void setOptions(Options options) { + this.options = options; + } + + // + // FINGERPRINTABLE METHODS + // + + /** {@inheritDoc} */ + @Override + public synchronized String getFingerprint() { + Hasher hash = Hashing.sha256().newHasher(); + hash.putString(profile.getFingerprint()); + + hash.putString("Posts("); + for (Post post : getPosts()) { + hash.putString("Post(").putString(post.getId()).putString(")"); + } + hash.putString(")"); + + List replies = new ArrayList(getReplies()); + Collections.sort(replies, Reply.TIME_COMPARATOR); + hash.putString("Replies("); + for (PostReply reply : replies) { + hash.putString("Reply(").putString(reply.getId()).putString(")"); + } + hash.putString(")"); + + List likedPostIds = new ArrayList(getLikedPostIds()); + Collections.sort(likedPostIds); + hash.putString("LikedPosts("); + for (String likedPostId : likedPostIds) { + hash.putString("Post(").putString(likedPostId).putString(")"); + } + hash.putString(")"); + + List likedReplyIds = new ArrayList(getLikedReplyIds()); + Collections.sort(likedReplyIds); + hash.putString("LikedReplies("); + for (String likedReplyId : likedReplyIds) { + hash.putString("Reply(").putString(likedReplyId).putString(")"); + } + hash.putString(")"); + + hash.putString("Albums("); + for (Album album : rootAlbum.getAlbums()) { + if (!Album.NOT_EMPTY.apply(album)) { + continue; + } + hash.putString(album.getFingerprint()); + } + hash.putString(")"); + + return hash.hash().toString(); + } + + // + // INTERFACE Comparable + // + + /** {@inheritDoc} */ + @Override + public int compareTo(Sone sone) { + return NICE_NAME_COMPARATOR.compare(this, sone); + } + + // + // OBJECT METHODS + // + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object object) { + if (!(object instanceof Sone)) { + return false; + } + return ((Sone) object).getId().equals(id); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + "),albums(" + getRootAlbum().getAlbums().size() + ")]"; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/Database.java b/src/main/java/net/pterodactylus/sone/database/Database.java index ee7a9af..b43b2c4 100644 --- a/src/main/java/net/pterodactylus/sone/database/Database.java +++ b/src/main/java/net/pterodactylus/sone/database/Database.java @@ -26,7 +26,7 @@ import com.google.common.util.concurrent.Service; * * @author David ‘Bombe’ Roden */ -public interface Database extends Service, PostDatabase, PostReplyDatabase, AlbumDatabase, ImageDatabase { +public interface Database extends Service, SoneDatabase, PostDatabase, PostReplyDatabase, AlbumDatabase, ImageDatabase { /** * Saves the database. diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java index 34c819d..4437baf 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java @@ -19,6 +19,9 @@ package net.pterodactylus.sone.database.memory; import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.FluentIterable.from; +import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER; import java.util.ArrayList; import java.util.Collection; @@ -45,7 +48,7 @@ import net.pterodactylus.sone.database.DatabaseException; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostDatabase; import net.pterodactylus.sone.database.PostReplyBuilder; -import net.pterodactylus.sone.database.SoneProvider; +import net.pterodactylus.sone.database.SoneBuilder; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; @@ -67,12 +70,11 @@ public class MemoryDatabase extends AbstractService implements Database { /** The lock. */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); - /** The Sone provider. */ - private final SoneProvider soneProvider; - /** The configuration. */ private final Configuration configuration; + private final Map sones = new HashMap(); + /** All posts by their ID. */ private final Map allPosts = new HashMap(); @@ -112,14 +114,11 @@ public class MemoryDatabase extends AbstractService implements Database { /** * Creates a new memory database. * - * @param soneProvider - * The Sone provider * @param configuration * The configuration for loading and saving elements */ @Inject - public MemoryDatabase(SoneProvider soneProvider, Configuration configuration) { - this.soneProvider = soneProvider; + public MemoryDatabase(Configuration configuration) { this.configuration = configuration; } @@ -162,6 +161,51 @@ public class MemoryDatabase extends AbstractService implements Database { } } + @Override + public Optional getSone(String soneId) { + lock.readLock().lock(); + try { + return fromNullable(sones.get(soneId)); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getSones() { + lock.readLock().lock(); + try { + return Collections.unmodifiableCollection(sones.values()); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getLocalSones() { + lock.readLock().lock(); + try { + return from(getSones()).filter(LOCAL_SONE_FILTER).toSet(); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public Collection getRemoteSones() { + lock.readLock().lock(); + try { + return from(getSones()).filter(not(LOCAL_SONE_FILTER)).toSet(); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public SoneBuilder newSoneBuilder() { + return null; + } + // // POSTPROVIDER METHODS // @@ -332,7 +376,7 @@ public class MemoryDatabase extends AbstractService implements Database { /** {@inheritDocs} */ @Override public PostReplyBuilder newPostReplyBuilder() { - return new MemoryPostReplyBuilder(this, soneProvider); + return new MemoryPostReplyBuilder(this, this); } // diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index 0b1585a..18afb39 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -28,7 +28,7 @@ import java.util.regex.Pattern; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.data.SoneImpl; +import net.pterodactylus.sone.data.impl.DefaultSone; import net.pterodactylus.sone.database.PostProvider; import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.util.io.Closer; @@ -249,7 +249,7 @@ public class SoneTextParser implements Parser { * don’t use create=true above, we don’t want * the empty shell. */ - sone = Optional.of(new SoneImpl(soneId, false)); + sone = Optional.of(new DefaultSone(soneId, false)); } parts.add(new SonePart(sone.get())); line = line.substring(50); diff --git a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java index 83913b4..f32e089 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java @@ -17,7 +17,7 @@ package net.pterodactylus.sone.web; -import com.google.common.base.Optional; +import static com.google.common.base.Optional.of; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; @@ -27,6 +27,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 create a new {@link Post}. * @@ -63,13 +65,13 @@ public class CreatePostPage extends SoneTemplatePage { String senderId = request.getHttpRequest().getPartAsStringFailsafe("sender", 43); String recipientId = request.getHttpRequest().getPartAsStringFailsafe("recipient", 43); Sone currentSone = getCurrentSone(request.getToadletContext()); - Sone sender = webInterface.getCore().getLocalSone(senderId, false); + Optional sender = webInterface.getCore().getLocalSone(senderId); if (sender == null) { - sender = currentSone; + sender = of(currentSone); } Optional recipient = webInterface.getCore().getSone(recipientId); text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - webInterface.getCore().createPost(sender, recipient, System.currentTimeMillis(), text); + webInterface.getCore().createPost(sender.get(), recipient, System.currentTimeMillis(), text); throw new RedirectException(returnPage); } templateContext.set("errorTextEmpty", true); diff --git a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java index c8979c1..84231aa 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java @@ -17,7 +17,7 @@ package net.pterodactylus.sone.web; -import com.google.common.base.Optional; +import static com.google.common.base.Optional.of; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; @@ -27,6 +27,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 post a reply to a post. * @@ -66,12 +68,12 @@ public class CreateReplyPage extends SoneTemplatePage { } if (text.length() > 0) { String senderId = request.getHttpRequest().getPartAsStringFailsafe("sender", 43); - Sone sender = webInterface.getCore().getLocalSone(senderId, false); - if (sender == null) { - sender = getCurrentSone(request.getToadletContext()); + Optional sender = webInterface.getCore().getLocalSone(senderId); + if (!sender.isPresent()) { + sender = of(getCurrentSone(request.getToadletContext())); } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - webInterface.getCore().createReply(sender, post.get(), text); + webInterface.getCore().createReply(sender.get(), post.get(), text); throw new RedirectException(returnPage); } templateContext.set("errorTextEmpty", true); diff --git a/src/main/java/net/pterodactylus/sone/web/LockSonePage.java b/src/main/java/net/pterodactylus/sone/web/LockSonePage.java index 604b176..3c3e2c3 100644 --- a/src/main/java/net/pterodactylus/sone/web/LockSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/LockSonePage.java @@ -22,6 +22,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; + /** * This page lets the user lock a {@link Sone} to prevent it from being * inserted. @@ -53,9 +55,9 @@ public class LockSonePage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44); - Sone sone = webInterface.getCore().getLocalSone(soneId, false); - if (sone != null) { - webInterface.getCore().lockSone(sone); + Optional sone = webInterface.getCore().getLocalSone(soneId); + if (sone.isPresent()) { + webInterface.getCore().lockSone(sone.get()); } String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/LoginPage.java b/src/main/java/net/pterodactylus/sone/web/LoginPage.java index a837bd1..d1bb0ed 100644 --- a/src/main/java/net/pterodactylus/sone/web/LoginPage.java +++ b/src/main/java/net/pterodactylus/sone/web/LoginPage.java @@ -29,8 +29,11 @@ import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.web.Method; + import freenet.clients.http.ToadletContext; +import com.google.common.base.Optional; + /** * The login page manages logging the user in. * @@ -70,9 +73,9 @@ public class LoginPage extends SoneTemplatePage { templateContext.set("sones", localSones); if (request.getMethod() == Method.POST) { String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone-id", 100); - Sone selectedSone = webInterface.getCore().getLocalSone(soneId, false); - if (selectedSone != null) { - setCurrentSone(request.getToadletContext(), selectedSone); + Optional selectedSone = webInterface.getCore().getLocalSone(soneId); + if (selectedSone.isPresent()) { + setCurrentSone(request.getToadletContext(), selectedSone.get()); String target = request.getHttpRequest().getParam("target"); if ((target == null) || (target.length() == 0)) { target = "index.html"; diff --git a/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java b/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java index ed92246..314a8c0 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java @@ -22,6 +22,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; + /** * This page lets the user unlock a {@link Sone} to allow its insertion. * @@ -52,9 +54,9 @@ public class UnlockSonePage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44); - Sone sone = webInterface.getCore().getLocalSone(soneId, false); - if (sone != null) { - webInterface.getCore().unlockSone(sone); + Optional sone = webInterface.getCore().getLocalSone(soneId); + if (sone.isPresent()) { + webInterface.getCore().unlockSone(sone.get()); } String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 593193c..f6645ca 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -150,11 +150,6 @@ import net.pterodactylus.util.web.RedirectPage; import net.pterodactylus.util.web.StaticPage; import net.pterodactylus.util.web.TemplatePage; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableSet; -import com.google.common.eventbus.Subscribe; -import com.google.inject.Inject; - import freenet.clients.http.SessionManager; import freenet.clients.http.SessionManager.Session; import freenet.clients.http.ToadletContainer; @@ -162,6 +157,11 @@ import freenet.clients.http.ToadletContext; import freenet.l10n.BaseL10n; import freenet.support.api.HTTPRequest; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; + /** * Bundles functionality that a web interface of a Freenet plugin needs, e.g. * references to l10n helpers. @@ -423,7 +423,7 @@ public class WebInterface { if (soneId == null) { return null; } - return getCore().getLocalSone(soneId, false); + return getCore().getLocalSone(soneId).orNull(); } /** diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java index 423d2df..a4ab02c 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import static com.google.common.base.Optional.of; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.text.TextFilter; @@ -54,17 +56,17 @@ public class CreatePostAjaxPage extends JsonPage { String recipientId = request.getHttpRequest().getParam("recipient"); Optional recipient = webInterface.getCore().getSone(recipientId); String senderId = request.getHttpRequest().getParam("sender"); - Sone sender = webInterface.getCore().getLocalSone(senderId, false); - if (sender == null) { - sender = sone; + Optional sender = webInterface.getCore().getLocalSone(senderId); + if (!sender.isPresent()) { + sender = of(sone); } String text = request.getHttpRequest().getParam("text"); if ((text == null) || (text.trim().length() == 0)) { return createErrorJsonObject("text-required"); } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - Post newPost = webInterface.getCore().createPost(sender, recipient, text); - return createSuccessJsonObject().put("postId", newPost.getId()).put("sone", sender.getId()).put("recipient", newPost.getRecipientId().orNull()); + Post newPost = webInterface.getCore().createPost(sender.get(), recipient, text); + return createSuccessJsonObject().put("postId", newPost.getId()).put("sone", sender.get().getId()).put("recipient", newPost.getRecipientId().orNull()); } } 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 ea2a2c2..ab5bca3 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java @@ -17,7 +17,7 @@ package net.pterodactylus.sone.web.ajax; -import com.google.common.base.Optional; +import static com.google.common.base.Optional.of; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; @@ -26,6 +26,8 @@ import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; +import com.google.common.base.Optional; + /** * This AJAX page create a reply. * @@ -55,17 +57,17 @@ public class CreateReplyAjaxPage extends JsonPage { String postId = request.getHttpRequest().getParam("post"); String text = request.getHttpRequest().getParam("text").trim(); String senderId = request.getHttpRequest().getParam("sender"); - Sone sender = webInterface.getCore().getLocalSone(senderId, false); - if (sender == null) { - sender = getCurrentSone(request.getToadletContext()); + Optional sender = webInterface.getCore().getLocalSone(senderId); + if (!sender.isPresent()) { + sender = of(getCurrentSone(request.getToadletContext())); } 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.get(), text); - return createSuccessJsonObject().put("reply", reply.getId()).put("sone", sender.getId()); + PostReply reply = webInterface.getCore().createReply(sender.get(), post.get(), text); + return createSuccessJsonObject().put("reply", reply.getId()).put("sone", sender.get().getId()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java index 68668b7..f1e86f9 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java @@ -22,6 +22,8 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; +import com.google.common.base.Optional; + /** * Lets the user {@link Core#lockSone(Sone) lock} a {@link Sone}. * @@ -45,11 +47,11 @@ public class LockSoneAjaxPage extends JsonPage { @Override protected JsonReturnObject createJsonObject(FreenetRequest request) { String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getLocalSone(soneId, false); - if (sone == null) { + Optional sone = webInterface.getCore().getLocalSone(soneId); + if (!sone.isPresent()) { return createErrorJsonObject("invalid-sone-id"); } - webInterface.getCore().lockSone(sone); + webInterface.getCore().lockSone(sone.get()); return createSuccessJsonObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java index f92132d..38f8dde 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java @@ -22,6 +22,8 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; +import com.google.common.base.Optional; + /** * Lets the user {@link Core#unlockSone(Sone) unlock} a {@link Sone}. * @@ -45,11 +47,11 @@ public class UnlockSoneAjaxPage extends JsonPage { @Override protected JsonReturnObject createJsonObject(FreenetRequest request) { String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getLocalSone(soneId, false); - if (sone == null) { + Optional sone = webInterface.getCore().getLocalSone(soneId); + if (!sone.isPresent()) { return createErrorJsonObject("invalid-sone-id"); } - webInterface.getCore().unlockSone(sone); + webInterface.getCore().unlockSone(sone.get()); return createSuccessJsonObject(); } diff --git a/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java b/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java index ae1993a..01f9994 100644 --- a/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java +++ b/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java @@ -17,10 +17,10 @@ package net.pterodactylus.sone.fcp; +import static com.google.common.base.Optional.of; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -34,7 +34,6 @@ import net.pterodactylus.sone.freenet.fcp.FcpException; import freenet.support.SimpleFieldSet; -import com.google.common.base.Optional; import org.junit.Test; /** @@ -50,8 +49,8 @@ public class LockSoneCommandTest { when(localSone.getId()).thenReturn("LocalSone"); when(localSone.isLocal()).thenReturn(true); Core core = mock(Core.class); - when(core.getSone(eq("LocalSone"))).thenReturn(Optional.of(localSone)); - when(core.getLocalSone(eq("LocalSone"), anyBoolean())).thenReturn(localSone); + when(core.getSone(eq("LocalSone"))).thenReturn(of(localSone)); + when(core.getLocalSone(eq("LocalSone"))).thenReturn(of(localSone)); SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "LocalSone").get(); LockSoneCommand lockSoneCommand = new LockSoneCommand(core); @@ -68,7 +67,7 @@ public class LockSoneCommandTest { public void testLockingARemoteSone() throws FcpException { Sone removeSone = mock(Sone.class); Core core = mock(Core.class); - when(core.getSone(eq("RemoteSone"))).thenReturn(Optional.of(removeSone)); + when(core.getSone(eq("RemoteSone"))).thenReturn(of(removeSone)); SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "RemoteSone").get(); LockSoneCommand lockSoneCommand = new LockSoneCommand(core); diff --git a/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java b/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java index b966b19..245cdb5 100644 --- a/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java +++ b/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java @@ -17,10 +17,10 @@ package net.pterodactylus.sone.fcp; +import static com.google.common.base.Optional.of; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -34,7 +34,6 @@ import net.pterodactylus.sone.freenet.fcp.FcpException; import freenet.support.SimpleFieldSet; -import com.google.common.base.Optional; import org.junit.Test; /** @@ -50,8 +49,8 @@ public class UnlockSoneCommandTest { when(localSone.getId()).thenReturn("LocalSone"); when(localSone.isLocal()).thenReturn(true); Core core = mock(Core.class); - when(core.getSone(eq("LocalSone"))).thenReturn(Optional.of(localSone)); - when(core.getLocalSone(eq("LocalSone"), anyBoolean())).thenReturn(localSone); + when(core.getSone(eq("LocalSone"))).thenReturn(of(localSone)); + when(core.getLocalSone(eq("LocalSone"))).thenReturn(of(localSone)); SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "LocalSone").get(); UnlockSoneCommand unlockSoneCommand = new UnlockSoneCommand(core); @@ -68,7 +67,7 @@ public class UnlockSoneCommandTest { public void testUnlockingARemoteSone() throws FcpException { Sone removeSone = mock(Sone.class); Core core = mock(Core.class); - when(core.getSone(eq("RemoteSone"))).thenReturn(Optional.of(removeSone)); + when(core.getSone(eq("RemoteSone"))).thenReturn(of(removeSone)); SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "RemoteSone").get(); UnlockSoneCommand unlockSoneCommand = new UnlockSoneCommand(core); diff --git a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java index 2c04b23..0dd1032 100644 --- a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java +++ b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java @@ -26,7 +26,7 @@ import com.google.common.base.Optional; import junit.framework.TestCase; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.data.SoneImpl; +import net.pterodactylus.sone.data.impl.DefaultSone; import net.pterodactylus.sone.database.SoneProvider; /** @@ -186,7 +186,7 @@ public class SoneTextParserTest extends TestCase { */ @Override public Optional getSone(final String soneId) { - return Optional.of(new SoneImpl(soneId, false) { + return Optional.of(new DefaultSone(soneId, false) { /** * {@inheritDoc}