X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdata%2FSone.java;h=901d676bc9b55df1b5f3774f85da8bc0c1ffad85;hp=fd69832b03062c1bf319eadaeb32e81708b97588;hb=90179865f79eae0c8cf5726c21dab488fcd5be45;hpb=6187b535deb8c1b727e41f2c204b0f9311a8e31b diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index fd69832..901d676 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -1,5 +1,5 @@ /* - * FreenetSone - Sone.java - Copyright © 2010 David Roden + * Sone - Sone.java - Copyright © 2010 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 @@ -27,9 +27,14 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.core.Options; import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.template.SoneAccessor; +import net.pterodactylus.util.filter.Filter; import net.pterodactylus.util.logging.Logging; +import net.pterodactylus.util.validation.Validation; import freenet.keys.FreenetURI; /** @@ -40,7 +45,31 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Sone { +public class Sone implements Fingerprintable, Comparable { + + /** + * The possible values for the “show custom avatars” option. + * + * @author David ‘Bombe’ Roden + */ + public static enum ShowCustomAvatars { + + /** Never show custom avatars. */ + NEVER, + + /** Only show custom avatars of followed Sones. */ + FOLLOWED, + + /** Only show custom avatars of Sones you manually trust. */ + MANUALLY_TRUSTED, + + /** Only show custom avatars of automatically trusted Sones. */ + TRUSTED, + + /** Always show custom avatars. */ + ALWAYS, + + } /** comparator that sorts Sones by their nice name. */ public static final Comparator NICE_NAME_COMPARATOR = new Comparator() { @@ -56,6 +85,69 @@ public class Sone { }; + /** + * Comparator that sorts Sones by last activity (least recent active first). + */ + public static final Comparator LAST_ACTIVITY_COMPARATOR = new Comparator() { + + @Override + public int compare(Sone firstSone, Sone secondSone) { + return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, secondSone.getTime() - firstSone.getTime())); + } + }; + + /** Comparator that sorts Sones by numbers of posts (descending). */ + public static final Comparator POST_COUNT_COMPARATOR = new Comparator() { + + /** + * {@inheritDoc} + */ + @Override + public int compare(Sone leftSone, Sone rightSone) { + return (leftSone.getPosts().size() != rightSone.getPosts().size()) ? (rightSone.getPosts().size() - leftSone.getPosts().size()) : (rightSone.getReplies().size() - leftSone.getReplies().size()); + } + }; + + /** Comparator that sorts Sones by number of images (descending). */ + public static final Comparator IMAGE_COUNT_COMPARATOR = new Comparator() { + + /** + * {@inheritDoc} + */ + @Override + public int compare(Sone leftSone, Sone rightSone) { + return rightSone.getAllImages().size() - leftSone.getAllImages().size(); + } + }; + + /** Filter to remove Sones that have not been downloaded. */ + public static final Filter EMPTY_SONE_FILTER = new Filter() { + + @Override + public boolean filterObject(Sone sone) { + return sone.getTime() != 0; + } + }; + + /** Filter that matches all {@link Core#isLocalSone(Sone) local Sones}. */ + public static final Filter LOCAL_SONE_FILTER = new Filter() { + + @Override + public boolean filterObject(Sone sone) { + return sone.getIdentity() instanceof OwnIdentity; + } + + }; + + /** Filter that matches Sones that have at least one album. */ + public static final Filter HAS_ALBUM_FILTER = new Filter() { + + @Override + public boolean filterObject(Sone sone) { + return !sone.getAlbums().isEmpty(); + } + }; + /** The logger. */ private static final Logger logger = Logging.getLogger(Sone.class); @@ -79,16 +171,19 @@ public class Sone { private volatile long time; /** The profile of this Sone. */ - private volatile Profile profile; + private volatile Profile profile = new Profile(this); + + /** The client used by the Sone. */ + private volatile Client client; /** All friend Sones. */ - private final Set friendSones = Collections.synchronizedSet(new HashSet()); + private final Set friendSones = Collections.synchronizedSet(new HashSet()); /** All posts. */ private final Set posts = Collections.synchronizedSet(new HashSet()); /** All replies. */ - private final Set replies = Collections.synchronizedSet(new HashSet()); + private final Set replies = Collections.synchronizedSet(new HashSet()); /** The IDs of all liked posts. */ private final Set likedPostIds = Collections.synchronizedSet(new HashSet()); @@ -96,8 +191,11 @@ public class Sone { /** The IDs of all liked replies. */ private final Set likedReplyIds = Collections.synchronizedSet(new HashSet()); - /** Modification count. */ - private volatile long modificationCounter = 0; + /** The albums of this Sone. */ + private final List albums = Collections.synchronizedList(new ArrayList()); + + /** Sone-specific options. */ + private final Options options = new Options(); /** * Creates a new Sone. @@ -164,7 +262,7 @@ public class Sone { * @return The request URI of this Sone */ public FreenetURI getRequestUri() { - return requestUri.setSuggestedEdition(latestEdition); + return (requestUri != null) ? requestUri.setSuggestedEdition(latestEdition) : null; } /** @@ -176,7 +274,7 @@ public class Sone { */ public Sone setRequestUri(FreenetURI requestUri) { if (this.requestUri == null) { - this.requestUri = requestUri.setDocName("Sone").setMetaString(new String[0]); + this.requestUri = requestUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]); return this; } if (!this.requestUri.equalsKeypair(requestUri)) { @@ -204,7 +302,7 @@ public class Sone { */ public Sone setInsertUri(FreenetURI insertUri) { if (this.insertUri == null) { - this.insertUri = insertUri.setDocName("Sone").setMetaString(new String[0]); + this.insertUri = insertUri.setKeyType("USK").setDocName("Sone").setMetaString(new String[0]); return this; } if (!this.insertUri.equalsKeypair(insertUri)) { @@ -233,7 +331,7 @@ public class 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 }); + logger.log(Level.FINE, "New latest edition %d is not greater than current latest edition %d!", new Object[] { latestEdition, this.latestEdition }); return; } this.latestEdition = latestEdition; @@ -267,7 +365,7 @@ public class Sone { * * @return A copy of the profile */ - public Profile getProfile() { + public synchronized Profile getProfile() { return new Profile(profile); } @@ -281,53 +379,49 @@ public class Sone { */ public synchronized void setProfile(Profile profile) { this.profile = new Profile(profile); - modificationCounter++; } /** - * Returns all friend Sones of this Sone. + * Returns the client used by this Sone. * - * @return The friend Sones of this Sone + * @return The client used by this Sone, or {@code null} */ - public List getFriends() { - List friends = new ArrayList(friendSones); - Collections.sort(friends, 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 (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, rightSone.getTime() - leftSone.getTime())); - } - }); - return friends; + public Client getClient() { + return client; } /** - * Sets all friends of this Sone at once. + * Sets the client used by this Sone. * - * @param friends - * The new (and only) friends of this Sone + * @param client + * The client used by this Sone, or {@code null} * @return This Sone (for method chaining) */ - public Sone setFriends(Collection friends) { - friendSones.clear(); - friendSones.addAll(friends); + public Sone setClient(Client client) { + this.client = client; 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 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); } /** @@ -337,8 +431,8 @@ public class Sone { * The friend Sone to add * @return This Sone (for method chaining) */ - public Sone addFriend(Sone friendSone) { - if (!friendSone.equals(this)) { + public Sone addFriend(String friendSone) { + if (!friendSone.equals(id)) { friendSones.add(friendSone); } return this; @@ -347,12 +441,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 Sone removeFriend(Sone friendSone) { - friendSones.remove(friendSone); + public Sone removeFriend(String friendSoneId) { + friendSones.remove(friendSoneId); return this; } @@ -378,9 +472,10 @@ public class Sone { * @return This Sone (for method chaining) */ public synchronized Sone setPosts(Collection posts) { - this.posts.clear(); - this.posts.addAll(posts); - modificationCounter++; + synchronized (this) { + this.posts.clear(); + this.posts.addAll(posts); + } return this; } @@ -394,7 +489,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++; } } @@ -405,8 +499,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); } } @@ -415,7 +509,7 @@ public class Sone { * * @return All replies this Sone made */ - public Set getReplies() { + public synchronized Set getReplies() { return Collections.unmodifiableSet(replies); } @@ -426,10 +520,9 @@ public class Sone { * The new (and only) replies of this Sone * @return This Sone (for method chaining) */ - public synchronized Sone setReplies(Collection replies) { + public synchronized Sone setReplies(Collection replies) { this.replies.clear(); this.replies.addAll(replies); - modificationCounter++; return this; } @@ -440,9 +533,9 @@ public class Sone { * @param reply * The reply to add */ - public synchronized void addReply(Reply reply) { - if (reply.getSone().equals(this) && replies.add(reply)) { - modificationCounter++; + public synchronized void addReply(PostReply reply) { + if (reply.getSone().equals(this)) { + replies.add(reply); } } @@ -452,9 +545,9 @@ public class Sone { * @param reply * The reply to remove */ - public synchronized void removeReply(Reply reply) { - if (reply.getSone().equals(this) && replies.remove(reply)) { - modificationCounter++; + public synchronized void removeReply(PostReply reply) { + if (reply.getSone().equals(this)) { + replies.remove(reply); } } @@ -477,7 +570,6 @@ public class Sone { public synchronized Sone setLikePostIds(Set likedPostIds) { this.likedPostIds.clear(); this.likedPostIds.addAll(likedPostIds); - modificationCounter++; return this; } @@ -501,9 +593,7 @@ public class Sone { * @return This Sone (for method chaining) */ public synchronized Sone addLikedPostId(String postId) { - if (likedPostIds.add(postId)) { - modificationCounter++; - } + likedPostIds.add(postId); return this; } @@ -515,9 +605,7 @@ public class Sone { * @return This Sone (for method chaining) */ public synchronized Sone removeLikedPostId(String postId) { - if (likedPostIds.remove(postId)) { - modificationCounter++; - } + likedPostIds.remove(postId); return this; } @@ -540,7 +628,6 @@ public class Sone { public synchronized Sone setLikeReplyIds(Set likedReplyIds) { this.likedReplyIds.clear(); this.likedReplyIds.addAll(likedReplyIds); - modificationCounter++; return this; } @@ -564,9 +651,7 @@ public class Sone { * @return This Sone (for method chaining) */ public synchronized Sone addLikedReplyId(String replyId) { - if (likedReplyIds.add(replyId)) { - modificationCounter++; - } + likedReplyIds.add(replyId); return this; } @@ -578,29 +663,200 @@ public class Sone { * @return This Sone (for method chaining) */ public synchronized Sone removeLikedReplyId(String replyId) { - if (likedReplyIds.remove(replyId)) { - modificationCounter++; - } + likedReplyIds.remove(replyId); return this; } /** - * Returns the modification counter. + * Returns the albums of this Sone. + * + * @return The albums of this Sone + */ + public List getAlbums() { + return Collections.unmodifiableList(albums); + } + + /** + * Returns a flattened list of all albums of this Sone. The resulting list + * contains parent albums before child albums so that the resulting list can + * be parsed in a single pass. + * + * @return The flattened albums + */ + public List getAllAlbums() { + List flatAlbums = new ArrayList(); + flatAlbums.addAll(albums); + int lastAlbumIndex = 0; + while (lastAlbumIndex < flatAlbums.size()) { + int previousAlbumCount = flatAlbums.size(); + for (Album album : new ArrayList(flatAlbums.subList(lastAlbumIndex, flatAlbums.size()))) { + flatAlbums.addAll(album.getAlbums()); + } + lastAlbumIndex = previousAlbumCount; + } + return flatAlbums; + } + + /** + * Returns all images of a Sone. Images of a album are inserted into this + * list before images of all child albums. * - * @return The modification counter + * @return The list of all images */ - public synchronized long getModificationCounter() { - return modificationCounter; + public List getAllImages() { + List allImages = new ArrayList(); + for (Album album : getAllAlbums()) { + allImages.addAll(album.getImages()); + } + return allImages; } /** - * Sets the modification counter. + * Adds an album to this Sone. * - * @param modificationCounter - * The new modification counter + * @param album + * The album to add */ - public synchronized void setModificationCounter(long modificationCounter) { - this.modificationCounter = modificationCounter; + public synchronized void addAlbum(Album album) { + Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check(); + albums.add(album); + } + + /** + * Sets the albums of this Sone. + * + * @param albums + * The albums of this Sone + */ + public synchronized void setAlbums(Collection albums) { + Validation.begin().isNotNull("Albums", albums).check(); + this.albums.clear(); + for (Album album : albums) { + addAlbum(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); + } + + /** + * Moves the given album up in this album’s albums. If the album is already + * the first album, nothing happens. + * + * @param album + * The album to move up + * @return The album that the given album swapped the place with, or + * null if the album did not change its place + */ + public Album moveAlbumUp(Album album) { + Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).isNull("Album Parent", album.getParent()).check(); + int oldIndex = albums.indexOf(album); + if (oldIndex <= 0) { + return null; + } + albums.remove(oldIndex); + albums.add(oldIndex - 1, album); + return albums.get(oldIndex); + } + + /** + * Moves the given album down in this album’s albums. If the album is + * already the last album, nothing happens. + * + * @param album + * The album to move down + * @return The album that the given album swapped the place with, or + * null if the album did not change its place + */ + public Album moveAlbumDown(Album album) { + Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).isNull("Album Parent", album.getParent()).check(); + int oldIndex = albums.indexOf(album); + if ((oldIndex < 0) || (oldIndex >= (albums.size() - 1))) { + return null; + } + albums.remove(oldIndex); + albums.add(oldIndex + 1, album); + return albums.get(oldIndex); + } + + /** + * Returns Sone-specific options. + * + * @return The options of this Sone + */ + public Options getOptions() { + return options; + } + + // + // FINGERPRINTABLE METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public synchronized String getFingerprint() { + StringBuilder fingerprint = new StringBuilder(); + fingerprint.append(profile.getFingerprint()); + + 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 (PostReply 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(); + } + + // + // INTERFACE Comparable + // + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(Sone sone) { + return NICE_NAME_COMPARATOR.compare(this, sone); } // @@ -631,7 +887,7 @@ public class Sone { */ @Override public String toString() { - return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri=" + insertUri + ",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() + ")]"; } }