Insert a root album into all Sones to get rid of album manipulation in the Sone.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 11 Jul 2013 20:44:56 +0000 (22:44 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 11 Jul 2013 20:44:56 +0000 (22:44 +0200)
13 files changed:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/SoneDownloader.java
src/main/java/net/pterodactylus/sone/core/SoneInserter.java
src/main/java/net/pterodactylus/sone/data/Album.java
src/main/java/net/pterodactylus/sone/data/Sone.java
src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java
src/main/java/net/pterodactylus/sone/template/SoneAccessor.java
src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java
src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java
src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java
src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java
src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java
src/main/resources/templates/imageBrowser.html

index af2c3bf..5103b7d 100644 (file)
@@ -1035,13 +1035,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        database.storePostReplies(sone, sone.getReplies());
                        synchronized (albums) {
                                synchronized (images) {
-                                       for (Album album : storedSone.get().getAlbums()) {
+                                       for (Album album : storedSone.get().getRootAlbum().getAlbums()) {
                                                albums.remove(album.getId());
                                                for (Image image : album.getImages()) {
                                                        images.remove(image.getId());
                                                }
                                        }
-                                       for (Album album : sone.getAlbums()) {
+                                       for (Album album : sone.getRootAlbum().getAlbums()) {
                                                albums.put(album.getId(), album);
                                                for (Image image : album.getImages()) {
                                                        images.put(image.getId(), image);
@@ -1319,7 +1319,12 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        for (String friendId : friends) {
                                followSone(sone, friendId);
                        }
-                       sone.setAlbums(topLevelAlbums);
+                       for (Album album : sone.getRootAlbum().getAlbums()) {
+                               sone.getRootAlbum().removeAlbum(album);
+                       }
+                       for (Album album : topLevelAlbums) {
+                               sone.getRootAlbum().addAlbum(album);
+                       }
                        soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint);
                }
                synchronized (knownSones) {
@@ -1584,7 +1589,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         * @return The new album
         */
        public Album createAlbum(Sone sone) {
-               return createAlbum(sone, null);
+               return createAlbum(sone, sone.getRootAlbum());
        }
 
        /**
@@ -1603,11 +1608,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        albums.put(album.getId(), album);
                }
                album.setSone(sone);
-               if (parent != null) {
-                       parent.addAlbum(album);
-               } else {
-                       sone.addAlbum(album);
-               }
+               parent.addAlbum(album);
                return album;
        }
 
@@ -1624,11 +1625,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                if (!album.isEmpty()) {
                        return;
                }
-               if (album.getParent() == null) {
-                       album.getSone().removeAlbum(album);
-               } else {
-                       album.getParent().removeAlbum(album);
-               }
+               album.getParent().removeAlbum(album);
                synchronized (albums) {
                        albums.remove(album.getId());
                }
@@ -1883,7 +1880,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
 
                        /* save albums. first, collect in a flat structure, top-level first. */
-                       List<Album> albums = FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList();
+                       List<Album> albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList();
 
                        int albumCounter = 0;
                        for (Album album : albums) {
@@ -1891,7 +1888,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId());
                                configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle());
                                configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription());
-                               configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent() == null ? null : album.getParent().getId());
+                               configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId());
                                configuration.getStringValue(albumPrefix + "/AlbumImage").setValue(album.getAlbumImage() == null ? null : album.getAlbumImage().getId());
                        }
                        configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null);
index 5b56de8..9af6328 100644 (file)
@@ -518,7 +518,9 @@ public class SoneDownloader extends AbstractService {
                        sone.setReplies(replies);
                        sone.setLikePostIds(likedPostIds);
                        sone.setLikeReplyIds(likedReplyIds);
-                       sone.setAlbums(topLevelAlbums);
+                       for (Album album : topLevelAlbums) {
+                               sone.getRootAlbum().addAlbum(album);
+                       }
                }
 
                return sone;
index 01e414d..a1bb903 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.core;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static net.pterodactylus.sone.data.Album.NOT_EMPTY;
 
 import java.io.InputStreamReader;
 import java.io.StringWriter;
@@ -308,7 +309,7 @@ public class SoneInserter extends AbstractService {
                        soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies()));
                        soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
                        soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
-                       soneProperties.put("albums", FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList());
+                       soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(NOT_EMPTY).toList());
                }
 
                //
index cb7b4f3..aa14ede 100644 (file)
@@ -69,6 +69,15 @@ public class Album implements Fingerprintable {
                }
        };
 
