5a02f199c65b67145299a5e8677248ec115e1d9d
[Sone.git] / src / main / java / net / pterodactylus / sone / data / Album.java
1 /*
2  * Sone - Album.java - Copyright © 2011–2016 David Roden
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.pterodactylus.sone.data;
19
20 import static java.util.Arrays.asList;
21 import static java.util.Collections.emptyList;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.List;
27 import javax.annotation.Nonnull;
28
29 import com.google.common.base.Function;
30 import com.google.common.base.Predicate;
31 import com.google.common.collect.FluentIterable;
32 import com.google.common.collect.ImmutableList;
33
34 /**
35  * Container for images that can also contain nested {@link Album}s.
36  *
37  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
38  */
39 public interface Album extends Identified, Fingerprintable {
40
41         /** Compares two {@link Album}s by {@link #getTitle()}. */
42         Comparator<Album> TITLE_COMPARATOR = new Comparator<Album>() {
43
44                 @Override
45                 public int compare(Album leftAlbum, Album rightAlbum) {
46                         return leftAlbum.getTitle().compareToIgnoreCase(rightAlbum.getTitle());
47                 }
48         };
49
50         /** Function that flattens the given album and all albums beneath it. */
51         Function<Album, List<Album>> FLATTENER = new Function<Album, List<Album>>() {
52
53                 @Override
54                 @Nonnull
55                 public List<Album> apply(Album album) {
56                         if (album == null) {
57                                 return emptyList();
58                         }
59                         List<Album> albums = new ArrayList<Album>();
60                         albums.add(album);
61                         for (Album subAlbum : album.getAlbums()) {
62                                 albums.addAll(FluentIterable.from(ImmutableList.of(subAlbum)).transformAndConcat(FLATTENER).toList());
63                         }
64                         return albums;
65                 }
66         };
67
68         /** Function that transforms an album into the images it contains. */
69         Function<Album, List<Image>> IMAGES = new Function<Album, List<Image>>() {
70
71                 @Override
72                 @Nonnull
73                 public List<Image> apply(Album album) {
74                         return (album != null) ? album.getImages() : Collections.<Image>emptyList();
75                 }
76         };
77
78         /**
79          * Filter that removes all albums that do not have any images in any album
80          * below it.
81          */
82         Predicate<Album> NOT_EMPTY = new Predicate<Album>() {
83
84                 @Override
85                 public boolean apply(Album album) {
86                         /* so, we flatten all albums below the given one and check whether at least one album… */
87                         return FluentIterable.from(asList(album)).transformAndConcat(FLATTENER).anyMatch(new Predicate<Album>() {
88
89                                 @Override
90                                 public boolean apply(Album album) {
91                                         /* …contains any inserted images. */
92                                         return !album.getImages().isEmpty() && FluentIterable.from(album.getImages()).allMatch(new Predicate<Image>() {
93
94                                                 @Override
95                                                 public boolean apply(Image input) {
96                                                         return input.isInserted();
97                                                 }
98                                         });
99                                 }
100                         });
101                 }
102         };
103
104         /**
105          * Returns the ID of this album.
106          *
107          * @return The ID of this album
108          */
109         String getId();
110
111         /**
112          * Returns the Sone this album belongs to.
113          *
114          * @return The Sone this album belongs to
115          */
116         Sone getSone();
117
118         /**
119          * Returns the nested albums.
120          *
121          * @return The nested albums
122          */
123         List<Album> getAlbums();
124
125         /**
126          * Adds an album to this album.
127          *
128          * @param album
129          *              The album to add
130          */
131         void addAlbum(Album album);
132
133         /**
134          * Removes an album from this album.
135          *
136          * @param album
137          *              The album to remove
138          */
139         void removeAlbum(Album album);
140
141         /**
142          * Moves the given album up in this album’s albums. If the album is already the
143          * first album, nothing happens.
144          *
145          * @param album
146          *              The album to move up
147          * @return The album that the given album swapped the place with, or
148          *         <code>null</code> if the album did not change its place
149          */
150         Album moveAlbumUp(Album album);
151
152         /**
153          * Moves the given album down in this album’s albums. If the album is already
154          * the last album, nothing happens.
155          *
156          * @param album
157          *              The album to move down
158          * @return The album that the given album swapped the place with, or
159          *         <code>null</code> if the album did not change its place
160          */
161         Album moveAlbumDown(Album album);
162
163         /**
164          * Returns the images in this album.
165          *
166          * @return The images in this album
167          */
168         List<Image> getImages();
169
170         /**
171          * Adds the given image to this album.
172          *
173          * @param image
174          *              The image to add
175          */
176         void addImage(Image image);
177
178         /**
179          * Removes the given image from this album.
180          *
181          * @param image
182          *              The image to remove
183          */
184         void removeImage(Image image);
185
186         /**
187          * Moves the given image up in this album’s images. If the image is already the
188          * first image, nothing happens.
189          *
190          * @param image
191          *              The image to move up
192          * @return The image that the given image swapped the place with, or
193          *         <code>null</code> if the image did not change its place
194          */
195         Image moveImageUp(Image image);
196
197         /**
198          * Moves the given image down in this album’s images. If the image is already
199          * the last image, nothing happens.
200          *
201          * @param image
202          *              The image to move down
203          * @return The image that the given image swapped the place with, or
204          *         <code>null</code> if the image did not change its place
205          */
206         Image moveImageDown(Image image);
207
208         /**
209          * Returns whether this album contains any other albums or images.
210          *
211          * @return {@code true} if this album is empty, {@code false} otherwise
212          */
213         boolean isEmpty();
214
215         /**
216          * Returns whether this album is an identitiy’s root album.
217          *
218          * @return {@code true} if this album is an identity’s root album, {@code
219          *         false} otherwise
220          */
221         boolean isRoot();
222
223         /**
224          * Returns the parent album of this album.
225          *
226          * @return The parent album of this album, or {@code null} if this album does
227          *         not have a parent
228          */
229         Album getParent();
230
231         /**
232          * Sets the parent album of this album.
233          *
234          * @param parent
235          *              The new parent album of this album
236          * @return This album
237          */
238         Album setParent(Album parent);
239
240         /**
241          * Removes the parent album of this album.
242          *
243          * @return This album
244          */
245         Album removeParent();
246
247         /**
248          * Returns the title of this album.
249          *
250          * @return The title of this album
251          */
252         String getTitle();
253
254         /**
255          * Returns the description of this album.
256          *
257          * @return The description of this album
258          */
259         String getDescription();
260
261         /**
262          * Returns a modifier for this album.
263          *
264          * @return A modifier for this album
265          * @throws IllegalStateException
266          *              if this album can not be modified
267          */
268         Modifier modify() throws IllegalStateException;
269
270         /**
271          * Allows modifying an album. Modifications are only performed once {@link
272          * #update()} has succesfully returned a new album with the modifications
273          * made.
274          *
275          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
276          */
277         interface Modifier {
278
279                 Modifier setTitle(String title);
280
281                 Modifier setDescription(String description);
282
283                 Album update() throws IllegalStateException;
284
285                 class AlbumTitleMustNotBeEmpty extends IllegalStateException { }
286
287         }
288
289 }