Add enum for possible “show custom avatars” option values.
[Sone.git] / src / main / java / net / pterodactylus / sone / data / Sone.java
index c145b82..901d676 100644 (file)
@@ -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 <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class Sone implements Fingerprintable {
+public class Sone implements Fingerprintable, Comparable<Sone> {
+
+       /**
+        * The possible values for the “show custom avatars” option.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       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<Sone> NICE_NAME_COMPARATOR = new Comparator<Sone>() {
@@ -56,6 +85,69 @@ public class Sone implements Fingerprintable {
 
        };
 
+       /**
+        * Comparator that sorts Sones by last activity (least recent active first).
+        */
+       public static final Comparator<Sone> LAST_ACTIVITY_COMPARATOR = new Comparator<Sone>() {
+
+               @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<Sone> POST_COUNT_COMPARATOR = new Comparator<Sone>() {
+
+               /**
+                * {@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<Sone> IMAGE_COUNT_COMPARATOR = new Comparator<Sone>() {
+
+               /**
+                * {@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<Sone> EMPTY_SONE_FILTER = new Filter<Sone>() {
+
+               @Override
+               public boolean filterObject(Sone sone) {
+                       return sone.getTime() != 0;
+               }
+       };
+
+       /** Filter that matches all {@link Core#isLocalSone(Sone) local Sones}. */
+       public static final Filter<Sone> LOCAL_SONE_FILTER = new Filter<Sone>() {
+
+               @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<Sone> HAS_ALBUM_FILTER = new Filter<Sone>() {
+
+               @Override
+               public boolean filterObject(Sone sone) {
+                       return !sone.getAlbums().isEmpty();
+               }
+       };
+
        /** The logger. */
        private static final Logger logger = Logging.getLogger(Sone.class);
 
@@ -79,7 +171,7 @@ public class Sone implements Fingerprintable {
        private volatile long time;
 
        /** The profile of this Sone. */
-       private volatile Profile profile = new Profile();
+       private volatile Profile profile = new Profile(this);
 
        /** The client used by the Sone. */
        private volatile Client client;
@@ -91,7 +183,7 @@ public class Sone implements Fingerprintable {
        private final Set<Post> posts = Collections.synchronizedSet(new HashSet<Post>());
 
        /** All replies. */
-       private final Set<Reply> replies = Collections.synchronizedSet(new HashSet<Reply>());
+       private final Set<PostReply> replies = Collections.synchronizedSet(new HashSet<PostReply>());
 
        /** The IDs of all liked posts. */
        private final Set<String> likedPostIds = Collections.synchronizedSet(new HashSet<String>());
@@ -99,6 +191,12 @@ public class Sone implements Fingerprintable {
        /** The IDs of all liked replies. */
        private final Set<String> likedReplyIds = Collections.synchronizedSet(new HashSet<String>());
 
+       /** The albums of this Sone. */
+       private final List<Album> albums = Collections.synchronizedList(new ArrayList<Album>());
+
+       /** Sone-specific options. */
+       private final Options options = new Options();
+
        /**
         * Creates a new Sone.
         *
@@ -176,7 +274,7 @@ public class Sone implements Fingerprintable {
         */
        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 implements Fingerprintable {
         */
        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 implements Fingerprintable {
         */
        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;
@@ -315,19 +413,6 @@ public class Sone implements Fingerprintable {
        }
 
        /**
-        * 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<String> friends) {
-               friendSones.clear();
-               friendSones.addAll(friends);
-               return this;
-       }
-
-       /**
         * Returns whether this Sone has the given Sone as a friend Sone.
         *
         * @param friendSoneId
@@ -387,8 +472,10 @@ public class Sone implements Fingerprintable {
         * @return This Sone (for method chaining)
         */
        public synchronized Sone setPosts(Collection<Post> posts) {
-               this.posts.clear();
-               this.posts.addAll(posts);
+               synchronized (this) {
+                       this.posts.clear();
+                       this.posts.addAll(posts);
+               }
                return this;
        }
 
@@ -422,7 +509,7 @@ public class Sone implements Fingerprintable {
         *
         * @return All replies this Sone made
         */
-       public synchronized Set<Reply> getReplies() {
+       public synchronized Set<PostReply> getReplies() {
                return Collections.unmodifiableSet(replies);
        }
 
@@ -433,7 +520,7 @@ public class Sone implements Fingerprintable {
         *            The new (and only) replies of this Sone
         * @return This Sone (for method chaining)
         */
-       public synchronized Sone setReplies(Collection<Reply> replies) {
+       public synchronized Sone setReplies(Collection<PostReply> replies) {
                this.replies.clear();
                this.replies.addAll(replies);
                return this;
@@ -446,7 +533,7 @@ public class Sone implements Fingerprintable {
         * @param reply
         *            The reply to add
         */
-       public synchronized void addReply(Reply reply) {
+       public synchronized void addReply(PostReply reply) {
                if (reply.getSone().equals(this)) {
                        replies.add(reply);
                }
@@ -458,7 +545,7 @@ public class Sone implements Fingerprintable {
         * @param reply
         *            The reply to remove
         */
-       public synchronized void removeReply(Reply reply) {
+       public synchronized void removeReply(PostReply reply) {
                if (reply.getSone().equals(this)) {
                        replies.remove(reply);
                }
@@ -580,6 +667,135 @@ public class Sone implements Fingerprintable {
                return this;
        }
 
+       /**
+        * Returns the albums of this Sone.
+        *
+        * @return The albums of this Sone
+        */
+       public List<Album> 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<Album> getAllAlbums() {
+               List<Album> flatAlbums = new ArrayList<Album>();
+               flatAlbums.addAll(albums);
+               int lastAlbumIndex = 0;
+               while (lastAlbumIndex < flatAlbums.size()) {
+                       int previousAlbumCount = flatAlbums.size();
+                       for (Album album : new ArrayList<Album>(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 list of all images
+        */
+       public List<Image> getAllImages() {
+               List<Image> allImages = new ArrayList<Image>();
+               for (Album album : getAllAlbums()) {
+                       allImages.addAll(album.getImages());
+               }
+               return allImages;
+       }
+
+       /**
+        * 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);
+       }
+
+       /**
+        * Sets the albums of this Sone.
+        *
+        * @param albums
+        *            The albums of this Sone
+        */
+       public synchronized void setAlbums(Collection<? extends Album> 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
+        *         <code>null</code> 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
+        *         <code>null</code> 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
        //
@@ -590,26 +806,7 @@ public class Sone implements Fingerprintable {
        @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(profile.getFingerprint());
 
                fingerprint.append("Posts(");
                for (Post post : getPosts()) {
@@ -617,10 +814,10 @@ public class Sone implements Fingerprintable {
                }
                fingerprint.append(")");
 
-               List<Reply> replies = new ArrayList<Reply>(getReplies());
+               List<PostReply> replies = new ArrayList<PostReply>(getReplies());
                Collections.sort(replies, Reply.TIME_COMPARATOR);
                fingerprint.append("Replies(");
-               for (Reply reply : replies) {
+               for (PostReply reply : replies) {
                        fingerprint.append("Reply(").append(reply.getId()).append(')');
                }
                fingerprint.append(')');
@@ -641,10 +838,28 @@ public class Sone implements Fingerprintable {
                }
                fingerprint.append(')');
 
+               fingerprint.append("Albums(");
+               for (Album album : albums) {
+                       fingerprint.append(album.getFingerprint());
+               }
+               fingerprint.append(')');
+
                return fingerprint.toString();
        }
 
        //
+       // INTERFACE Comparable<Sone>
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public int compareTo(Sone sone) {
+               return NICE_NAME_COMPARATOR.compare(this, sone);
+       }
+
+       //
        // OBJECT METHODS
        //