7805bf46bcfd9e89e6986a36c0b7a0ff01e7010a
[Sone.git] / src / main / java / net / pterodactylus / sone / data / impl / DefaultAlbum.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.impl;
19
20 import static com.google.common.base.Optional.absent;
21 import static com.google.common.base.Optional.fromNullable;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static com.google.common.base.Preconditions.checkNotNull;
24 import static com.google.common.base.Preconditions.checkState;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.UUID;
31
32 import net.pterodactylus.sone.data.Album;
33 import net.pterodactylus.sone.data.Image;
34 import net.pterodactylus.sone.data.Sone;
35 import net.pterodactylus.sone.database.ImageBuilder;
36
37 import com.google.common.base.Function;
38 import com.google.common.base.Optional;
39 import com.google.common.base.Predicates;
40 import com.google.common.collect.Collections2;
41 import com.google.common.hash.Hasher;
42 import com.google.common.hash.Hashing;
43
44 /**
45  * Container for images that can also contain nested {@link Album}s.
46  *
47  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
48  */
49 public class DefaultAlbum implements Album {
50
51         /** The ID of this album. */
52         private final String id;
53
54         /** The Sone this album belongs to. */
55         private Sone sone;
56
57         /** Nested albums. */
58         private final List<Album> albums = new ArrayList<Album>();
59
60         /** The image IDs in order. */
61         private final List<String> imageIds = new ArrayList<String>();
62
63         /** The images in this album. */
64         private final Map<String, Image> images = new HashMap<String, Image>();
65
66         /** The parent album. */
67         private Album parent;
68
69         /** The title of this album. */
70         private String title;
71
72         /** The description of this album. */
73         private String description;
74
75         /** The ID of the album picture. */
76         private String albumImage;
77
78         /** Creates a new album with a random ID. */
79         public DefaultAlbum() {
80                 this(UUID.randomUUID().toString());
81         }
82
83         /**
84          * Creates a new album with the given ID.
85          *
86          * @param id
87          *              The ID of the album
88          */
89         public DefaultAlbum(String id) {
90                 this.id = checkNotNull(id, "id must not be null");
91         }
92
93         //
94         // ACCESSORS
95         //
96
97         @Override
98         public String getId() {
99                 return id;
100         }
101
102         @Override
103         public Sone getSone() {
104                 return sone;
105         }
106
107         @Override
108         public Album setSone(Sone sone) {
109                 checkNotNull(sone, "sone must not be null");
110                 checkState((this.sone == null) || (this.sone.equals(sone)), "album owner must not already be set to some other Sone");
111                 this.sone = sone;
112                 return this;
113         }
114
115         @Override
116         public List<Album> getAlbums() {
117                 return new ArrayList<Album>(albums);
118         }
119
120         @Override
121         public void addAlbum(Album album) {
122                 checkNotNull(album, "album must not be null");
123                 checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
124                 album.setParent(this);
125                 if (!albums.contains(album)) {
126                         albums.add(album);
127                 }
128         }
129
130         @Override
131         public void removeAlbum(Album album) {
132                 checkNotNull(album, "album must not be null");
133                 checkArgument(album.getSone().equals(sone), "album must belong this album’s Sone");
134                 checkArgument(equals(album.getParent()), "album must belong to this album");
135                 albums.remove(album);
136                 album.removeParent();
137         }
138
139         @Override
140         public Album moveAlbumUp(Album album) {
141                 checkNotNull(album, "album must not be null");
142                 checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
143                 checkArgument(equals(album.getParent()), "album must belong to this album");
144                 int oldIndex = albums.indexOf(album);
145                 if (oldIndex <= 0) {
146                         return null;
147                 }
148                 albums.remove(oldIndex);
149                 albums.add(oldIndex - 1, album);
150                 return albums.get(oldIndex);
151         }
152
153         @Override
154         public Album moveAlbumDown(Album album) {
155                 checkNotNull(album, "album must not be null");
156                 checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
157                 checkArgument(equals(album.getParent()), "album must belong to this album");
158                 int oldIndex = albums.indexOf(album);
159                 if ((oldIndex < 0) || (oldIndex >= (albums.size() - 1))) {
160                         return null;
161                 }
162                 albums.remove(oldIndex);
163                 albums.add(oldIndex + 1, album);
164                 return albums.get(oldIndex);
165         }
166
167         @Override
168         public List<Image> getImages() {
169                 return new ArrayList<Image>(Collections2.filter(Collections2.transform(imageIds, new Function<String, Image>() {
170
171                         @Override
172                         @SuppressWarnings("synthetic-access")
173                         public Image apply(String imageId) {
174                                 return images.get(imageId);
175                         }
176                 }), Predicates.notNull()));
177         }
178
179         @Override
180         public void removeImage(Image image) {
181                 checkNotNull(image, "image must not be null");
182                 checkNotNull(image.getSone(), "image must have an owner");
183                 checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album");
184                 imageIds.remove(image.getId());
185                 images.remove(image.getId());
186                 if (image.getId().equals(albumImage)) {
187                         if (images.isEmpty()) {
188                                 albumImage = null;
189                         } else {
190                                 albumImage = images.values().iterator().next().getId();
191                         }
192                 }
193         }
194
195         @Override
196         public Image moveImageUp(Image image) {
197                 checkNotNull(image, "image must not be null");
198                 checkNotNull(image.getSone(), "image must have an owner");
199                 checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album");
200                 checkArgument(image.getAlbum().equals(this), "image must belong to this album");
201                 int oldIndex = imageIds.indexOf(image.getId());
202                 if (oldIndex <= 0) {
203                         return null;
204                 }
205                 imageIds.remove(image.getId());
206                 imageIds.add(oldIndex - 1, image.getId());
207                 return images.get(imageIds.get(oldIndex));
208         }
209
210         @Override
211         public Image moveImageDown(Image image) {
212                 checkNotNull(image, "image must not be null");
213                 checkNotNull(image.getSone(), "image must have an owner");
214                 checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album");
215                 checkArgument(image.getAlbum().equals(this), "image must belong to this album");
216                 int oldIndex = imageIds.indexOf(image.getId());
217                 if ((oldIndex == -1) || (oldIndex >= (imageIds.size() - 1))) {
218                         return null;
219                 }
220                 imageIds.remove(image.getId());
221                 imageIds.add(oldIndex + 1, image.getId());
222                 return images.get(imageIds.get(oldIndex));
223         }
224
225         @Override
226         public Image getAlbumImage() {
227                 if (albumImage == null) {
228                         return null;
229                 }
230                 return Optional.fromNullable(images.get(albumImage)).or(images.values().iterator().next());
231         }
232
233         @Override
234         public boolean isEmpty() {
235                 return albums.isEmpty() && images.isEmpty();
236         }
237
238         @Override
239         public boolean isRoot() {
240                 return parent == null;
241         }
242
243         @Override
244         public Album getParent() {
245                 return parent;
246         }
247
248         @Override
249         public Album setParent(Album parent) {
250                 this.parent = checkNotNull(parent, "parent must not be null");
251                 return this;
252         }
253
254         @Override
255         public Album removeParent() {
256                 this.parent = null;
257                 return this;
258         }
259
260         @Override
261         public String getTitle() {
262                 return title;
263         }
264
265         @Override
266         public String getDescription() {
267                 return description;
268         }
269
270         @Override
271         public ImageBuilder newImageBuilder() throws IllegalStateException {
272                 return new DefaultImageBuilder(sone, this) {
273                         @Override
274                         public Image build() throws IllegalStateException {
275                                 Image image = super.build();
276                                 if (images.isEmpty() && (albumImage == null)) {
277                                         albumImage = image.getId();
278                                 }
279                                 images.put(image.getId(), image);
280                                 imageIds.add(image.getId());
281                                 return image;
282                         }
283                 };
284         }
285
286         @Override
287         public Modifier modify() throws IllegalStateException {
288                 // TODO: reenable check for local Sones
289                 return new Modifier() {
290                         private Optional<String> title = absent();
291
292                         private Optional<String> description = absent();
293
294                         private Optional<String> albumImage = absent();
295
296                         @Override
297                         public Modifier setTitle(String title) {
298                                 this.title = fromNullable(title);
299                                 return this;
300                         }
301
302                         @Override
303                         public Modifier setDescription(String description) {
304                                 this.description = fromNullable(description);
305                                 return this;
306                         }
307
308                         @Override
309                         public Modifier setAlbumImage(String imageId) {
310                                 this.albumImage = fromNullable(imageId);
311                                 return this;
312                         }
313
314                         @Override
315                         public Album update() throws IllegalStateException {
316                                 if (title.isPresent()) {
317                                         DefaultAlbum.this.title = title.get();
318                                 }
319                                 if (description.isPresent()) {
320                                         DefaultAlbum.this.description = description.get();
321                                 }
322                                 if (albumImage.isPresent()) {
323                                         DefaultAlbum.this.albumImage = albumImage.get();
324                                 }
325                                 return DefaultAlbum.this;
326                         }
327                 };
328         }
329
330         //
331         // FINGERPRINTABLE METHODS
332         //
333
334         @Override
335         public String getFingerprint() {
336                 Hasher hash = Hashing.sha256().newHasher();
337                 hash.putString("Album(");
338                 hash.putString("ID(").putString(id).putString(")");
339                 hash.putString("Title(").putString(title).putString(")");
340                 hash.putString("Description(").putString(description).putString(")");
341                 if (albumImage != null) {
342                         hash.putString("AlbumImage(").putString(albumImage).putString(")");
343                 }
344
345                 /* add nested albums. */
346                 hash.putString("Albums(");
347                 for (Album album : albums) {
348                         hash.putString(album.getFingerprint());
349                 }
350                 hash.putString(")");
351
352                 /* add images. */
353                 hash.putString("Images(");
354                 for (Image image : getImages()) {
355                         if (image.isInserted()) {
356                                 hash.putString(image.getFingerprint());
357                         }
358                 }
359                 hash.putString(")");
360
361                 hash.putString(")");
362                 return hash.hash().toString();
363         }
364
365         //
366         // OBJECT METHODS
367         //
368
369         @Override
370         public int hashCode() {
371                 return id.hashCode();
372         }
373
374         @Override
375         public boolean equals(Object object) {
376                 if (!(object instanceof DefaultAlbum)) {
377                         return false;
378                 }
379                 DefaultAlbum album = (DefaultAlbum) object;
380                 return id.equals(album.id);
381         }
382
383 }