2 * Sone - AlbumImpl.java - Copyright © 2011–2020 David Roden
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.
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.
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/>.
18 package net.pterodactylus.sone.data.impl;
21 import javax.annotation.*;
23 import com.google.common.base.*;
24 import com.google.common.collect.*;
25 import com.google.common.hash.Hashing;
26 import com.google.common.hash.*;
27 import net.pterodactylus.sone.data.*;
29 import static com.google.common.base.Preconditions.*;
30 import static java.nio.charset.StandardCharsets.*;
33 * Container for images that can also contain nested {@link AlbumImpl}s.
35 public class AlbumImpl implements Album {
37 /** The ID of this album. */
38 private final String id;
40 /** The Sone this album belongs to. */
41 private final Sone sone;
44 private final List<Album> albums = new ArrayList<>();
46 /** The image IDs in order. */
47 private final List<String> imageIds = new ArrayList<>();
49 /** The images in this album. */
50 private final Map<String, Image> images = new HashMap<>();
52 /** The parent album. */
55 /** The title of this album. */
58 /** The description of this album. */
59 private String description;
61 /** Creates a new album with a random ID. */
62 public AlbumImpl(Sone sone) {
63 this(sone, UUID.randomUUID().toString());
67 * Creates a new album with the given ID.
72 public AlbumImpl(Sone sone, String id) {
73 this.sone = checkNotNull(sone, "Sone must not be null");
74 this.id = checkNotNull(id, "id must not be null");
82 public String getId() {
87 public Sone getSone() {
92 public List<Album> getAlbums() {
93 return new ArrayList<>(albums);
97 public void addAlbum(Album album) {
98 checkNotNull(album, "album must not be null");
99 checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
100 album.setParent(this);
101 if (!albums.contains(album)) {
107 public void removeAlbum(Album album) {
108 checkNotNull(album, "album must not be null");
109 checkArgument(album.getSone().equals(sone), "album must belong this album’s Sone");
110 checkArgument(equals(album.getParent()), "album must belong to this album");
111 albums.remove(album);
112 album.removeParent();
116 public Album moveAlbumUp(Album album) {
117 checkNotNull(album, "album must not be null");
118 checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
119 checkArgument(equals(album.getParent()), "album must belong to this album");
120 int oldIndex = albums.indexOf(album);
124 albums.remove(oldIndex);
125 albums.add(oldIndex - 1, album);
126 return albums.get(oldIndex);
130 public Album moveAlbumDown(Album album) {
131 checkNotNull(album, "album must not be null");
132 checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album");
133 checkArgument(equals(album.getParent()), "album must belong to this album");
134 int oldIndex = albums.indexOf(album);
135 if ((oldIndex < 0) || (oldIndex >= (albums.size() - 1))) {
138 albums.remove(oldIndex);
139 albums.add(oldIndex + 1, album);
140 return albums.get(oldIndex);
144 public List<Image> getImages() {
145 return new ArrayList<>(Collections2.filter(Collections2.transform(imageIds, new Function<String, Image>() {
148 @SuppressWarnings("synthetic-access")
149 public Image apply(String imageId) {
150 return images.get(imageId);
152 }), Predicates.notNull()));
156 public void addImage(Image image) {
157 checkNotNull(image, "image must not be null");
158 checkNotNull(image.getSone(), "image must have an owner");
159 checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album");
160 if (image.getAlbum() != null) {
161 image.getAlbum().removeImage(image);
163 image.setAlbum(this);
164 if (!imageIds.contains(image.getId())) {
165 imageIds.add(image.getId());
166 images.put(image.getId(), image);
171 public void removeImage(Image image) {
172 checkNotNull(image, "image must not be null");
173 checkNotNull(image.getSone(), "image must have an owner");
174 checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album");
175 imageIds.remove(image.getId());
176 images.remove(image.getId());
180 public Image moveImageUp(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 checkArgument(image.getAlbum().equals(this), "image must belong to this album");
185 int oldIndex = imageIds.indexOf(image.getId());
189 imageIds.remove(image.getId());
190 imageIds.add(oldIndex - 1, image.getId());
191 return images.get(imageIds.get(oldIndex));
195 public Image moveImageDown(Image image) {
196 checkNotNull(image, "image must not be null");
197 checkNotNull(image.getSone(), "image must have an owner");
198 checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album");
199 checkArgument(image.getAlbum().equals(this), "image must belong to this album");
200 int oldIndex = imageIds.indexOf(image.getId());
201 if ((oldIndex == -1) || (oldIndex >= (imageIds.size() - 1))) {
204 imageIds.remove(image.getId());
205 imageIds.add(oldIndex + 1, image.getId());
206 return images.get(imageIds.get(oldIndex));
210 public boolean isEmpty() {
211 return albums.isEmpty() && images.isEmpty();
215 public boolean isRoot() {
216 return parent == null;
220 public Album getParent() {
225 public Album setParent(Album parent) {
226 this.parent = checkNotNull(parent, "parent must not be null");
231 public Album removeParent() {
237 public String getTitle() {
242 public String getDescription() {
247 public Modifier modify() throws IllegalStateException {
248 // TODO: reenable check for local Sones
249 return new Modifier() {
251 private String title;
253 private String description;
256 public Modifier setTitle(String title) {
262 public Modifier setDescription(String description) {
263 this.description = description;
268 public Album update() throws IllegalStateException {
269 if (title != null && title.trim().isEmpty()) {
270 throw new AlbumTitleMustNotBeEmpty();
273 AlbumImpl.this.title = title;
275 if (description != null) {
276 AlbumImpl.this.description = description;
278 return AlbumImpl.this;
284 // FINGERPRINTABLE METHODS
288 public String getFingerprint() {
289 Hasher hash = Hashing.sha256().newHasher();
290 hash.putString("Album(", UTF_8);
291 hash.putString("ID(", UTF_8).putString(id, UTF_8).putString(")", UTF_8);
292 hash.putString("Title(", UTF_8).putString(title, UTF_8).putString(")", UTF_8);
293 hash.putString("Description(", UTF_8).putString(description, UTF_8).putString(")", UTF_8);
295 /* add nested albums. */
296 hash.putString("Albums(", UTF_8);
297 for (Album album : albums) {
298 hash.putString(album.getFingerprint(), UTF_8);
300 hash.putString(")", UTF_8);
303 hash.putString("Images(", UTF_8);
304 for (Image image : getImages()) {
305 if (image.isInserted()) {
306 hash.putString(image.getFingerprint(), UTF_8);
309 hash.putString(")", UTF_8);
311 hash.putString(")", UTF_8);
312 return hash.hash().toString();
320 public int hashCode() {
321 return id.hashCode();
325 public boolean equals(Object object) {
326 if (!(object instanceof AlbumImpl)) {
329 AlbumImpl album = (AlbumImpl) object;
330 return id.equals(album.id);