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