X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdata%2FSone.java;h=91b1c10f667b3327e232ec04944bb0b9662c2117;hb=f910b58e34b2b94931c609f22ecde49b232b881d;hp=7041289b90e3ca45e91cbd9ba8337c4aa98bb801;hpb=78cd820b392069def4640d45497e4097ef031d53;p=Sone.git diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 7041289..91b1c10 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -18,16 +18,19 @@ package net.pterodactylus.sone.data; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.template.SoneAccessor; import net.pterodactylus.util.logging.Logging; +import net.pterodactylus.util.validation.Validation; import freenet.keys.FreenetURI; /** @@ -38,70 +41,76 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Sone { +public class Sone implements Fingerprintable { + + /** comparator that sorts Sones by their nice name. */ + public static final Comparator NICE_NAME_COMPARATOR = new Comparator() { + + @Override + public int compare(Sone leftSone, Sone rightSone) { + int diff = SoneAccessor.getNiceName(leftSone).compareToIgnoreCase(SoneAccessor.getNiceName(rightSone)); + if (diff != 0) { + return diff; + } + return leftSone.getId().compareToIgnoreCase(rightSone.getId()); + } + + }; /** The logger. */ private static final Logger logger = Logging.getLogger(Sone.class); - /** A GUID for this Sone. */ - private final UUID id; + /** The ID of this Sone. */ + private final String id; - /** The name of this Sone. */ - private final String name; + /** The identity of this Sone. */ + private Identity identity; /** The URI under which the Sone is stored in Freenet. */ - private FreenetURI requestUri; + private volatile FreenetURI requestUri; /** The URI used to insert a new version of this Sone. */ /* This will be null for remote Sones! */ - private FreenetURI insertUri; + 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 profile of this Sone. */ - private Profile profile; + private volatile Profile profile = new Profile(); + + /** The client used by the Sone. */ + private volatile Client client; /** All friend Sones. */ - private final Set friendSones = new HashSet(); + private final Set friendSones = Collections.synchronizedSet(new HashSet()); /** All posts. */ - private final List posts = new ArrayList(); + private final Set posts = Collections.synchronizedSet(new HashSet()); /** All replies. */ - private final Set replies = new HashSet(); + private final Set replies = Collections.synchronizedSet(new HashSet()); - /** Modification count. */ - private volatile long modificationCounter = 0; + /** The IDs of all liked posts. */ + private final Set likedPostIds = Collections.synchronizedSet(new HashSet()); - /** - * Creates a new Sone. - * - * @param id - * The ID of this Sone - * @param name - * The name of the Sone - * @param requestUri - * The request URI of the Sone - */ - public Sone(UUID id, String name, FreenetURI requestUri) { - this(id, name, requestUri, null); - } + /** The IDs of all liked replies. */ + private final Set likedReplyIds = Collections.synchronizedSet(new HashSet()); + + /** The albums of this Sone. */ + private final List albums = Collections.synchronizedList(new ArrayList()); /** * Creates a new Sone. * * @param id - * The ID of this Sone - * @param name - * The name of the Sone - * @param requestUri - * The request URI of the Sone - * @param insertUri - * The insert URI of the Sone + * The ID of the Sone */ - public Sone(UUID id, String name, FreenetURI requestUri, FreenetURI insertUri) { + public Sone(String id) { this.id = id; - this.name = name; - this.requestUri = requestUri; - this.insertUri = insertUri; } // @@ -109,12 +118,39 @@ public class Sone { // /** - * Returns the ID of this Sone. + * Returns the identity of this Sone. * - * @return The ID of this Sone + * @return The identity of this Sone */ public String getId() { - return id.toString(); + 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 Sone 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; } /** @@ -123,7 +159,7 @@ public class Sone { * @return The name of this Sone */ public String getName() { - return name; + return (identity != null) ? identity.getNickname() : null; } /** @@ -132,7 +168,26 @@ public class Sone { * @return The request URI of this Sone */ public FreenetURI getRequestUri() { - return requestUri; + 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.setDocName("Sone").setMetaString(new String[0]); + return this; + } + if (!this.requestUri.equalsKeypair(requestUri)) { + logger.log(Level.WARNING, "Request URI %s tried to overwrite %s!", new Object[] { requestUri, this.requestUri }); + return this; + } + return this; } /** @@ -141,7 +196,72 @@ public class Sone { * @return The insert URI of this Sone */ public FreenetURI getInsertUri() { - return insertUri; + 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.setDocName("Sone").setMetaString(new String[0]); + return this; + } + if (!this.insertUri.equalsKeypair(insertUri)) { + logger.log(Level.WARNING, "Request URI %s tried to overwrite %s!", new Object[] { 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.INFO, "New latest edition %d is not greater than current latest edition %d!", new Object[] { 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; } /** @@ -151,7 +271,7 @@ public class Sone { * * @return A copy of the profile */ - public Profile getProfile() { + public synchronized Profile getProfile() { return new Profile(profile); } @@ -165,7 +285,27 @@ public class Sone { */ public synchronized void setProfile(Profile profile) { this.profile = new Profile(profile); - modificationCounter++; + } + + /** + * 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; } /** @@ -173,20 +313,34 @@ public class Sone { * * @return The friend Sones of this Sone */ - public Set getFriends() { - return Collections.unmodifiableSet(friendSones); + public List getFriends() { + List friends = new ArrayList(friendSones); + return friends; + } + + /** + * Sets all friends of this Sone at once. + * + * @param friends + * The new (and only) friends of this Sone + * @return This Sone (for method chaining) + */ + public Sone setFriends(Collection friends) { + friendSones.clear(); + friendSones.addAll(friends); + return this; } /** * Returns whether this Sone has the given Sone as a friend Sone. * - * @param friendSone - * The friend Sone to check for + * @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(Sone friendSone) { - return friendSones.contains(friendSone); + public boolean hasFriend(String friendSoneId) { + return friendSones.contains(friendSoneId); } /** @@ -196,9 +350,9 @@ public class Sone { * The friend Sone to add * @return This Sone (for method chaining) */ - public synchronized Sone addFriend(Sone friendSone) { - if (friendSones.add(friendSone)) { - modificationCounter++; + public Sone addFriend(String friendSone) { + if (!friendSone.equals(id)) { + friendSones.add(friendSone); } return this; } @@ -206,14 +360,12 @@ public class Sone { /** * Removes the given Sone as a friend Sone. * - * @param friendSone - * The friend Sone to remove + * @param friendSoneId + * The ID of the friend Sone to remove * @return This Sone (for method chaining) */ - public synchronized Sone removeFriend(Sone friendSone) { - if (friendSones.remove(friendSone)) { - modificationCounter++; - } + public Sone removeFriend(String friendSoneId) { + friendSones.remove(friendSoneId); return this; } @@ -223,19 +375,28 @@ public class Sone { * @return All posts of this Sone */ public List getPosts() { - List sortedPosts = new ArrayList(posts); - Collections.sort(sortedPosts, new Comparator() { - - @Override - public int compare(Post leftPost, Post rightPost) { - return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, rightPost.getTime() - leftPost.getTime())); - } - - }); + 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 synchronized Sone setPosts(Collection posts) { + 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. * @@ -245,7 +406,6 @@ public class Sone { public synchronized void addPost(Post post) { if (post.getSone().equals(this) && posts.add(post)) { logger.log(Level.FINEST, "Adding %s to “%s”.", new Object[] { post, getName() }); - modificationCounter++; } } @@ -256,8 +416,8 @@ public class Sone { * The post to remove */ public synchronized void removePost(Post post) { - if (post.getSone().equals(this) && posts.remove(post)) { - modificationCounter++; + if (post.getSone().equals(this)) { + posts.remove(post); } } @@ -266,11 +426,24 @@ public class Sone { * * @return All replies this Sone made */ - public Set getReplies() { + public synchronized 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 synchronized 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. * @@ -278,8 +451,8 @@ public class Sone { * The reply to add */ public synchronized void addReply(Reply reply) { - if (reply.getSone().equals(this) && replies.add(reply)) { - modificationCounter++; + if (reply.getSone().equals(this)) { + replies.add(reply); } } @@ -290,41 +463,226 @@ public class Sone { * The reply to remove */ public synchronized void removeReply(Reply reply) { - if (reply.getSone().equals(this) && replies.remove(reply)) { - modificationCounter++; + if (reply.getSone().equals(this)) { + replies.remove(reply); } } /** - * Returns the modification counter. + * 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. * - * @return The modification counter + * @param likedPostIds + * All liked posts’ IDs + * @return This Sone (for method chaining) */ - public synchronized long getModificationCounter() { - return modificationCounter; + public synchronized Sone setLikePostIds(Set likedPostIds) { + this.likedPostIds.clear(); + this.likedPostIds.addAll(likedPostIds); + return this; } /** - * Sets the modification counter. + * Checks whether the given post ID is liked by this Sone. * - * @param modificationCounter - * The new modification counter + * @param postId + * The ID of the post + * @return {@code true} if this Sone likes the given post, {@code false} + * otherwise */ - public synchronized void setModificationCounter(long modificationCounter) { - this.modificationCounter = modificationCounter; + public boolean isLikedPostId(String postId) { + return likedPostIds.contains(postId); } /** - * Updates the suggested edition in both the request URI and the insert URI. + * Adds the given post ID to the list of posts this Sone likes. * - * @param requestUri - * The request URI that resulted from an insert + * @param postId + * The ID of the post + * @return This Sone (for method chaining) */ - public void updateUris(FreenetURI requestUri) { - /* TODO - check for the correct URI. */ - long latestEdition = requestUri.getSuggestedEdition(); - this.requestUri = this.requestUri.setSuggestedEdition(latestEdition); - this.insertUri = this.insertUri.setSuggestedEdition(latestEdition); + public synchronized 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 synchronized 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 synchronized 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 synchronized 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 synchronized Sone removeLikedReplyId(String replyId) { + likedReplyIds.remove(replyId); + return this; + } + + /** + * Returns the albums of this Sone. + * + * @return The albums of this Sone + */ + public List getAlbums() { + return Collections.unmodifiableList(albums); + } + + /** + * Adds an album to this Sone. + * + * @param album + * The album to add + */ + public synchronized void addAlbum(Album album) { + Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check(); + albums.add(album); + } + + /** + * Removes an album from this Sone. + * + * @param album + * The album to remove + */ + public synchronized void removeAlbum(Album album) { + Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check(); + albums.remove(album); + } + + // + // FINGERPRINTABLE METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public synchronized String getFingerprint() { + StringBuilder fingerprint = new StringBuilder(); + fingerprint.append("Profile("); + if (profile.getFirstName() != null) { + fingerprint.append("FirstName(").append(profile.getFirstName()).append(')'); + } + if (profile.getMiddleName() != null) { + fingerprint.append("MiddleName(").append(profile.getMiddleName()).append(')'); + } + if (profile.getLastName() != null) { + fingerprint.append("LastName(").append(profile.getLastName()).append(')'); + } + if (profile.getBirthDay() != null) { + fingerprint.append("BirthDay(").append(profile.getBirthDay()).append(')'); + } + if (profile.getBirthMonth() != null) { + fingerprint.append("BirthMonth(").append(profile.getBirthMonth()).append(')'); + } + if (profile.getBirthYear() != null) { + fingerprint.append("BirthYear(").append(profile.getBirthYear()).append(')'); + } + fingerprint.append(")"); + + fingerprint.append("Posts("); + for (Post post : getPosts()) { + fingerprint.append("Post(").append(post.getId()).append(')'); + } + fingerprint.append(")"); + + List replies = new ArrayList(getReplies()); + Collections.sort(replies, Reply.TIME_COMPARATOR); + fingerprint.append("Replies("); + for (Reply reply : replies) { + fingerprint.append("Reply(").append(reply.getId()).append(')'); + } + fingerprint.append(')'); + + List likedPostIds = new ArrayList(getLikedPostIds()); + Collections.sort(likedPostIds); + fingerprint.append("LikedPosts("); + for (String likedPostId : likedPostIds) { + fingerprint.append("Post(").append(likedPostId).append(')'); + } + fingerprint.append(')'); + + List likedReplyIds = new ArrayList(getLikedReplyIds()); + Collections.sort(likedReplyIds); + fingerprint.append("LikedReplies("); + for (String likedReplyId : likedReplyIds) { + fingerprint.append("Reply(").append(likedReplyId).append(')'); + } + fingerprint.append(')'); + + fingerprint.append("Albums("); + for (Album album : albums) { + fingerprint.append(album.getFingerprint()); + } + fingerprint.append(')'); + + return fingerprint.toString(); } // @@ -355,7 +713,7 @@ public class Sone { */ @Override public String toString() { - return getClass().getName() + "[id=" + getId() + ",name=" + getName() + ",requestUri=" + getRequestUri() + ",insertUri=" + getInsertUri() + ",friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + ")]"; + return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + ")]"; } }