+       /** Function that transforms an album into the images it contains. */
+       public static final Function<Album, List<Image>> IMAGES = new Function<Album, List<Image>>() {
+
+               @Override
+               public List<Image> apply(Album album) {
+                       return album.getImages();
+               }
+       };
+
        /**
         * Filter that removes all albums that do not have any images in any album
         * below it.
@@ -186,7 +195,6 @@ public class Album implements Fingerprintable {
        public void addAlbum(Album album) {
                checkNotNull(album, "album must not be null");
                checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
-               checkState((this.parent == null) || (this.parent.equals(album.parent)), "album must not already be set to some other Sone");
                album.setParent(this);
                if (!albums.contains(album)) {
                        albums.add(album);
index 4f4fd98..22dc593 100644 (file)
 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;
@@ -25,7 +29,6 @@ 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;
@@ -39,9 +42,9 @@ import net.pterodactylus.util.logging.Logging;
 import freenet.keys.FreenetURI;
 
 import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
 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
@@ -140,7 +143,10 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
                 */
                @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);
                }
        };
 
@@ -168,7 +174,7 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
 
                @Override
                public boolean apply(Sone sone) {
-                       return !sone.getAlbums().isEmpty();
+                       return !sone.getRootAlbum().getAlbums().isEmpty();
                }
        };
 
@@ -224,8 +230,8 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
        /** The IDs of all liked replies. */
        private final Set<String> likedReplyIds = new CopyOnWriteArraySet<String>();
 
-       /** The albums of this Sone. */
-       private final List<Album> albums = new CopyOnWriteArrayList<Album>();
+       /** The root album containing all albums. */
+       private final Album rootAlbum = new Album().setSone(this);
 
        /** Sone-specific options. */
        private Options options = new Options();
@@ -757,110 +763,12 @@ public class Sone implements Fingerprintable, Comparable<Sone> {
        }
 
        /**
-        * Returns the albums of this Sone.
+        * Returns the root album that contains all visible albums of this Sone.
         *
-        * @return The albums of this Sone
+        * @return The root album of this Sone
         */
