Return an Optional for the album image.
[Sone.git] / src / main / java / net / pterodactylus / sone / data / Album.java
1 /*
2  * Sone - Album.java - Copyright © 2011–2013 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 net.pterodactylus.sone.database.AlbumBuilder;
30 import net.pterodactylus.sone.database.ImageBuilder;
31
32 import com.google.common.base.Function;
33 import com.google.common.base.Optional;
34 import com.google.common.base.Predicate;
35 import com.google.common.collect.FluentIterable;
36 import com.google.common.collect.ImmutableList;
37
38 /**
39  * Container for images that can also contain nested {@link Album}s.
40  *
41  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
42  */
43 public interface Album extends Identified, Fingerprintable {
44
45         /** Compares two {@link Album}s by {@link #getTitle()}. */
46         Comparator<Album> TITLE_COMPARATOR = new Comparator<Album>() {
47
48                 @Override
49                 public int compare(Album leftAlbum, Album rightAlbum) {
50                         return leftAlbum.getTitle().compareToIgnoreCase(rightAlbum.getTitle());
51                 }
52         };
53
54         /** Function that flattens the given album and all albums beneath it. */
55         Function<Album, List<Album>> FLATTENER = new Function<Album, List<Album>>() {
56
57                 @Override
58                 @Nonnull
59                 public List<Album> apply(Album album) {
60                         if (album == null) {
61                                 return emptyList();
62                         }
63                         List<Album> albums = new ArrayList<Album>();
64                         albums.add(album);
65                         for (Album subAlbum : album.getAlbums()) {
66                                 albums.addAll(FluentIterable.from(ImmutableList.of(subAlbum)).transformAndConcat(FLATTENER).toList());
67                         }
68                         return albums;
69                 }
70         };
71
72         /** Function that transforms an album into the images it contains. */
73         Function<Album, List<Image>> IMAGES = new Function<Album, List<Image>>() {
74
75                 @Override
76                 @Nonnull
77                 public List<Image> apply(Album album) {
78                         return (album != null) ? album.getImages() : Collections.<Image>emptyList();
79                 }
80         };
81
82         /**
83          * Filter that removes all albums that do not have any images in any album
84          * below it.
85          */
86         Predicate<Album> NOT_EMPTY = new Predicate<Album>() {
87
88                 @Override
89                 public boolean apply(Album album) {
90                         /* so, we flatten all albums below the given one and check whether at least one album… */
91                         return FluentIterable.from(asList(album)).transformAndConcat(FLATTENER).anyMatch(new Predicate<Album>() {
92
93                                 @Override
94                                 public boolean apply(Album album) {
95                                         /* …contains any inserted images. */
96                                         return !album.getImages().isEmpty() && FluentIterable.from(album.getImages()).allMatch(new Predicate<Image>() {
97
98                                                 @Override
99                                                 public boolean apply(Image input) {
100                                                         return input.isInserted();
101                                                 }
102                                         });
103                                 }
104                         });
105                 }
106         };
107
108         /**
109          * Returns the ID of this album.
110          *
111          * @return The ID of this album
112          */
113         String getId();
114
115         /**
116          * Returns the Sone this album belongs to.
117          *
118          * @return The Sone this album belongs to
119          */
120         Sone getSone();
121
122         List<Album> getAlbums();
123
124         /**
125          * Returns the images in this album.
126          *
127          * @return The images in this album
128          */
129         List<Image> getImages();
130
131         Optional<Image> getAlbumImage();
132
133         /**
134          * Returns whether this album contains any other albums or images.
135          *
136          * @return {@code true} if this album is empty, {@code false} otherwise
137          */
138         boolean isEmpty();
139
140         /**
141          * Returns whether this album is an identitiy’s root album.
142          *
143          * @return {@code true} if this album is an identity’s root album, {@code
144          *         false} otherwise
145          */
146         boolean isRoot();
147
148         /**
149          * Returns the parent album of this album.
150          *
151          * @return The parent album of this album, or {@code null} if this album does
152          *         not have a parent
153          */
154         Album getParent();
155
156         /**
157          * Returns the title of this album.
158          *
159          * @return The title of this album
160          */
161         String getTitle();
162
163         /**
164          * Returns the description of this album.
165          *
166          * @return The description of this album
167          */
168         String getDescription();
169
170         AlbumBuilder newAlbumBuilder() throws IllegalStateException;
171
172         ImageBuilder newImageBuilder() throws IllegalStateException;
173
174         /**
175          * Returns a modifier for this album.
176          *
177          * @return A modifier for this album
178          * @throws IllegalStateException
179          *              if this album can not be modified
180          */
181         Modifier modify() throws IllegalStateException;
182
183         void moveUp();
184
185         void moveDown();
186
187         void remove() throws IllegalStateException;
188
189         /**
190          * Allows modifying an album. Modifications are only performed once {@link
191          * #update()} has succesfully returned a new album with the modifications
192          * made.
193          *
194          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
195          */
196         interface Modifier {
197
198                 Modifier setTitle(String title);
199
200                 Modifier setDescription(String description);
201
202                 Modifier setAlbumImage(String imageId);
203
204                 Album update() throws IllegalStateException;
205
206         }
207
208 }