Use images in correct order to generate fingerprint.
[Sone.git] / src / main / java / net / pterodactylus / sone / data / Album.java
1 /*
2  * Sone - Album.java - Copyright © 2011 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 java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.UUID;
25
26 import net.pterodactylus.util.collection.Mapper;
27 import net.pterodactylus.util.collection.Mappers;
28 import net.pterodactylus.util.object.Default;
29 import net.pterodactylus.util.validation.Validation;
30
31 /**
32  * Container for images that can also contain nested {@link Album}s.
33  *
34  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
35  */
36 public class Album implements Fingerprintable {
37
38         /** The ID of this album. */
39         private final String id;
40
41         /** The Sone this album belongs to. */
42         private Sone sone;
43
44         /** Nested albums. */
45         private final List<Album> albums = new ArrayList<Album>();
46
47         /** The image IDs in order. */
48         private final List<String> imageIds = new ArrayList<String>();
49
50         /** The images in this album. */
51         private final Map<String, Image> images = new HashMap<String, Image>();
52
53         /** The parent album. */
54         private Album parent;
55
56         /** The title of this album. */
57         private String title;
58
59         /** The description of this album. */
60         private String description;
61
62         /** The ID of the album picture. */
63         private String albumImage;
64
65         /**
66          * Creates a new album with a random ID.
67          */
68         public Album() {
69                 this(UUID.randomUUID().toString());
70         }
71
72         /**
73          * Creates a new album with the given ID.
74          *
75          * @param id
76          *            The ID of the album
77          */
78         public Album(String id) {
79                 Validation.begin().isNotNull("Album ID", id).check();
80                 this.id = id;
81         }
82
83         //
84         // ACCESSORS
85         //
86
87         /**
88          * Returns the ID of this album.
89          *
90          * @return The ID of this album
91          */
92         public String getId() {
93                 return id;
94         }
95
96         /**
97          * Returns the Sone this album belongs to.
98          *
99          * @return The Sone this album belongs to
100          */
101         public Sone getSone() {
102                 return sone;
103         }
104
105         /**
106          * Sets the owner of the album. The owner can only be set as long as the
107          * current owner is {@code null}.
108          *
109          * @param sone
110          *            The album owner
111          * @return This album
112          */
113         public Album setSone(Sone sone) {
114                 Validation.begin().isNotNull("New Album Owner", sone).isEither("Old Album Owner", this.sone, null, sone).check();
115                 this.sone = sone;
116                 return this;
117         }
118
119         /**
120          * Returns the nested albums.
121          *
122          * @return The nested albums
123          */
124         public List<Album> getAlbums() {
125                 return new ArrayList<Album>(albums);
126         }
127
128         /**
129          * Adds an album to this album.
130          *
131          * @param album
132          *            The album to add
133          */
134         public void addAlbum(Album album) {
135                 Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEither("Old Album Parent", this.parent, null, album.parent).check();
136                 album.setParent(this);
137                 if (!albums.contains(album)) {
138                         albums.add(album);
139                 }
140         }
141
142         /**
143          * Removes an album from this album.
144          *
145          * @param album
146          *            The album to remove
147          */
148         public void removeAlbum(Album album) {
149                 Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check();
150                 albums.remove(album);
151                 album.removeParent();
152         }
153
154         /**
155          * Returns the images in this album.
156          *
157          * @return The images in this album
158          */
159         public List<Image> getImages() {
160                 return Mappers.mappedList(imageIds, new Mapper<String, Image>() {
161
162                         @Override
163                         @SuppressWarnings("synthetic-access")
164                         public Image map(String imageId) {
165                                 return images.get(imageId);
166                         }
167
168                 });
169         }
170
171         /**
172          * Adds the given image to this album.
173          *
174          * @param image
175          *            The image to add
176          */
177         public void addImage(Image image) {
178                 Validation.begin().isNotNull("Image", image).check().isNotNull("Image Owner", image.getSone()).check().isEqual("Image Owner", image.getSone(), sone).check();
179                 if (image.getAlbum() != null) {
180                         image.getAlbum().removeImage(image);
181                 }
182                 image.setAlbum(this);
183                 if (imageIds.isEmpty() && (albumImage == null)) {
184                         albumImage = image.getId();
185                 }
186                 if (!imageIds.contains(image.getId())) {
187                         imageIds.add(image.getId());
188                         images.put(image.getId(), image);
189                 }
190         }
191
192         /**
193          * Removes the given image from this album.
194          *
195          * @param image
196          *            The image to remove
197          */
198         public void removeImage(Image image) {
199                 Validation.begin().isNotNull("Image", image).check().isEqual("Image Owner", image.getSone(), sone).check();
200                 imageIds.remove(image.getId());
201                 images.remove(image.getId());
202                 if (image.getId().equals(albumImage)) {
203                         if (images.isEmpty()) {
204                                 albumImage = null;
205                         } else {
206                                 albumImage = images.values().iterator().next().getId();
207                         }
208                 }
209         }
210
211         /**
212          * Moves the given image up in this album’s images. If the image is already
213          * the first image, nothing happens.
214          *
215          * @param image
216          *            The image to move up
217          * @return The image that the given image swapped the place with, or
218          *         <code>null</code> if the image did not change its place
219          */
220         public Image moveImageUp(Image image) {
221                 Validation.begin().isNotNull("Image", image).check().isEqual("Image Album", image.getAlbum(), this).isEqual("Album Owner", image.getAlbum().getSone(), sone).check();
222                 int oldIndex = imageIds.indexOf(image.getId());
223                 if (oldIndex <= 0) {
224                         return null;
225                 }
226                 imageIds.remove(image.getId());
227                 imageIds.add(oldIndex - 1, image.getId());
228                 return images.get(imageIds.get(oldIndex));
229         }
230
231         /**
232          * Move the given image down in this album’s images. If the image is already
233          * the last image, nothing happens.
234          *
235          * @param image
236          *            The image to move down
237          * @return The image that the given image swapped the place with, or
238          *         <code>null</code> if the image did not change its place
239          */
240         public Image moveImageDown(Image image) {
241                 Validation.begin().isNotNull("Image", image).check().isEqual("Image Album", image.getAlbum(), this).isEqual("Album Owner", image.getAlbum().getSone(), sone).check();
242                 int oldIndex = imageIds.indexOf(image.getId());
243                 if ((oldIndex == -1) || (oldIndex >= (imageIds.size() - 1))) {
244                         return null;
245                 }
246                 imageIds.remove(image.getId());
247                 imageIds.add(oldIndex + 1, image.getId());
248                 return images.get(imageIds.get(oldIndex));
249         }
250
251         /**
252          * Returns the album image of this album, or {@code null} if no album image
253          * has been set.
254          *
255          * @return The image to show when this album is listed
256          */
257         public Image getAlbumImage() {
258                 if (albumImage == null) {
259                         return null;
260                 }
261                 return Default.forNull(images.get(albumImage), images.values().iterator().next());
262         }
263
264         /**
265          * Sets the ID of the album image.
266          *
267          * @param id
268          *            The ID of the album image
269          * @return This album
270          */
271         public Album setAlbumImage(String id) {
272                 this.albumImage = id;
273                 return this;
274         }
275
276         /**
277          * Returns whether this album contains any other albums or images.
278          *
279          * @return {@code true} if this album is empty, {@code false} otherwise
280          */
281         public boolean isEmpty() {
282                 return albums.isEmpty() && images.isEmpty();
283         }
284
285         /**
286          * Returns the parent album of this album.
287          *
288          * @return The parent album of this album, or {@code null} if this album
289          *         does not have a parent
290          */
291         public Album getParent() {
292                 return parent;
293         }
294
295         /**
296          * Sets the parent album of this album.
297          *
298          * @param parent
299          *            The new parent album of this album
300          * @return This album
301          */
302         protected Album setParent(Album parent) {
303                 Validation.begin().isNotNull("Album Parent", parent).check();
304                 this.parent = parent;
305                 return this;
306         }
307
308         /**
309          * Removes the parent album of this album.
310          *
311          * @return This album
312          */
313         protected Album removeParent() {
314                 this.parent = null;
315                 return this;
316         }
317
318         /**
319          * Returns the title of this album.
320          *
321          * @return The title of this album
322          */
323         public String getTitle() {
324                 return title;
325         }
326
327         /**
328          * Sets the title of this album.
329          *
330          * @param title
331          *            The title of this album
332          * @return This album
333          */
334         public Album setTitle(String title) {
335                 Validation.begin().isNotNull("Album Title", title).check();
336                 this.title = title;
337                 return this;
338         }
339
340         /**
341          * Returns the description of this album.
342          *
343          * @return The description of this album
344          */
345         public String getDescription() {
346                 return description;
347         }
348
349         /**
350          * Sets the description of this album.
351          *
352          * @param description
353          *            The description of this album
354          * @return This album
355          */
356         public Album setDescription(String description) {
357                 Validation.begin().isNotNull("Album Description", description).check();
358                 this.description = description;
359                 return this;
360         }
361
362         //
363         // FINGERPRINTABLE METHODS
364         //
365
366         /**
367          * {@inheritDoc}
368          */
369         @Override
370         public String getFingerprint() {
371                 StringBuilder fingerprint = new StringBuilder();
372                 fingerprint.append("Album(");
373                 fingerprint.append("ID(").append(id).append(')');
374                 fingerprint.append("Title(").append(title).append(')');
375                 fingerprint.append("Description(").append(description).append(')');
376                 if (albumImage != null) {
377                         fingerprint.append("AlbumImage(").append(albumImage).append(')');
378                 }
379
380                 /* add nested albums. */
381                 fingerprint.append("Albums(");
382                 for (Album album : albums) {
383                         fingerprint.append(album.getFingerprint());
384                 }
385                 fingerprint.append(')');
386
387                 /* add images. */
388                 fingerprint.append("Images(");
389                 for (Image image : getImages()) {
390                         if (image.isInserted()) {
391                                 fingerprint.append(image.getFingerprint());
392                         }
393                 }
394                 fingerprint.append(')');
395
396                 fingerprint.append(')');
397                 return fingerprint.toString();
398         }
399
400         //
401         // OBJECT METHODS
402         //
403
404         /**
405          * {@inheritDoc}
406          */
407         @Override
408         public int hashCode() {
409                 return id.hashCode();
410         }
411
412         /**
413          * {@inheritDoc}
414          */
415         @Override
416         public boolean equals(Object object) {
417                 if (!(object instanceof Album)) {
418                         return false;
419                 }
420                 Album album = (Album) object;
421                 return id.equals(album.id);
422         }
423
424 }