-       public List<Album> getAlbums() {
-               return Collections.unmodifiableList(albums);
-       }
-
-       /**
-        * 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 : FluentIterable.from(getAlbums()).transformAndConcat(Album.FLATTENER).toList()) {
-                       allImages.addAll(album.getImages());
-               }
-               return allImages;
-       }
-
-       /**
-        * Adds an album to this Sone.
-        *
-        * @param album
-        *              The album to add
-        */
-       public void addAlbum(Album album) {
-               checkNotNull(album, "album must not be null");
-               checkArgument(album.getSone().equals(this), "album must belong to this Sone");
-               if (!albums.contains(album)) {
-                       albums.add(album);
-               }
-       }
-
-       /**
-        * Sets the albums of this Sone.
-        *
-        * @param albums
-        *              The albums of this Sone
-        */
-       public void setAlbums(Collection<? extends Album> albums) {
-               checkNotNull(albums, "albums must not be null");
-               this.albums.clear();
-               for (Album album : albums) {
-                       addAlbum(album);
-               }
-       }
-
-       /**
-        * Removes an album from this Sone.
-        *
-        * @param album
-        *              The album to remove
-        */
-       public void removeAlbum(Album album) {
-               checkNotNull(album, "album must not be null");
-               checkArgument(album.getSone().equals(this), "album must belong to this Sone");
-               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) {
-               checkNotNull(album, "album must not be null");
-               checkArgument(album.getSone().equals(this), "album must belong to this Sone");
-               checkArgument(album.getParent() == null, "album must not have a parent");
-               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) {
-               checkNotNull(album, "album must not be null");
-               checkArgument(album.getSone().equals(this), "album must belong to this Sone");
-               checkArgument(album.getParent() == null, "album must not have a parent");
-               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 Album getRootAlbum() {
+               return rootAlbum;
        }
 
        /**
index 96891e5..fcd7a70 100644 (file)
@@ -45,7 +45,7 @@ public class AlbumAccessor extends ReflectionAccessor {
                if ("backlinks".equals(member)) {
                        List<Map<String, String>> backlinks = new ArrayList<Map<String, String>>();
                        Album currentAlbum = album;
-                       while (currentAlbum != null) {
+                       while (!currentAlbum.equals(album.getSone().getRootAlbum())) {
                                backlinks.add(0, createLink("imageBrowser.html?album=" + currentAlbum.getId(), currentAlbum.getTitle()));
                                currentAlbum = currentAlbum.getParent();
                        }
index c54fc0d..ab872d9 100644 (file)
 
 package net.pterodactylus.sone.template;
 
+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.logging.Level;
 import java.util.logging.Logger;
 
@@ -111,6 +116,8 @@ public class SoneAccessor extends ReflectionAccessor {
                                return new Trust(null, null, null);
                        }
                        return trust;
+               } else if (member.equals("allImages")) {
+                       return from(asList(sone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES);
                }
                return super.get(templateContext, object, member);
        }
index 95ca0da..9e9cd9e 100644 (file)
@@ -64,6 +64,9 @@ public class CreateAlbumPage extends SoneTemplatePage {
                        Sone currentSone = getCurrentSone(request.getToadletContext());
                        String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
                        Album parent = webInterface.getCore().getAlbum(parentId, false);
+                       if (parentId.equals("")) {
+                               parent = currentSone.getRootAlbum();
+                       }
                        Album album = webInterface.getCore().createAlbum(currentSone, parent);
                        album.setTitle(name).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description));
                        webInterface.getCore().touchConfiguration();
index 3dfb8b4..391e9ff 100644 (file)
@@ -62,7 +62,7 @@ public class DeleteAlbumPage extends SoneTemplatePage {
                        }
                        Album parentAlbum = album.getParent();
                        webInterface.getCore().deleteAlbum(album);
-                       if (parentAlbum == null) {
+                       if (parentAlbum.equals(album.getSone().getRootAlbum())) {
                                throw new RedirectException("imageBrowser.html?sone=" + album.getSone().getId());
                        }
                        throw new RedirectException("imageBrowser.html?album=" + parentAlbum.getId());
index 8ef03e3..c66ae05 100644 (file)
@@ -61,20 +61,10 @@ public class EditAlbumPage extends SoneTemplatePage {
                                throw new RedirectException("noPermission.html");
                        }
                        if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveLeft", 4))) {
-                               if (album.getParent() == null) {
-                                       currentSone.moveAlbumUp(album);
-                                       webInterface.getCore().touchConfiguration();
-                                       throw new RedirectException("imageBrowser.html?sone=" + currentSone.getId());
-                               }
                                album.getParent().moveAlbumUp(album);
                                webInterface.getCore().touchConfiguration();
                                throw new RedirectException("imageBrowser.html?album=" + album.getParent().getId());
                        } else if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveRight", 4))) {
-                               if (album.getParent() == null) {
-                                       currentSone.moveAlbumDown(album);
-                                       webInterface.getCore().touchConfiguration();
-                                       throw new RedirectException("imageBrowser.html?sone=" + currentSone.getId());
-                               }
                                album.getParent().moveAlbumDown(album);
                                webInterface.getCore().touchConfiguration();
                                throw new RedirectException("imageBrowser.html?album=" + album.getParent().getId());
index 9dec1dc..766f018 100644 (file)
@@ -17,6 +17,7 @@
 
 package net.pterodactylus.sone.web;
 
+import static com.google.common.collect.FluentIterable.from;
 import static net.pterodactylus.sone.data.Album.FLATTENER;
 import static net.pterodactylus.sone.data.Album.NOT_EMPTY;
 import static net.pterodactylus.sone.data.Album.TITLE_COMPARATOR;
@@ -27,7 +28,6 @@ import java.util.Collections;
 import java.util.List;
 
 import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
 
 import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Image;
@@ -94,7 +94,7 @@ public class ImageBrowserPage extends SoneTemplatePage {
                        templateContext.set("galleryRequested", true);
                        List<Album> albums = new ArrayList<Album>();
                        for (Sone sone : webInterface.getCore().getSones()) {
-                               albums.addAll(FluentIterable.from(sone.getAlbums()).transformAndConcat(FLATTENER).filter(NOT_EMPTY).toList());
+                               albums.addAll(from(sone.getRootAlbum().getAlbums()).transformAndConcat(FLATTENER).filter(NOT_EMPTY).toList());
                        }
                        Collections.sort(albums, TITLE_COMPARATOR);
                        Pagination<Album> albumPagination = new Pagination<Album>(albums, 12).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
index 1bd94ca..5d587db 100644 (file)
@@ -57,12 +57,12 @@ public class EditAlbumAjaxPage extends JsonPage {
                        return createErrorJsonObject("not-authorized");
                }
                if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) {
-                       Album swappedAlbum = (album.getParent() != null) ? album.getParent().moveAlbumUp(album) : album.getSone().moveAlbumUp(album);
+                       Album swappedAlbum = album.getParent().moveAlbumUp(album);
                        webInterface.getCore().touchConfiguration();
                        return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId());
                }
                if ("true".equals(request.getHttpRequest().getParam("moveRight"))) {
-                       Album swappedAlbum = (album.getParent() != null) ? album.getParent().moveAlbumDown(album) : album.getSone().moveAlbumDown(album);
+                       Album swappedAlbum = album.getParent().moveAlbumDown(album);
                        webInterface.getCore().touchConfiguration();
                        return createSuccessJsonObject().put("sourceAlbumId", album.getId()).put("destinationAlbumId", swappedAlbum.getId());
                }
index a80680f..da8d032 100644 (file)
                                <div class="backlink"><a href="imageBrowser.html?sone=<%sone.id|html>"><%sone.niceName|html></a></div>
                        </div>
 
-                       <%include include/browseAlbums.html albums=sone.albums>
+                       <%include include/browseAlbums.html albums=sone.rootAlbum.albums>
 
                        <%if sone.local>
                                <div class="show-create-album hidden toggle-link"><a class="small-link">» <%= View.CreateAlbum.Title|l10n|html></a></div>