2 * FreenetSone - Sone.java - Copyright © 2010 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.sone.data;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.HashSet;
25 import java.util.List;
27 import java.util.UUID;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
31 import net.pterodactylus.util.logging.Logging;
32 import freenet.keys.FreenetURI;
35 * A Sone defines everything about a user: her profile, her status updates, her
36 * replies, her likes and dislikes, etc.
38 * Operations that modify the Sone need to synchronize on the Sone in question.
40 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
45 private static final Logger logger = Logging.getLogger(Sone.class);
47 /** A GUID for this Sone. */
48 private final UUID id;
50 /** The name of this Sone. */
53 /** The URI under which the Sone is stored in Freenet. */
54 private FreenetURI requestUri;
56 /** The URI used to insert a new version of this Sone. */
57 /* This will be null for remote Sones! */
58 private FreenetURI insertUri;
60 /** The time of the last inserted update. */
63 /** The profile of this Sone. */
64 private Profile profile;
66 /** All friend Sones. */
67 private final Set<Sone> friendSones = new HashSet<Sone>();
70 private final Set<Post> posts = new HashSet<Post>();
73 private final Set<Reply> replies = new HashSet<Reply>();
75 /** The IDs of all blocked Sones. */
76 private final Set<String> blockedSoneIds = new HashSet<String>();
78 /** Modification count. */
79 private volatile long modificationCounter = 0;
87 public Sone(String id) {
88 this.id = UUID.fromString(id);
96 * Returns the ID of this Sone.
98 * @return The ID of this Sone
100 public String getId() {
101 return id.toString();
105 * Returns the name of this Sone.
107 * @return The name of this Sone
109 public String getName() {
114 * Sets the name of this Sone.
117 * The name of this Sone
118 * @return This sone (for method chaining)
120 public Sone setName(String name) {
126 * Returns the request URI of this Sone.
128 * @return The request URI of this Sone
130 public FreenetURI getRequestUri() {
135 * Sets the request URI of this Sone.
138 * The request URI of this Sone
139 * @return This Sone (for method chaining)
141 public Sone setRequestUri(FreenetURI requestUri) {
142 this.requestUri = requestUri;
147 * Returns the insert URI of this Sone.
149 * @return The insert URI of this Sone
151 public FreenetURI getInsertUri() {
156 * Sets the insert URI of this Sone.
159 * The insert URI of this Sone
160 * @return This Sone (for method chaining)
162 public Sone setInsertUri(FreenetURI insertUri) {
163 this.insertUri = insertUri;
168 * Return the time of the last inserted update of this Sone.
170 * @return The time of the update (in milliseconds since Jan 1, 1970 UTC)
172 public long getTime() {
177 * Sets the time of the last inserted update of this Sone.
180 * The time of the update (in milliseconds since Jan 1, 1970 UTC)
181 * @return This Sone (for method chaining)
183 public Sone setTime(long time) {
189 * Returns a copy of the profile. If you want to update values in the
190 * profile of this Sone, update the values in the returned {@link Profile}
191 * and use {@link #setProfile(Profile)} to change the profile in this Sone.
193 * @return A copy of the profile
195 public Profile getProfile() {
196 return new Profile(profile);
200 * Sets the profile of this Sone. A copy of the given profile is stored so
201 * that subsequent modifications of the given profile are not reflected in
207 public synchronized void setProfile(Profile profile) {
208 this.profile = new Profile(profile);
209 modificationCounter++;
213 * Returns all friend Sones of this Sone.
215 * @return The friend Sones of this Sone
217 public Set<Sone> getFriends() {
218 return Collections.unmodifiableSet(friendSones);
222 * Sets all friends of this Sone at once.
225 * The new (and only) friends of this Sone
226 * @return This Sone (for method chaining)
228 public synchronized Sone setFriends(Collection<Sone> friends) {
230 friendSones.addAll(friends);
231 modificationCounter++;
236 * Returns whether this Sone has the given Sone as a friend Sone.
239 * The friend Sone to check for
240 * @return {@code true} if this Sone has the given Sone as a friend,
241 * {@code false} otherwise
243 public boolean hasFriend(Sone friendSone) {
244 return friendSones.contains(friendSone);
248 * Adds the given Sone as a friend Sone.
251 * The friend Sone to add
252 * @return This Sone (for method chaining)
254 public synchronized Sone addFriend(Sone friendSone) {
255 if (!friendSone.equals(this) && friendSones.add(friendSone)) {
256 modificationCounter++;
262 * Removes the given Sone as a friend Sone.
265 * The friend Sone to remove
266 * @return This Sone (for method chaining)
268 public synchronized Sone removeFriend(Sone friendSone) {
269 if (friendSones.remove(friendSone)) {
270 modificationCounter++;
276 * Returns the list of posts of this Sone, sorted by time, newest first.
278 * @return All posts of this Sone
280 public List<Post> getPosts() {
281 List<Post> sortedPosts = new ArrayList<Post>(posts);
282 Collections.sort(sortedPosts, new Comparator<Post>() {
285 public int compare(Post leftPost, Post rightPost) {
286 return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, rightPost.getTime() - leftPost.getTime()));
294 * Sets all posts of this Sone at once.
297 * The new (and only) posts of this Sone
298 * @return This Sone (for method chaining)
300 public synchronized Sone setPosts(Collection<Post> posts) {
302 this.posts.addAll(posts);
303 modificationCounter++;
308 * Adds the given post to this Sone. The post will not be added if its
309 * {@link Post#getSone() Sone} is not this Sone.
314 public synchronized void addPost(Post post) {
315 if (post.getSone().equals(this) && posts.add(post)) {
316 logger.log(Level.FINEST, "Adding %s to “%s”.", new Object[] { post, getName() });
317 modificationCounter++;
322 * Removes the given post from this Sone.
327 public synchronized void removePost(Post post) {
328 if (post.getSone().equals(this) && posts.remove(post)) {
329 modificationCounter++;
334 * Returns all replies this Sone made.
336 * @return All replies this Sone made
338 public Set<Reply> getReplies() {
339 logger.log(Level.FINEST, "Friends of %s: %s", new Object[] { this, friendSones });
340 return Collections.unmodifiableSet(replies);
344 * Sets all replies of this Sone at once.
347 * The new (and only) replies of this Sone
348 * @return This Sone (for method chaining)
350 public synchronized Sone setReplies(Collection<Reply> replies) {
351 this.replies.clear();
352 this.replies.addAll(replies);
353 modificationCounter++;
358 * Adds a reply to this Sone. If the given reply was not made by this Sone,
359 * nothing is added to this Sone.
364 public synchronized void addReply(Reply reply) {
365 if (reply.getSone().equals(this) && replies.add(reply)) {
366 modificationCounter++;
371 * Removes a reply from this Sone.
374 * The reply to remove
376 public synchronized void removeReply(Reply reply) {
377 if (reply.getSone().equals(this) && replies.remove(reply)) {
378 modificationCounter++;
383 * Returns the IDs of all blocked Sones. These Sones will not propagated
384 * using the “known Sones” mechanism.
386 * @return The IDs of all blocked Sones
388 public Set<String> getBlockedSoneIds() {
389 return Collections.unmodifiableSet(blockedSoneIds);
393 * Returns whether the given Sone ID is blocked.
396 * The Sone ID to check
397 * @return {@code true} if the given Sone ID is blocked, {@code false}
400 public boolean isSoneBlocked(String soneId) {
401 return blockedSoneIds.contains(soneId);
405 * Adds the given ID to the list of blocked IDs.
408 * The Sone ID to block
410 public synchronized void addBlockedSoneId(String soneId) {
411 if (blockedSoneIds.add(soneId)) {
412 modificationCounter++;
417 * Removes the given ID from the list of blocked IDs.
420 * The Sone ID to unblock
422 public synchronized void removeBlockedSoneId(String soneId) {
423 if (blockedSoneIds.remove(soneId)) {
424 modificationCounter++;
429 * Returns the modification counter.
431 * @return The modification counter
433 public synchronized long getModificationCounter() {
434 return modificationCounter;
438 * Sets the modification counter.
440 * @param modificationCounter
441 * The new modification counter
443 public synchronized void setModificationCounter(long modificationCounter) {
444 this.modificationCounter = modificationCounter;
448 * Updates the suggested edition in both the request URI and the insert URI.
451 * The request URI that resulted from an insert
453 public void updateUris(FreenetURI requestUri) {
454 /* TODO - check for the correct URI. */
455 long latestEdition = requestUri.getSuggestedEdition();
456 this.requestUri = this.requestUri.setSuggestedEdition(latestEdition);
457 if (this.insertUri != null) {
458 this.insertUri = this.insertUri.setSuggestedEdition(latestEdition);
470 public int hashCode() {
471 return id.hashCode();
478 public boolean equals(Object object) {
479 if (!(object instanceof Sone)) {
482 return ((Sone) object).id.equals(id);
489 public String toString() {
490 return getClass().getName() + "[id=" + id + ",name=" + name + ",requestUri=" + requestUri + ",insertUri=" + insertUri + ",friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + ")]";