X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;ds=inline;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdata%2Fimpl%2FDefaultSone.java;h=08a9ac12316b1760ca8478af93aa7660bcf66ba6;hb=e01ceeed92f226bf74c9f516bce882b5cd816a62;hp=bbf7a2d5e3d826c321b2447fe5419e9c70f1a66c;hpb=9d9e7df5ad1cb5646be2caa666099f8c40a45ee3;p=Sone.git
diff --git a/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java b/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java
index bbf7a2d..08a9ac1 100644
--- a/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java
+++ b/src/main/java/net/pterodactylus/sone/data/impl/DefaultSone.java
@@ -17,6 +17,8 @@
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;
@@ -24,18 +26,75 @@ 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.database.AlbumBuilder;
+import net.pterodactylus.sone.database.Database;
+import net.pterodactylus.sone.database.PostBuilder;
+import net.pterodactylus.sone.database.PostReplyBuilder;
+import net.pterodactylus.sone.freenet.wot.Identity;
+import net.pterodactylus.util.logging.Logging;
+
+import freenet.keys.FreenetURI;
+
+import com.google.common.base.Optional;
+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 extends AbstractSone {
+public class DefaultSone implements Sone {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(DefaultSone.class);
+
+ /** The database. */
+ private final Database database;
+
+ /** The ID of this Sone. */
+ private final String id;
+
+ /** Whether the Sone is local. */
+ private final boolean local;
+
+ /** 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 final 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();
@@ -49,6 +108,12 @@ public class DefaultSone extends AbstractSone {
/** The IDs of all liked replies. */
private final Set likedReplyIds = new CopyOnWriteArraySet();
+ /** The root album containing all albums. */
+ private final Album rootAlbum;
+
+ /** Sone-specific options. */
+ private Options options = new Options();
+
/**
* Creates a new Sone.
*
@@ -57,64 +122,126 @@ public class DefaultSone extends AbstractSone {
* @param local
* {@code true} if the Sone is a local Sone, {@code false} otherwise
*/
- public DefaultSone(String id, boolean local) {
- super(id, local);
+ public DefaultSone(Database database, String id, boolean local, Client client) {
+ this.database = database;
+ this.id = id;
+ this.local = local;
+ this.client = client;
+ rootAlbum = new DefaultAlbumBuilder(database, this, null).build();
}
//
// ACCESSORS
//
- /**
- * Returns whether this Sone is known.
- *
- * @return {@code true} if this Sone is known, {@code false} otherwise
- */
+ public String getId() {
+ return id;
+ }
+
+ public Identity getIdentity() {
+ return database.getIdentity(id).get();
+ }
+
+ public String getName() {
+ return getIdentity().getNickname();
+ }
+
+ public boolean isLocal() {
+ return local;
+ }
+
+ public FreenetURI getRequestUri() {
+ return (requestUri != null) ? requestUri.setSuggestedEdition(latestEdition) : null;
+ }
+
+ 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;
+ }
+
+ public FreenetURI getInsertUri() {
+ return (insertUri != null) ? insertUri.setSuggestedEdition(latestEdition) : null;
+ }
+
+ 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;
+ }
+
+ public long getLatestEdition() {
+ return latestEdition;
+ }
+
+ 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;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ public Sone setTime(long time) {
+ this.time = time;
+ return this;
+ }
+
+ public SoneStatus getStatus() {
+ return status;
+ }
+
+ public Sone setStatus(SoneStatus status) {
+ this.status = checkNotNull(status, "status must not be null");
+ return this;
+ }
+
+ public Profile getProfile() {
+ return new Profile(profile);
+ }
+
+ public void setProfile(Profile profile) {
+ this.profile = new Profile(profile);
+ }
+
+ public Client getClient() {
+ return client;
+ }
+
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);
@@ -122,23 +249,11 @@ public class DefaultSone extends AbstractSone {
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) {
@@ -148,13 +263,6 @@ public class DefaultSone extends AbstractSone {
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();
@@ -163,94 +271,44 @@ public class DefaultSone extends AbstractSone {
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);
@@ -261,11 +319,6 @@ public class DefaultSone extends AbstractSone {
return likedPostIds.contains(postId);
}
- public Sone addLikedPostId(String postId) {
- likedPostIds.add(postId);
- return this;
- }
-
public Sone removeLikedPostId(String postId) {
likedPostIds.remove(postId);
return this;
@@ -295,9 +348,144 @@ public class DefaultSone extends AbstractSone {
return this;
}
+ public Album getRootAlbum() {
+ return rootAlbum;
+ }
+
+ public Options getOptions() {
+ return options;
+ }
+
+ /* TODO - remove this method again, maybe add an option provider */
+ public void setOptions(Options options) {
+ this.options = options;
+ }
+
@Override
public AlbumBuilder newAlbumBuilder() {
- return new DefaultAlbumBuilder(this, rootAlbum);
+ return new DefaultAlbumBuilder(database, this, rootAlbum.getId());
+ }
+
+ public PostBuilder newPostBuilder() {
+ return new DefaultPostBuilder(database, getId()) {
+ @Override
+ public Post build(Optional postCreated) {
+ Post post = super.build(postCreated);
+ database.storePost(post);
+ return post;
+ }
+ };
+ }
+
+ @Override
+ public PostReplyBuilder newPostReplyBuilder(String postId) throws IllegalStateException {
+ return new DefaultPostReplyBuilder(database, getId(), postId) {
+ @Override
+ public PostReply build(Optional postReplyCreated) {
+ PostReply postReply = super.build(postReplyCreated);
+ database.storePostReply(postReply);
+ return postReply;
+ }
+ };
+ }
+
+ public Modifier modify() {
+ return new Modifier() {
+ private long latestEdition = DefaultSone.this.latestEdition;
+ @Override
+ public Modifier setLatestEdition(long latestEdition) {
+ this.latestEdition = latestEdition;
+ return this;
+ }
+
+ @Override
+ public Sone update() {
+ DefaultSone.this.latestEdition = latestEdition;
+ return DefaultSone.this;
+ }
+ };
+ }
+
+ //
+ // FINGERPRINTABLE METHODS
+ //
+
+ @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
+ //
+
+ @Override
+ public int compareTo(Sone sone) {
+ return NICE_NAME_COMPARATOR.compare(this, sone);
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Sone)) {
+ return false;
+ }
+ return ((Sone) object).getId().equals(id);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[id=" + id + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + "),albums(" + getRootAlbum().getAlbums().size() + ")]";
}
}