X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fdata%2FSone.java;h=22dc593d3215deb12ffbf812c7fc854dbf275be9;hp=c2f9ae19585251698f0e2964afaa2700a0f4820d;hb=c2e868714435ac7c75d77d1911d0dfb00393d051;hpb=c3240e5c85d0cd49e346939f864cf93fa36daed0 diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index c2f9ae1..22dc593 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 @@ /* - * Sone - Sone.java - Copyright © 2010 David Roden + * Sone - Sone.java - Copyright © 2010–2013 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 @@ -17,31 +17,39 @@ package net.pterodactylus.sone.data; +import static com.google.common.base.Preconditions.*; +import static com.google.common.collect.FluentIterable.from; +import static java.util.Arrays.asList; +import static net.pterodactylus.sone.data.Album.FLATTENER; +import static net.pterodactylus.sone.data.Album.IMAGES; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; 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; +import com.google.common.base.Predicate; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.primitives.Ints; + /** * A Sone defines everything about a user: her profile, her status updates, her * replies, her likes and dislikes, etc. - *

+ *

* Operations that modify the Sone need to synchronize on the Sone in question. * * @author David ‘Bombe’ Roden @@ -106,9 +114,7 @@ public class Sone implements Fingerprintable, Comparable { }; - /** - * Comparator that sorts Sones by last activity (least recent active first). - */ + /** Comparator that sorts Sones by last activity (least recent active first). */ public static final Comparator LAST_ACTIVITY_COMPARATOR = new Comparator() { @Override @@ -137,35 +143,38 @@ public class Sone implements Fingerprintable, Comparable { */ @Override public int compare(Sone leftSone, Sone rightSone) { - return rightSone.getAllImages().size() - leftSone.getAllImages().size(); + int rightSoneImageCount = from(asList(rightSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size(); + int leftSoneImageCount = from(asList(leftSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size(); + /* sort descending. */ + return Ints.compare(rightSoneImageCount, leftSoneImageCount); } }; /** Filter to remove Sones that have not been downloaded. */ - public static final Filter EMPTY_SONE_FILTER = new Filter() { + public static final Predicate EMPTY_SONE_FILTER = new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(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() { + /** Filter that matches all {@link Sone#isLocal() local Sones}. */ + public static final Predicate LOCAL_SONE_FILTER = new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(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() { + public static final Predicate HAS_ALBUM_FILTER = new Predicate() { @Override - public boolean filterObject(Sone sone) { - return !sone.getAlbums().isEmpty(); + public boolean apply(Sone sone) { + return !sone.getRootAlbum().getAlbums().isEmpty(); } }; @@ -175,6 +184,9 @@ public class Sone implements Fingerprintable, Comparable { /** The ID of this Sone. */ private final String id; + /** Whether the Sone is local. */ + private final boolean local; + /** The identity of this Sone. */ private Identity identity; @@ -218,20 +230,23 @@ public class Sone implements Fingerprintable, Comparable { /** The IDs of all liked replies. */ private final Set likedReplyIds = new CopyOnWriteArraySet(); - /** The albums of this Sone. */ - private final List albums = new CopyOnWriteArrayList(); + /** The root album containing all albums. */ + private final Album rootAlbum = new Album().setSone(this); /** Sone-specific options. */ - private final Options options = new Options(); + private Options options = new Options(); /** * Creates a new Sone. * * @param id - * The ID of the Sone + * The ID of the Sone + * @param local + * {@code true} if the Sone is a local Sone, {@code false} otherwise */ - public Sone(String id) { + public Sone(String id, boolean local) { this.id = id; + this.local = local; } // @@ -261,10 +276,10 @@ public class Sone implements Fingerprintable, Comparable { * identity has to match this Sone’s {@link #getId()}. * * @param identity - * The identity of this Sone + * 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 + * 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)) { @@ -284,6 +299,15 @@ public class Sone implements Fingerprintable, Comparable { } /** + * Returns whether this Sone is a local Sone. + * + * @return {@code true} if this Sone is a local Sone, {@code false} otherwise + */ + public boolean isLocal() { + return local; + } + + /** * Returns the request URI of this Sone. * * @return The request URI of this Sone @@ -296,7 +320,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the request URI of this Sone. * * @param requestUri - * The request URI of this Sone + * The request URI of this Sone * @return This Sone (for method chaining) */ public Sone setRequestUri(FreenetURI requestUri) { @@ -305,7 +329,7 @@ public class Sone implements Fingerprintable, Comparable { return this; } if (!this.requestUri.equalsKeypair(requestUri)) { - logger.log(Level.WARNING, "Request URI %s tried to overwrite %s!", new Object[] { requestUri, this.requestUri }); + logger.log(Level.WARNING, String.format("Request URI %s tried to overwrite %s!", requestUri, this.requestUri)); return this; } return this; @@ -324,7 +348,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the insert URI of this Sone. * * @param insertUri - * The insert URI of this Sone + * The insert URI of this Sone * @return This Sone (for method chaining) */ public Sone setInsertUri(FreenetURI insertUri) { @@ -333,7 +357,7 @@ public class Sone implements Fingerprintable, Comparable { return this; } if (!this.insertUri.equalsKeypair(insertUri)) { - logger.log(Level.WARNING, "Request URI %s tried to overwrite %s!", new Object[] { insertUri, this.insertUri }); + logger.log(Level.WARNING, String.format("Request URI %s tried to overwrite %s!", insertUri, this.insertUri)); return this; } return this; @@ -350,15 +374,15 @@ public class Sone implements Fingerprintable, Comparable { /** * 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. + * greater than the current latest edition, the latest edition of this Sone is + * not changed. * * @param latestEdition - * The latest edition of this Sone + * The latest edition of this Sone */ public void setLatestEdition(long latestEdition) { if (!(latestEdition > this.latestEdition)) { - logger.log(Level.FINE, "New latest edition %d is not greater than current latest edition %d!", new Object[] { 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; @@ -377,7 +401,7 @@ public class Sone implements Fingerprintable, Comparable { * 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) + * The time of the update (in milliseconds since Jan 1, 1970 UTC) * @return This Sone (for method chaining) */ public Sone setTime(long time) { @@ -398,21 +422,20 @@ public class Sone implements Fingerprintable, Comparable { * Sets the new status of this Sone. * * @param status - * The new status of this Sone + * The new status of this Sone * @return This Sone * @throws IllegalArgumentException - * if {@code status} is {@code null} + * if {@code status} is {@code null} */ public Sone setStatus(SoneStatus status) { - Validation.begin().isNotNull("Sone Status", status).check(); - this.status = status; + this.status = checkNotNull(status, "status must not be null"); return this; } /** - * Returns a copy of the profile. If you want to update values in the - * profile of this Sone, update the values in the returned {@link Profile} - * and use {@link #setProfile(Profile)} to change the profile in this Sone. + * Returns a copy of the profile. If you want to update values in the profile + * of this Sone, update the values in the returned {@link Profile} and use + * {@link #setProfile(Profile)} to change the profile in this Sone. * * @return A copy of the profile */ @@ -421,12 +444,12 @@ public class Sone implements Fingerprintable, Comparable { } /** - * Sets the profile of this Sone. A copy of the given profile is stored so - * that subsequent modifications of the given profile are not reflected in - * this Sone! + * Sets the profile of this Sone. A copy of the given profile is stored so that + * subsequent modifications of the given profile are not reflected in this + * Sone! * * @param profile - * The profile to set + * The profile to set */ public void setProfile(Profile profile) { this.profile = new Profile(profile); @@ -445,7 +468,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the client used by this Sone. * * @param client - * The client used by this Sone, or {@code null} + * The client used by this Sone, or {@code null} * @return This Sone (for method chaining) */ public Sone setClient(Client client) { @@ -466,7 +489,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets whether this Sone is known. * * @param known - * {@code true} if this Sone is known, {@code false} otherwise + * {@code true} if this Sone is known, {@code false} otherwise * @return This Sone */ public Sone setKnown(boolean known) { @@ -488,9 +511,9 @@ public class Sone implements Fingerprintable, Comparable { * 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 + * 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); @@ -500,7 +523,7 @@ public class Sone implements Fingerprintable, Comparable { * Adds the given Sone as a friend Sone. * * @param friendSone - * The friend Sone to add + * The friend Sone to add * @return This Sone (for method chaining) */ public Sone addFriend(String friendSone) { @@ -514,7 +537,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given Sone as a friend Sone. * * @param friendSoneId - * The ID of the friend Sone to remove + * The ID of the friend Sone to remove * @return This Sone (for method chaining) */ public Sone removeFriend(String friendSoneId) { @@ -540,7 +563,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets all posts of this Sone at once. * * @param posts - * The new (and only) posts of this Sone + * The new (and only) posts of this Sone * @return This Sone (for method chaining) */ public Sone setPosts(Collection posts) { @@ -552,15 +575,15 @@ public class Sone implements Fingerprintable, Comparable { } /** - * Adds the given post to this Sone. The post will not be added if its - * {@link Post#getSone() Sone} is not this Sone. + * 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 + * The post to add */ public void addPost(Post post) { if (post.getSone().equals(this) && posts.add(post)) { - logger.log(Level.FINEST, "Adding %s to “%s”.", new Object[] { post, getName() }); + logger.log(Level.FINEST, String.format("Adding %s to “%s”.", post, getName())); } } @@ -568,7 +591,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given post from this Sone. * * @param post - * The post to remove + * The post to remove */ public void removePost(Post post) { if (post.getSone().equals(this)) { @@ -589,7 +612,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets all replies of this Sone at once. * * @param replies - * The new (and only) replies of this Sone + * The new (and only) replies of this Sone * @return This Sone (for method chaining) */ public Sone setReplies(Collection replies) { @@ -603,7 +626,7 @@ public class Sone implements Fingerprintable, Comparable { * nothing is added to this Sone. * * @param reply - * The reply to add + * The reply to add */ public void addReply(PostReply reply) { if (reply.getSone().equals(this)) { @@ -615,7 +638,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes a reply from this Sone. * * @param reply - * The reply to remove + * The reply to remove */ public void removeReply(PostReply reply) { if (reply.getSone().equals(this)) { @@ -636,7 +659,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the IDs of all liked posts. * * @param likedPostIds - * All liked posts’ IDs + * All liked posts’ IDs * @return This Sone (for method chaining) */ public Sone setLikePostIds(Set likedPostIds) { @@ -649,7 +672,7 @@ public class Sone implements Fingerprintable, Comparable { * Checks whether the given post ID is liked by this Sone. * * @param postId - * The ID of the post + * The ID of the post * @return {@code true} if this Sone likes the given post, {@code false} * otherwise */ @@ -661,7 +684,7 @@ public class Sone implements Fingerprintable, Comparable { * Adds the given post ID to the list of posts this Sone likes. * * @param postId - * The ID of the post + * The ID of the post * @return This Sone (for method chaining) */ public Sone addLikedPostId(String postId) { @@ -673,7 +696,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given post ID from the list of posts this Sone likes. * * @param postId - * The ID of the post + * The ID of the post * @return This Sone (for method chaining) */ public Sone removeLikedPostId(String postId) { @@ -694,7 +717,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the IDs of all liked replies. * * @param likedReplyIds - * All liked replies’ IDs + * All liked replies’ IDs * @return This Sone (for method chaining) */ public Sone setLikeReplyIds(Set likedReplyIds) { @@ -707,7 +730,7 @@ public class Sone implements Fingerprintable, Comparable { * Checks whether the given reply ID is liked by this Sone. * * @param replyId - * The ID of the reply + * The ID of the reply * @return {@code true} if this Sone likes the given reply, {@code false} * otherwise */ @@ -719,7 +742,7 @@ public class Sone implements Fingerprintable, Comparable { * Adds the given reply ID to the list of replies this Sone likes. * * @param replyId - * The ID of the reply + * The ID of the reply * @return This Sone (for method chaining) */ public Sone addLikedReplyId(String replyId) { @@ -731,7 +754,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given post ID from the list of replies this Sone likes. * * @param replyId - * The ID of the reply + * The ID of the reply * @return This Sone (for method chaining) */ public Sone removeLikedReplyId(String replyId) { @@ -740,192 +763,88 @@ public class Sone implements Fingerprintable, Comparable { } /** - * 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 list of all images - */ - public List getAllImages() { - List allImages = new ArrayList(); - for (Album album : getAllAlbums()) { - allImages.addAll(album.getImages()); - } - return allImages; - } - - /** - * Adds an album to this Sone. - * - * @param album - * The album to add - */ - public 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. + * Returns the root album that contains all visible albums of this Sone. * - * @param albums - * The albums of this Sone + * @return The root album of this Sone */ - public void setAlbums(Collection albums) { - Validation.begin().isNotNull("Albums", albums).check(); - this.albums.clear(); - for (Album album : albums) { - addAlbum(album); - } + public Album getRootAlbum() { + return rootAlbum; } /** - * Removes an album from this Sone. - * - * @param album - * The album to remove - */ - public 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. + * Returns Sone-specific options. * - * @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 + * @return The options of this Sone */ - 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); + public Options getOptions() { + return options; } /** - * Returns Sone-specific options. + * Sets the options of this Sone. * - * @return The options of this Sone + * @param options + * The options of this Sone */ - public Options getOptions() { - return options; + /* TODO - remove this method again, maybe add an option provider */ + public void setOptions(Options options) { + this.options = options; } // // FINGERPRINTABLE METHODS // - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public synchronized String getFingerprint() { - StringBuilder fingerprint = new StringBuilder(); - fingerprint.append(profile.getFingerprint()); + Hasher hash = Hashing.sha256().newHasher(); + hash.putString(profile.getFingerprint()); - fingerprint.append("Posts("); + hash.putString("Posts("); for (Post post : getPosts()) { - fingerprint.append("Post(").append(post.getId()).append(')'); + hash.putString("Post(").putString(post.getId()).putString(")"); } - fingerprint.append(")"); + hash.putString(")"); List replies = new ArrayList(getReplies()); Collections.sort(replies, Reply.TIME_COMPARATOR); - fingerprint.append("Replies("); + hash.putString("Replies("); for (PostReply reply : replies) { - fingerprint.append("Reply(").append(reply.getId()).append(')'); + hash.putString("Reply(").putString(reply.getId()).putString(")"); } - fingerprint.append(')'); + hash.putString(")"); List likedPostIds = new ArrayList(getLikedPostIds()); Collections.sort(likedPostIds); - fingerprint.append("LikedPosts("); + hash.putString("LikedPosts("); for (String likedPostId : likedPostIds) { - fingerprint.append("Post(").append(likedPostId).append(')'); + hash.putString("Post(").putString(likedPostId).putString(")"); } - fingerprint.append(')'); + hash.putString(")"); List likedReplyIds = new ArrayList(getLikedReplyIds()); Collections.sort(likedReplyIds); - fingerprint.append("LikedReplies("); + hash.putString("LikedReplies("); for (String likedReplyId : likedReplyIds) { - fingerprint.append("Reply(").append(likedReplyId).append(')'); + hash.putString("Reply(").putString(likedReplyId).putString(")"); } - fingerprint.append(')'); + hash.putString(")"); - fingerprint.append("Albums("); + hash.putString("Albums("); for (Album album : albums) { - fingerprint.append(album.getFingerprint()); + hash.putString(album.getFingerprint()); } - fingerprint.append(')'); + hash.putString(")"); - return fingerprint.toString(); + return hash.hash().toString(); } // // INTERFACE Comparable // - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public int compareTo(Sone sone) { return NICE_NAME_COMPARATOR.compare(this, sone); @@ -935,17 +854,13 @@ public class Sone implements Fingerprintable, Comparable { // OBJECT METHODS // - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public int hashCode() { return id.hashCode(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public boolean equals(Object object) { if (!(object instanceof Sone)) { @@ -954,9 +869,7 @@ public class Sone implements Fingerprintable, Comparable { return ((Sone) object).id.equals(id); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public String toString() { return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + ")]";