From 9d32a0f70e14a764946ae29edcf07304f9e5f75e Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Thu, 10 Oct 2013 13:06:11 +0200 Subject: [PATCH] =?utf8?q?Add=20image=20builder=20that=20creates=20?= =?utf8?q?=E2=80=9Cold=E2=80=9D=20images.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/core/Core.java | 54 ++--- .../pterodactylus/sone/core/SoneDownloader.java | 6 +- .../java/net/pterodactylus/sone/data/Image.java | 259 +++------------------ .../net/pterodactylus/sone/data/ImageImpl.java | 257 ++++++++++++++++++++ .../sone/data/impl/AbstractImageBuilder.java | 63 +++++ .../sone/data/impl/ImageBuilderImpl.java | 37 +++ .../net/pterodactylus/sone/database/Database.java | 2 +- .../pterodactylus/sone/database/ImageBuilder.java | 34 +++ .../sone/database/ImageBuilderFactory.java | 29 +++ .../pterodactylus/sone/database/ImageDatabase.java | 30 +++ .../pterodactylus/sone/database/ImageProvider.java | 33 +++ .../pterodactylus/sone/database/ImageStore.java | 33 +++ .../sone/database/memory/MemoryDatabase.java | 52 +++++ .../net/pterodactylus/sone/web/EditImagePage.java | 3 +- .../pterodactylus/sone/web/UploadImagePage.java | 2 +- .../sone/web/ajax/EditImageAjaxPage.java | 2 +- 16 files changed, 630 insertions(+), 266 deletions(-) create mode 100644 src/main/java/net/pterodactylus/sone/data/ImageImpl.java create mode 100644 src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java create mode 100644 src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java create mode 100644 src/main/java/net/pterodactylus/sone/database/ImageBuilder.java create mode 100644 src/main/java/net/pterodactylus/sone/database/ImageBuilderFactory.java create mode 100644 src/main/java/net/pterodactylus/sone/database/ImageDatabase.java create mode 100644 src/main/java/net/pterodactylus/sone/database/ImageProvider.java create mode 100644 src/main/java/net/pterodactylus/sone/database/ImageStore.java diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 1ec4d7b..0b9b08f 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -187,9 +187,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** Trusted identities, sorted by own identities. */ private final Multimap trustedIdentities = Multimaps.synchronizedSetMultimap(HashMultimap.create()); - /** All known images. */ - private final Map images = new HashMap(); - /** All temporary images. */ private final Map temporaryImages = new HashMap(); @@ -669,14 +666,16 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * none was created */ public Image getImage(String imageId, boolean create) { - synchronized (images) { - Image image = images.get(imageId); - if (create && (image == null)) { - image = new Image(imageId); - images.put(imageId, image); - } - return image; + Optional image = database.getImage(imageId); + if (image.isPresent()) { + return image.get(); } + if (!create) { + return null; + } + Image newImage = database.newImageBuilder().withId(imageId).build(); + database.storeImage(newImage); + return newImage; } /** @@ -1032,18 +1031,16 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } } database.storePostReplies(sone, sone.getReplies()); - synchronized (images) { - for (Album album : storedSone.get().getRootAlbum().getAlbums()) { - database.removeAlbum(album); - for (Image image : album.getImages()) { - images.remove(image.getId()); - } + for (Album album : storedSone.get().getRootAlbum().getAlbums()) { + database.removeAlbum(album); + for (Image image : album.getImages()) { + database.removeImage(image); } - for (Album album : sone.getRootAlbum().getAlbums()) { - database.storeAlbum(album); - for (Image image : album.getImages()) { - images.put(image.getId(), image); - } + } + for (Album album : sone.getRootAlbum().getAlbums()) { + database.storeAlbum(album); + for (Image image : album.getImages()) { + database.storeImage(image); } } synchronized (sones) { @@ -1286,8 +1283,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, "Invalid album image encountered, aborting load!"); return; } - Image image = getImage(imageId).setSone(sone).setCreationTime(creationTime).setKey(key); - image.setTitle(title).setDescription(description).setWidth(width).setHeight(height); + Image image = getImage(imageId).modify().setSone(sone).setCreationTime(creationTime).setKey(key).setTitle(title).setDescription(description).setWidth(width).setHeight(height).update(); album.addImage(image); } @@ -1642,11 +1638,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, checkNotNull(temporaryImage, "temporaryImage must not be null"); checkArgument(sone.isLocal(), "sone must be a local Sone"); checkArgument(sone.equals(album.getSone()), "album must belong to the given Sone"); - Image image = new Image(temporaryImage.getId()).setSone(sone).setCreationTime(System.currentTimeMillis()); + Image image = database.newImageBuilder().withId(temporaryImage.getId()).build().modify().setSone(sone).setCreationTime(System.currentTimeMillis()).update(); album.addImage(image); - synchronized (images) { - images.put(image.getId(), image); - } + database.storeImage(image); imageInserter.insertImage(temporaryImage, image); return image; } @@ -1664,9 +1658,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, checkArgument(image.getSone().isLocal(), "image must belong to a local Sone"); deleteTemporaryImage(image.getId()); image.getAlbum().removeImage(image); - synchronized (images) { - images.remove(image.getId()); - } + database.removeImage(image); touchConfiguration(); } @@ -2223,7 +2215,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, @Subscribe public void imageInsertFinished(ImageInsertFinishedEvent imageInsertFinishedEvent) { logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", imageInsertFinishedEvent.image(), imageInsertFinishedEvent.resultingUri())); - imageInsertFinishedEvent.image().setKey(imageInsertFinishedEvent.resultingUri().toString()); + imageInsertFinishedEvent.image().modify().setKey(imageInsertFinishedEvent.resultingUri().toString()).update(); deleteTemporaryImage(imageInsertFinishedEvent.image().getId()); touchConfiguration(); } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java index eaf56a9..56ae917 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java @@ -495,9 +495,9 @@ public class SoneDownloader extends AbstractService { logger.log(Level.WARNING, String.format("Downloaded Sone %s contains image %s with invalid dimensions (%s, %s)!", sone, imageId, imageWidthString, imageHeightString)); return null; } - Image image = core.getImage(imageId).setSone(sone).setKey(imageKey).setCreationTime(creationTime); - image.setTitle(imageTitle).setDescription(imageDescription); - image.setWidth(imageWidth).setHeight(imageHeight); + Image image = core.getImage(imageId).modify().setSone(sone).setKey(imageKey).setCreationTime(creationTime).update(); + image = image.modify().setTitle(imageTitle).setDescription(imageDescription).update(); + image = image.modify().setWidth(imageWidth).setHeight(imageHeight).update(); album.addImage(image); } } diff --git a/src/main/java/net/pterodactylus/sone/data/Image.java b/src/main/java/net/pterodactylus/sone/data/Image.java index d8a8ab1..e3e1b4d 100644 --- a/src/main/java/net/pterodactylus/sone/data/Image.java +++ b/src/main/java/net/pterodactylus/sone/data/Image.java @@ -17,112 +17,33 @@ package net.pterodactylus.sone.data; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import java.util.UUID; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; - /** * Container for image metadata. * - * @author David ‘Bombe’ Roden + * @author David Roden */ -public class Image implements Identified, Fingerprintable { - - /** The ID of the image. */ - private final String id; - - /** The Sone the image belongs to. */ - private Sone sone; - - /** The album this image belongs to. */ - private Album album; - - /** The request key of the image. */ - private String key; - - /** The creation time of the image. */ - private long creationTime; - - /** The width of the image. */ - private int width; - - /** The height of the image. */ - private int height; - - /** The title of the image. */ - private String title; - - /** The description of the image. */ - private String description; - - /** - * Creates a new image with a random ID. - */ - public Image() { - this(UUID.randomUUID().toString()); - setCreationTime(System.currentTimeMillis()); - } - - /** - * Creates a new image. - * - * @param id - * The ID of the image - */ - public Image(String id) { - this.id = checkNotNull(id, "id must not be null"); - } - - // - // ACCESSORS - // +public interface Image extends Identified, Fingerprintable { /** * Returns the ID of this image. * * @return The ID of this image */ - public String getId() { - return id; - } + String getId(); /** * Returns the Sone this image belongs to. * * @return The Sone this image belongs to */ - public Sone getSone() { - return sone; - } - - /** - * Sets the owner of this image. The owner can only be set if no owner has - * yet been set. - * - * @param sone - * The new owner of this image - * @return This image - */ - public Image setSone(Sone sone) { - checkNotNull(sone, "sone must not be null"); - checkArgument((this.sone == null) || this.sone.equals(sone), "sone must not already be set to another sone"); - this.sone = sone; - return this; - } + Sone getSone(); /** * Returns the album this image belongs to. * * @return The album this image belongs to */ - public Album getAlbum() { - return album; - } + Album getAlbum(); /** * Sets the album this image belongs to. The album of an image can only be @@ -132,36 +53,14 @@ public class Image implements Identified, Fingerprintable { * The album this image belongs to * @return This image */ - public Image setAlbum(Album album) { - checkNotNull(album, "album must not be null"); - checkNotNull(album.getSone().equals(getSone()), "album must belong to the same Sone as this image"); - this.album = album; - return this; - } + Image setAlbum(Album album); /** * Returns the request key of this image. * * @return The request key of this image */ - public String getKey() { - return key; - } - - /** - * Sets the request key of this image. The request key can only be set as - * long as no request key has yet been set. - * - * @param key - * The new request key of this image - * @return This image - */ - public Image setKey(String key) { - checkNotNull(key, "key must not be null"); - checkState((this.key == null) || this.key.equals(key), "key must not be already set to another key"); - this.key = key; - return this; - } + String getKey(); /** * Returns whether the image has already been inserted. An image is @@ -171,9 +70,7 @@ public class Image implements Identified, Fingerprintable { * @return {@code true} if there is a key for this image, {@code false} * otherwise */ - public boolean isInserted() { - return key != null; - } + boolean isInserted(); /** * Returns the creation time of this image. @@ -181,154 +78,62 @@ public class Image implements Identified, Fingerprintable { * @return The creation time of this image (in milliseconds since 1970, Jan * 1, UTC) */ - public long getCreationTime() { - return creationTime; - } - - /** - * Sets the new creation time of this image. The creation time can only be - * set as long as no creation time has been set yet. - * - * @param creationTime - * The new creation time of this image - * @return This image - */ - public Image setCreationTime(long creationTime) { - checkArgument(creationTime > 0, "creationTime must be > 0"); - checkState((this.creationTime == 0) || (this.creationTime == creationTime), "creationTime must not already be set"); - this.creationTime = creationTime; - return this; - } + long getCreationTime(); /** * Returns the width of this image. * * @return The width of this image (in pixels) */ - public int getWidth() { - return width; - } - - /** - * Sets the width of this image. The width can only be set as long as no - * width has been set yet. - * - * @param width - * The new width of this image - * @return This image - */ - public Image setWidth(int width) { - checkArgument(width > 0, "width must be > 0"); - checkState((this.width == 0) || (this.width == width), "width must not already be set to another width"); - this.width = width; - return this; - } + int getWidth(); /** * Returns the height of this image. * * @return The height of this image (in pixels) */ - public int getHeight() { - return height; - } - - /** - * Sets the new height of this image. The height can only be set as long as - * no height has yet been set. - * - * @param height - * The new height of this image - * @return This image - */ - public Image setHeight(int height) { - checkArgument(height > 0, "height must be > 0"); - checkState((this.height == 0) || (this.height == height), "height must not already be set to another height"); - this.height = height; - return this; - } + int getHeight(); /** * Returns the title of this image. * * @return The title of this image */ - public String getTitle() { - return title; - } - - /** - * Sets the title of this image. - * - * @param title - * The title of this image - * @return This image - */ - public Image setTitle(String title) { - this.title = checkNotNull(title, "title must not be null"); - return this; - } + String getTitle(); /** * Returns the description of this image. * * @return The description of this image */ - public String getDescription() { - return description; - } - - /** - * Sets the description of this image. - * - * @param description - * The description of this image - * @return This image - */ - public Image setDescription(String description) { - this.description = checkNotNull(description, "description must not be null"); - return this; - } - - // - // FINGERPRINTABLE METHODS - // + String getDescription(); /** * {@inheritDoc} */ @Override - public String getFingerprint() { - Hasher hash = Hashing.sha256().newHasher(); - hash.putString("Image("); - hash.putString("ID(").putString(id).putString(")"); - hash.putString("Title(").putString(title).putString(")"); - hash.putString("Description(").putString(description).putString(")"); - hash.putString(")"); - return hash.hash().toString(); - } + String getFingerprint(); - // - // OBJECT METHODS - // + Modifier modify() throws IllegalStateException; - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return id.hashCode(); - } + interface Modifier { + + Modifier setSone(Sone sone); + + Modifier setCreationTime(long creationTime); + + Modifier setKey(String key); + + Modifier setTitle(String title); + + Modifier setDescription(String description); + + Modifier setWidth(int width); + + Modifier setHeight(int height); + + Image update() throws IllegalStateException; - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object object) { - if (!(object instanceof Image)) { - return false; - } - return ((Image) object).id.equals(id); } } diff --git a/src/main/java/net/pterodactylus/sone/data/ImageImpl.java b/src/main/java/net/pterodactylus/sone/data/ImageImpl.java new file mode 100644 index 0000000..58741c3 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/ImageImpl.java @@ -0,0 +1,257 @@ +/* + * Sone - ImageImpl.java - Copyright © 2011–2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.pterodactylus.sone.data; + +import static com.google.common.base.Optional.absent; +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Optional.of; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.util.UUID; + +import com.google.common.base.Optional; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + +/** + * Container for image metadata. + * + * @author David ‘Bombe’ Roden + */ +public class ImageImpl implements Image { + + /** The ID of the image. */ + private final String id; + + /** The Sone the image belongs to. */ + private Sone sone; + + /** The album this image belongs to. */ + private Album album; + + /** The request key of the image. */ + private String key; + + /** The creation time of the image. */ + private long creationTime; + + /** The width of the image. */ + private int width; + + /** The height of the image. */ + private int height; + + /** The title of the image. */ + private String title; + + /** The description of the image. */ + private String description; + + /** Creates a new image with a random ID. */ + public ImageImpl() { + this(UUID.randomUUID().toString()); + this.creationTime = System.currentTimeMillis(); + } + + /** + * Creates a new image. + * + * @param id + * The ID of the image + */ + public ImageImpl(String id) { + this.id = checkNotNull(id, "id must not be null"); + } + + // + // ACCESSORS + // + + @Override + public String getId() { + return id; + } + + @Override + public Sone getSone() { + return sone; + } + + @Override + public Album getAlbum() { + return album; + } + + @Override + public Image setAlbum(Album album) { + checkNotNull(album, "album must not be null"); + checkNotNull(album.getSone().equals(getSone()), "album must belong to the same Sone as this image"); + this.album = album; + return this; + } + + @Override + public String getKey() { + return key; + } + + @Override + public boolean isInserted() { + return key != null; + } + + @Override + public long getCreationTime() { + return creationTime; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public String getDescription() { + return description; + } + + public Modifier modify() throws IllegalStateException { + checkState((sone == null) || sone.isLocal(), "only local images may be modified"); + return new Modifier() { + private Optional sone = absent(); + + private Optional creationTime = absent(); + + private Optional key = absent(); + + private Optional title = absent(); + + private Optional description = absent(); + + private Optional width = absent(); + + private Optional height = absent(); + + @Override + public Modifier setSone(Sone sone) { + this.sone = fromNullable(sone); + return this; + } + + @Override + public Modifier setCreationTime(long creationTime) { + this.creationTime = of(creationTime); + return this; + } + + @Override + public Modifier setKey(String key) { + this.key = fromNullable(key); + return this; + } + + @Override + public Modifier setTitle(String title) { + this.title = fromNullable(title); + return this; + } + + @Override + public Modifier setDescription(String description) { + this.description = fromNullable(description); + return this; + } + + @Override + public Modifier setWidth(int width) { + this.width = of(width); + return this; + } + + @Override + public Modifier setHeight(int height) { + this.height = of(height); + return this; + } + + @Override + public Image update() throws IllegalStateException { + checkState(!sone.isPresent() || sone.get().equals(ImageImpl.this.sone), "can not change Sone once set"); + checkState(!creationTime.isPresent() || (ImageImpl.this.creationTime == 0), "can not change creation time once set"); + checkState(!key.isPresent() || key.get().equals(ImageImpl.this.key), "can not change key once set"); + checkState(!width.isPresent() || width.get().equals(ImageImpl.this.width), "can not change width once set"); + checkState(!height.isPresent() || height.get().equals(ImageImpl.this.height), "can not change height once set"); + + ImageImpl.this.sone = sone.or(ImageImpl.this.sone); + ImageImpl.this.creationTime = creationTime.or(ImageImpl.this.creationTime); + ImageImpl.this.key = key.or(ImageImpl.this.key); + ImageImpl.this.title = title.or(ImageImpl.this.title); + ImageImpl.this.description = description.or(ImageImpl.this.description); + ImageImpl.this.width = width.or(ImageImpl.this.width); + ImageImpl.this.height = height.or(ImageImpl.this.height); + + return ImageImpl.this; + } + }; + } + + // + // FINGERPRINTABLE METHODS + // + + @Override + public String getFingerprint() { + Hasher hash = Hashing.sha256().newHasher(); + hash.putString("Image("); + hash.putString("ID(").putString(id).putString(")"); + hash.putString("Title(").putString(title).putString(")"); + hash.putString("Description(").putString(description).putString(")"); + hash.putString(")"); + return hash.hash().toString(); + } + + // + // OBJECT METHODS + // + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object object) { + if (!(object instanceof ImageImpl)) { + return false; + } + return ((ImageImpl) object).id.equals(id); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java new file mode 100644 index 0000000..533ba39 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java @@ -0,0 +1,63 @@ +/* + * Sone - AbstractImageBuilder.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.pterodactylus.sone.data.impl; + +import static com.google.common.base.Preconditions.checkState; + +import net.pterodactylus.sone.database.ImageBuilder; + +/** + * Abstract {@link ImageBuilder} implementation. It stores the state of the new + * album and performs validation, you only need to implement {@link #build()}. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractImageBuilder implements ImageBuilder { + + /** Whether to create an album with a random ID. */ + protected boolean randomId; + + /** The ID of the album to create. */ + protected String id; + + @Override + public ImageBuilder randomId() { + randomId = true; + return this; + } + + @Override + public ImageBuilder withId(String id) { + this.id = id; + return this; + } + + // + // PROTECTED METHODS + // + + /** + * Validates the state of this image builder. + * + * @throws IllegalStateException + * if the state is not valid for building a new image + */ + protected void validate() throws IllegalStateException { + checkState((randomId && (id == null)) || (!randomId && (id != null)), "exactly one of random ID or custom ID must be set"); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java new file mode 100644 index 0000000..870b5d7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java @@ -0,0 +1,37 @@ +/* + * Sone - ImageBuilderImpl.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data.impl; + +import net.pterodactylus.sone.data.Image; +import net.pterodactylus.sone.data.ImageImpl; +import net.pterodactylus.sone.database.ImageBuilder; + +/** + * {@link ImageBuilder} implementation that creates {@link ImageImpl} objects. + * + * @author David Roden + */ +public class ImageBuilderImpl extends AbstractImageBuilder { + + @Override + public Image build() throws IllegalStateException { + validate(); + return randomId ? new ImageImpl() : new ImageImpl(id); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/Database.java b/src/main/java/net/pterodactylus/sone/database/Database.java index 4bf863f..ee7a9af 100644 --- a/src/main/java/net/pterodactylus/sone/database/Database.java +++ b/src/main/java/net/pterodactylus/sone/database/Database.java @@ -26,7 +26,7 @@ import com.google.common.util.concurrent.Service; * * @author David ‘Bombe’ Roden */ -public interface Database extends Service, PostDatabase, PostReplyDatabase, AlbumDatabase { +public interface Database extends Service, PostDatabase, PostReplyDatabase, AlbumDatabase, ImageDatabase { /** * Saves the database. diff --git a/src/main/java/net/pterodactylus/sone/database/ImageBuilder.java b/src/main/java/net/pterodactylus/sone/database/ImageBuilder.java new file mode 100644 index 0000000..af405c7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/ImageBuilder.java @@ -0,0 +1,34 @@ +/* + * Sone - ImageBuilder.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.pterodactylus.sone.database; + +import net.pterodactylus.sone.data.Image; + +/** + * Builder for {@link Image} objects. + * + * @author David Roden + */ +public interface ImageBuilder { + + ImageBuilder randomId(); + + ImageBuilder withId(String id); + + Image build() throws IllegalStateException; + +} diff --git a/src/main/java/net/pterodactylus/sone/database/ImageBuilderFactory.java b/src/main/java/net/pterodactylus/sone/database/ImageBuilderFactory.java new file mode 100644 index 0000000..29b605b --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/ImageBuilderFactory.java @@ -0,0 +1,29 @@ +/* + * Sone - ImageBuilderFactory.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.database; + +/** + * Factory for {@link ImageBuilder}s. + * + * @author David Roden + */ +public interface ImageBuilderFactory { + + ImageBuilder newImageBuilder(); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/ImageDatabase.java b/src/main/java/net/pterodactylus/sone/database/ImageDatabase.java new file mode 100644 index 0000000..b5e436d --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/ImageDatabase.java @@ -0,0 +1,30 @@ +/* + * Sone - ImageDatabase.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.database; + +/** + * Combines an {@link ImageProvider}, an {@link ImageBuilderFactory}, and an + * {@link ImageStore} into an image database. + * + * @author David ‘Bombe’ Roden + */ +public interface ImageDatabase extends ImageProvider, ImageBuilderFactory, ImageStore { + + /* nothing here. */ + +} diff --git a/src/main/java/net/pterodactylus/sone/database/ImageProvider.java b/src/main/java/net/pterodactylus/sone/database/ImageProvider.java new file mode 100644 index 0000000..c9ad9e1 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/ImageProvider.java @@ -0,0 +1,33 @@ +/* + * Sone - ImageProvider.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.database; + +import net.pterodactylus.sone.data.Image; + +import com.google.common.base.Optional; + +/** + * Provides {@link Image}. + * + * @author David Roden + */ +public interface ImageProvider { + + Optional getImage(String imageId); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/ImageStore.java b/src/main/java/net/pterodactylus/sone/database/ImageStore.java new file mode 100644 index 0000000..f09eff0 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/ImageStore.java @@ -0,0 +1,33 @@ +/* + * Sone - ImageStore.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.database; + +import net.pterodactylus.sone.data.Image; + +/** + * Manages {@link Image} storage. + * + * @author David Roden + */ +public interface ImageStore { + + void storeImage(Image image); + + void removeImage(Image image); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java index 77ff2f4..8b25954 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java @@ -35,14 +35,17 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.impl.AlbumBuilderImpl; +import net.pterodactylus.sone.data.impl.ImageBuilderImpl; import net.pterodactylus.sone.database.AlbumBuilder; import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.database.DatabaseException; +import net.pterodactylus.sone.database.ImageBuilder; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostDatabase; import net.pterodactylus.sone.database.PostReplyBuilder; @@ -104,6 +107,8 @@ public class MemoryDatabase extends AbstractService implements Database { private final Map allAlbums = new HashMap(); + private final Map allImages = new HashMap(); + /** * Creates a new memory database. * @@ -465,6 +470,53 @@ public class MemoryDatabase extends AbstractService implements Database { } // + // IMAGEPROVIDER METHODS + // + + @Override + public Optional getImage(String imageId) { + lock.readLock().lock(); + try { + return fromNullable(allImages.get(imageId)); + } finally { + lock.readLock().unlock(); + } + } + + // + // IMAGEBUILDERFACTORY METHODS + // + + @Override + public ImageBuilder newImageBuilder() { + return new ImageBuilderImpl(); + } + + // + // IMAGESTORE METHODS + // + + @Override + public void storeImage(Image image) { + lock.writeLock().lock(); + try { + allImages.put(image.getId(), image); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void removeImage(Image image) { + lock.writeLock().lock(); + try { + allImages.remove(image.getId()); + } finally { + lock.writeLock().unlock(); + } + } + + // // PACKAGE-PRIVATE METHODS // diff --git a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java index 75872eb..9a29c85 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java @@ -73,8 +73,7 @@ public class EditImagePage extends SoneTemplatePage { if (title.length() == 0) { templateContext.set("titleMissing", true); } - image.setTitle(title); - image.setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)); + image.modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); } webInterface.getCore().touchConfiguration(); throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java index 2800f29..3844082 100644 --- a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java @@ -123,7 +123,7 @@ public class UploadImagePage extends SoneTemplatePage { String mimeType = getMimeType(imageData); TemporaryImage temporaryImage = webInterface.getCore().createTemporaryImage(mimeType, imageData); image = webInterface.getCore().createImage(currentSone, parent, temporaryImage); - image.setTitle(name).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).setWidth(uploadedImage.getWidth(null)).setHeight(uploadedImage.getHeight(null)); + image.modify().setTitle(name).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).setWidth(uploadedImage.getWidth(null)).setHeight(uploadedImage.getHeight(null)).update(); } catch (IOException ioe1) { logger.log(Level.WARNING, "Could not read uploaded image!", ioe1); return; diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java index 08452ef..0c45225 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java @@ -78,7 +78,7 @@ public class EditImageAjaxPage extends JsonPage { } String title = request.getHttpRequest().getParam("title").trim(); String description = request.getHttpRequest().getParam("description").trim(); - image.setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)); + image.modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update(); webInterface.getCore().touchConfiguration(); return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), ImmutableMap.builder().put("sone", image.getSone()).build())); } -- 2.7.4