From: David ‘Bombe’ Roden Date: Sat, 29 Jun 2013 14:19:50 +0000 (+0200) Subject: Merge branch 'release-0.8.5' X-Git-Tag: 0.8.5^0 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=6e9a43ccd93ae125720547c0fe421dc81a54ba90;hp=b64f7d11fca2a616c8ceaf146a79fc3782877afd Merge branch 'release-0.8.5' --- diff --git a/pom.xml b/pom.xml index 3b894ae..a3d564e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,17 +2,23 @@ 4.0.0 net.pterodactylus sone - 0.8.4 + 0.8.5 net.pterodactylus utils - 0.12.2 + 0.12.3 junit junit - 3.8.2 + 4.11 + test + + + org.mockito + mockito-all + 1.9.5 test @@ -32,6 +38,21 @@ utils.json 0.1 + + com.google.inject + guice + 3.0 + + + com.google.guava + guava + 14.0-rc1 + + + commons-lang + commons-lang + 2.6 + @@ -47,6 +68,7 @@ org.apache.maven.plugins maven-compiler-plugin + 2.0.2 1.6 1.6 @@ -55,6 +77,7 @@ org.apache.maven.plugins maven-jar-plugin + 2.2 @@ -90,21 +113,17 @@ - - - - org.apache.maven.plugins maven-javadoc-plugin + 2.7 - - http://download.oracle.com/javase/6/docs/api/ - http://java.pterodactylus.net/utils/apidocs/ - http://java.pterodactylus.net/utils.json/apidocs/ - + true + true + private +
© 2010–2013 David ‘Bombe’ Roden
-
+ diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index c391582..d66e032 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -1,5 +1,5 @@ /* - * Sone - Core.java - Copyright © 2010–2012 David Roden + * Sone - Core.java - Copyright © 2010–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 @@ -17,8 +17,12 @@ package net.pterodactylus.sone.core; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -28,12 +32,26 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import net.pterodactylus.sone.core.Options.DefaultOption; import net.pterodactylus.sone.core.Options.Option; import net.pterodactylus.sone.core.Options.OptionWatcher; +import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; +import net.pterodactylus.sone.core.event.MarkPostKnownEvent; +import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent; +import net.pterodactylus.sone.core.event.MarkSoneKnownEvent; +import net.pterodactylus.sone.core.event.NewPostFoundEvent; +import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent; +import net.pterodactylus.sone.core.event.NewSoneFoundEvent; +import net.pterodactylus.sone.core.event.PostRemovedEvent; +import net.pterodactylus.sone.core.event.PostReplyRemovedEvent; +import net.pterodactylus.sone.core.event.SoneLockedEvent; +import net.pterodactylus.sone.core.event.SoneRemovedEvent; +import net.pterodactylus.sone.core.event.SoneUnlockedEvent; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Image; @@ -46,25 +64,41 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; import net.pterodactylus.sone.data.Sone.SoneStatus; import net.pterodactylus.sone.data.TemporaryImage; -import net.pterodactylus.sone.data.impl.PostImpl; +import net.pterodactylus.sone.database.Database; +import net.pterodactylus.sone.database.DatabaseException; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.PostProvider; +import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.PostReplyProvider; +import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.IdentityListener; import net.pterodactylus.sone.freenet.wot.IdentityManager; import net.pterodactylus.sone.freenet.wot.OwnIdentity; +import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent; +import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent; +import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent; +import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent; +import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent; import net.pterodactylus.sone.main.SonePlugin; +import net.pterodactylus.sone.utils.IntegerRangePredicate; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.service.AbstractService; -import net.pterodactylus.util.thread.Ticker; -import net.pterodactylus.util.validation.EqualityValidator; -import net.pterodactylus.util.validation.IntegerRangeValidator; -import net.pterodactylus.util.validation.OrValidator; -import net.pterodactylus.util.validation.Validation; -import net.pterodactylus.util.version.Version; +import net.pterodactylus.util.thread.NamedThreadFactory; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.FluentIterable; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; + import freenet.keys.FreenetURI; /** @@ -72,7 +106,7 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Core extends AbstractService implements IdentityListener, UpdateListener, SoneProvider, PostProvider, SoneInsertListener, ImageInsertListener { +public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider { /** The logger. */ private static final Logger logger = Logging.getLogger(Core.class); @@ -86,8 +120,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** The preferences. */ private final Preferences preferences = new Preferences(options); - /** The core listener manager. */ - private final CoreListenerManager coreListenerManager = new CoreListenerManager(this); + /** The event bus. */ + private final EventBus eventBus; /** The configuration. */ private Configuration configuration; @@ -108,7 +142,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis private final ImageInserter imageInserter; /** Sone downloader thread-pool. */ - private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10); + private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10, new NamedThreadFactory("Sone Downloader %2$d")); /** The update checker. */ private final UpdateChecker updateChecker; @@ -120,42 +154,29 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis private volatile FcpInterface fcpInterface; /** The times Sones were followed. */ - private final Map soneFollowingTimes = new HashMap(); + private final Map soneFollowingTimes = new HashMap(); /** Locked local Sones. */ /* synchronize on itself. */ private final Set lockedSones = new HashSet(); /** Sone inserters. */ - /* synchronize access on this on localSones. */ + /* synchronize access on this on sones. */ private final Map soneInserters = new HashMap(); /** Sone rescuers. */ - /* synchronize access on this on localSones. */ + /* synchronize access on this on sones. */ private final Map soneRescuers = new HashMap(); - /** All local Sones. */ - /* synchronize access on this on itself. */ - private final Map localSones = new HashMap(); - - /** All remote Sones. */ + /** All Sones. */ /* synchronize access on this on itself. */ - private final Map remoteSones = new HashMap(); + private final Map sones = new HashMap(); /** All known Sones. */ private final Set knownSones = new HashSet(); - /** All posts. */ - private final Map posts = new HashMap(); - - /** All known posts. */ - private final Set knownPosts = new HashSet(); - - /** All replies. */ - private final Map replies = new HashMap(); - - /** All known replies. */ - private final Set knownReplies = new HashSet(); + /** The post database. */ + private final Database database; /** All bookmarked posts. */ /* synchronize access on itself. */ @@ -174,7 +195,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis private final Map temporaryImages = new HashMap(); /** Ticker for threads that mark own elements as known. */ - private final Ticker localElementTicker = new Ticker(); + private final ScheduledExecutorService localElementTicker = Executors.newScheduledThreadPool(1); /** The time the configuration was last touched. */ private volatile long lastConfigurationUpdate; @@ -190,40 +211,23 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The identity manager * @param webOfTrustUpdater * The WebOfTrust updater + * @param eventBus + * The event bus + * @param database + * The database */ - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater) { + @Inject + public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; this.identityManager = identityManager; this.soneDownloader = new SoneDownloader(this, freenetInterface); - this.imageInserter = new ImageInserter(this, freenetInterface); - this.updateChecker = new UpdateChecker(freenetInterface); + this.imageInserter = new ImageInserter(freenetInterface); + this.updateChecker = new UpdateChecker(eventBus, freenetInterface); this.webOfTrustUpdater = webOfTrustUpdater; - } - - // - // LISTENER MANAGEMENT - // - - /** - * Adds a new core listener. - * - * @param coreListener - * The listener to add - */ - public void addCoreListener(CoreListener coreListener) { - coreListenerManager.addListener(coreListener); - } - - /** - * Removes a core listener. - * - * @param coreListener - * The listener to remove - */ - public void removeCoreListener(CoreListener coreListener) { - coreListenerManager.removeListener(coreListener); + this.eventBus = eventBus; + this.database = database; } // @@ -296,8 +300,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The Sone rescuer for the given Sone */ public SoneRescuer getSoneRescuer(Sone sone) { - Validation.begin().isNotNull("Sone", sone).check().is("Local Sone", isLocalSone(sone)).check(); - synchronized (localSones) { + checkNotNull(sone, "sone must not be null"); + checkArgument(sone.isLocal(), "sone must be local"); + synchronized (sones) { SoneRescuer soneRescuer = soneRescuers.get(sone); if (soneRescuer == null) { soneRescuer = new SoneRescuer(this, soneDownloader, sone); @@ -322,28 +327,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } /** - * Returns all Sones, remote and local. - * - * @return All Sones + * {@inheritDocs} */ - public Set getSones() { - Set allSones = new HashSet(); - allSones.addAll(getLocalSones()); - allSones.addAll(getRemoteSones()); - return allSones; - } - - /** - * Returns the Sone with the given ID, regardless whether it’s local or - * remote. - * - * @param id - * The ID of the Sone to get - * @return The Sone with the given ID, or {@code null} if there is no such - * Sone - */ - public Sone getSone(String id) { - return getSone(id, true); + @Override + public Collection getSones() { + synchronized (sones) { + return Collections.unmodifiableCollection(sones.values()); + } } /** @@ -352,83 +342,33 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * * @param id * The ID of the Sone to get - * @param create - * {@code true} to create a new Sone if none exists, - * {@code false} to return {@code null} if a Sone with the given - * ID does not exist * @return The Sone with the given ID, or {@code null} if there is no such * Sone */ @Override - public Sone getSone(String id, boolean create) { - if (isLocalSone(id)) { - return getLocalSone(id); - } - return getRemoteSone(id, create); - } - - /** - * Checks whether the core knows a Sone with the given ID. - * - * @param id - * The ID of the Sone - * @return {@code true} if there is a Sone with the given ID, {@code false} - * otherwise - */ - public boolean hasSone(String id) { - return isLocalSone(id) || isRemoteSone(id); - } - - /** - * Returns whether the given Sone is a local Sone. - * - * @param sone - * The Sone to check for its locality - * @return {@code true} if the given Sone is local, {@code false} otherwise - */ - public boolean isLocalSone(Sone sone) { - synchronized (localSones) { - return localSones.containsKey(sone.getId()); + public Optional getSone(String id) { + synchronized (sones) { + return Optional.fromNullable(sones.get(id)); } } /** - * Returns whether the given ID is the ID of a local Sone. - * - * @param id - * The Sone ID to check for its locality - * @return {@code true} if the given ID is a local Sone, {@code false} - * otherwise + * {@inheritDocs} */ - public boolean isLocalSone(String id) { - synchronized (localSones) { - return localSones.containsKey(id); - } - } + @Override + public Collection getLocalSones() { + synchronized (sones) { + return Collections2.filter(sones.values(), new Predicate() { - /** - * Returns all local Sones. - * - * @return All local Sones - */ - public Set getLocalSones() { - synchronized (localSones) { - return new HashSet(localSones.values()); + @Override + public boolean apply(Sone sone) { + return sone.isLocal(); + } + }); } } /** - * Returns the local Sone with the given ID. - * - * @param id - * The ID of the Sone to get - * @return The Sone with the given ID - */ - public Sone getLocalSone(String id) { - return getLocalSone(id, true); - } - - /** * Returns the local Sone with the given ID, optionally creating a new Sone. * * @param id @@ -439,24 +379,33 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The Sone with the given ID, or {@code null} */ public Sone getLocalSone(String id, boolean create) { - synchronized (localSones) { - Sone sone = localSones.get(id); + synchronized (sones) { + Sone sone = sones.get(id); if ((sone == null) && create) { - sone = new Sone(id); - localSones.put(id, sone); + sone = new Sone(id, true); + sones.put(id, sone); + } + if ((sone != null) && !sone.isLocal()) { + sone = new Sone(id, true); + sones.put(id, sone); } return sone; } } /** - * Returns all remote Sones. - * - * @return All remote Sones + * {@inheritDocs} */ - public Set getRemoteSones() { - synchronized (remoteSones) { - return new HashSet(remoteSones.values()); + @Override + public Collection getRemoteSones() { + synchronized (sones) { + return Collections2.filter(sones.values(), new Predicate() { + + @Override + public boolean apply(Sone sone) { + return !sone.isLocal(); + } + }); } } @@ -471,45 +420,17 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The Sone with the given ID */ public Sone getRemoteSone(String id, boolean create) { - synchronized (remoteSones) { - Sone sone = remoteSones.get(id); + synchronized (sones) { + Sone sone = sones.get(id); if ((sone == null) && create && (id != null) && (id.length() == 43)) { - sone = new Sone(id); - remoteSones.put(id, sone); + sone = new Sone(id, false); + sones.put(id, sone); } return sone; } } /** - * Returns whether the given Sone is a remote Sone. - * - * @param sone - * The Sone to check - * @return {@code true} if the given Sone is a remote Sone, {@code false} - * otherwise - */ - public boolean isRemoteSone(Sone sone) { - synchronized (remoteSones) { - return remoteSones.containsKey(sone.getId()); - } - } - - /** - * Returns whether the Sone with the given ID is a remote Sone. - * - * @param id - * The ID of the Sone to check - * @return {@code true} if the Sone with the given ID is a remote Sone, - * {@code false} otherwise - */ - public boolean isRemoteSone(String id) { - synchronized (remoteSones) { - return remoteSones.containsKey(id); - } - } - - /** * Returns whether the given Sone has been modified. * * @param sone @@ -531,10 +452,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ public long getSoneFollowingTime(Sone sone) { synchronized (soneFollowingTimes) { - if (soneFollowingTimes.containsKey(sone)) { - return soneFollowingTimes.get(sone); - } - return Long.MAX_VALUE; + return Optional.fromNullable(soneFollowingTimes.get(sone.getId())).or(Long.MAX_VALUE); } } @@ -548,118 +466,69 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return {@code true} if the target Sone is trusted by the origin Sone */ public boolean isSoneTrusted(Sone origin, Sone target) { - Validation.begin().isNotNull("Origin", origin).isNotNull("Target", target).check().isInstanceOf("Origin’s OwnIdentity", origin.getIdentity(), OwnIdentity.class).check(); + checkNotNull(origin, "origin must not be null"); + checkNotNull(target, "target must not be null"); + checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin’s identity must be an OwnIdentity"); return trustedIdentities.containsKey(origin.getIdentity()) && trustedIdentities.get(origin.getIdentity()).contains(target.getIdentity()); } /** - * Returns the post with the given ID. + * Returns a post builder. * - * @param postId - * The ID of the post to get - * @return The post with the given ID, or a new post with the given ID + * @return A new post builder */ - public Post getPost(String postId) { - return getPost(postId, true); + public PostBuilder postBuilder() { + return database.newPostBuilder(); } /** - * Returns the post with the given ID, optionally creating a new post. - * - * @param postId - * The ID of the post to get - * @param create - * {@code true} it create a new post if no post with the given ID - * exists, {@code false} to return {@code null} - * @return The post, or {@code null} if there is no such post + * {@inheritDoc} */ @Override - public Post getPost(String postId, boolean create) { - synchronized (posts) { - Post post = posts.get(postId); - if ((post == null) && create) { - post = new PostImpl(postId); - posts.put(postId, post); - } - return post; - } + public Optional getPost(String postId) { + return database.getPost(postId); } /** - * Returns all posts that have the given Sone as recipient. - * - * @see Post#getRecipient() - * @param recipient - * The recipient of the posts - * @return All posts that have the given Sone as recipient - */ - public Set getDirectedPosts(Sone recipient) { - Validation.begin().isNotNull("Recipient", recipient).check(); - Set directedPosts = new HashSet(); - synchronized (posts) { - for (Post post : posts.values()) { - if (recipient.equals(post.getRecipient())) { - directedPosts.add(post); - } - } - } - return directedPosts; + * {@inheritDocs} + */ + @Override + public Collection getPosts(String soneId) { + return database.getPosts(soneId); } /** - * Returns the reply with the given ID. If there is no reply with the given - * ID yet, a new one is created. - * - * @param replyId - * The ID of the reply to get - * @return The reply + * {@inheritDoc} */ - public PostReply getReply(String replyId) { - return getReply(replyId, true); + @Override + public Collection getDirectedPosts(final String recipientId) { + checkNotNull(recipientId, "recipient must not be null"); + return database.getDirectedPosts(recipientId); } /** - * Returns the reply with the given ID. If there is no reply with the given - * ID yet, a new one is created, unless {@code create} is false in which - * case {@code null} is returned. + * Returns a post reply builder. * - * @param replyId - * The ID of the reply to get - * @param create - * {@code true} to always return a {@link Reply}, {@code false} - * to return {@code null} if no reply can be found - * @return The reply, or {@code null} if there is no such reply + * @return A new post reply builder */ - public PostReply getReply(String replyId, boolean create) { - synchronized (replies) { - PostReply reply = replies.get(replyId); - if (create && (reply == null)) { - reply = new PostReply(replyId); - replies.put(replyId, reply); - } - return reply; - } + public PostReplyBuilder postReplyBuilder() { + return database.newPostReplyBuilder(); } /** - * Returns all replies for the given post, order ascending by time. - * - * @param post - * The post to get all replies for - * @return All replies for the given post + * {@inheritDoc} */ - public List getReplies(Post post) { - Set sones = getSones(); - List replies = new ArrayList(); - for (Sone sone : sones) { - for (PostReply reply : sone.getReplies()) { - if (reply.getPost().equals(post)) { - replies.add(reply); - } - } - } - Collections.sort(replies, Reply.TIME_COMPARATOR); - return replies; + @Override + public Optional getPostReply(String replyId) { + return database.getPostReply(replyId); + } + + /** + * {@inheritDoc} + */ + @Override + public List getReplies(final String postId) { + return database.getReplies(postId); } /** @@ -731,9 +600,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis Set posts = new HashSet(); synchronized (bookmarkedPosts) { for (String bookmarkedPostId : bookmarkedPosts) { - Post post = getPost(bookmarkedPostId, false); - if (post != null) { - posts.add(post); + Optional post = getPost(bookmarkedPostId); + if (!post.isPresent()) { + posts.add(post.get()); } } } @@ -838,7 +707,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void lockSone(Sone sone) { synchronized (lockedSones) { if (lockedSones.add(sone)) { - coreListenerManager.fireSoneLocked(sone); + eventBus.post(new SoneLockedEvent(sone)); } } } @@ -853,7 +722,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void unlockSone(Sone sone) { synchronized (lockedSones) { if (lockedSones.remove(sone)) { - coreListenerManager.fireSoneUnlocked(sone); + eventBus.post(new SoneUnlockedEvent(sone)); } } } @@ -870,10 +739,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Given OwnIdentity is null!"); return null; } - synchronized (localSones) { + synchronized (sones) { final Sone sone; try { - sone = getLocalSone(ownIdentity.getId()).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); + sone = getLocalSone(ownIdentity.getId(), true).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); } catch (MalformedURLException mue1) { logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1); return null; @@ -882,9 +751,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); sone.setKnown(true); /* TODO - load posts ’n stuff */ - localSones.put(ownIdentity.getId(), sone); - final SoneInserter soneInserter = new SoneInserter(this, freenetInterface, sone); - soneInserter.addSoneInsertListener(this); + trustedIdentities.put(ownIdentity, Collections.synchronizedSet(new HashSet())); + sones.put(ownIdentity.getId(), sone); + final SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, sone); soneInserters.put(sone, soneInserter); sone.setStatus(SoneStatus.idle); loadSone(sone); @@ -913,7 +782,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis sone.getOptions().addBooleanOption("ShowNotification/NewReplies", new DefaultOption(true)); sone.getOptions().addEnumOption("ShowCustomAvatars", new DefaultOption(ShowCustomAvatars.NEVER)); - followSone(sone, getSone("nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI")); + followSone(sone, "nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"); touchConfiguration(); return sone; } @@ -930,10 +799,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Given Identity is null!"); return null; } - synchronized (remoteSones) { + synchronized (sones) { final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity); boolean newSone = sone.getRequestUri() == null; - sone.setRequestUri(getSoneUri(identity.getRequestUri())); + sone.setRequestUri(SoneUri.create(identity.getRequestUri())); sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)); if (newSone) { synchronized (knownSones) { @@ -941,10 +810,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } sone.setKnown(!newSone); if (newSone) { - coreListenerManager.fireNewSoneFound(sone); + eventBus.post(new NewSoneFoundEvent(sone)); for (Sone localSone : getLocalSones()) { if (localSone.getOptions().getBooleanOption("AutoFollow").get()) { - followSone(localSone, sone); + followSone(localSone, sone.getId()); } } } @@ -972,39 +841,23 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The ID of the Sone to follow */ public void followSone(Sone sone, String soneId) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Sone ID", soneId).check(); - Sone followedSone = getSone(soneId, true); - if (followedSone == null) { - logger.log(Level.INFO, String.format("Ignored Sone with invalid ID: %s", soneId)); - return; - } - followSone(sone, getSone(soneId)); - } - - /** - * Lets the given local Sone follow the other given Sone. If the given Sone - * was not followed by any local Sone before, this will mark all elements of - * the followed Sone as read that have been created before the current - * moment. - * - * @param sone - * The local Sone that should follow the other Sone - * @param followedSone - * The Sone that should be followed - */ - public void followSone(Sone sone, Sone followedSone) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Followed Sone", followedSone).check(); - sone.addFriend(followedSone.getId()); + checkNotNull(sone, "sone must not be null"); + checkNotNull(soneId, "soneId must not be null"); + sone.addFriend(soneId); synchronized (soneFollowingTimes) { - if (!soneFollowingTimes.containsKey(followedSone)) { + if (!soneFollowingTimes.containsKey(soneId)) { long now = System.currentTimeMillis(); - soneFollowingTimes.put(followedSone, now); - for (Post post : followedSone.getPosts()) { + soneFollowingTimes.put(soneId, now); + Optional followedSone = getSone(soneId); + if (!followedSone.isPresent()) { + return; + } + for (Post post : followedSone.get().getPosts()) { if (post.getTime() < now) { markPostKnown(post); } } - for (PostReply reply : followedSone.getReplies()) { + for (PostReply reply : followedSone.get().getReplies()) { if (reply.getTime() < now) { markReplyKnown(reply); } @@ -1023,30 +876,16 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The ID of the Sone being unfollowed */ public void unfollowSone(Sone sone, String soneId) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Sone ID", soneId).check(); - unfollowSone(sone, getSone(soneId, false)); - } - - /** - * Lets the given local Sone unfollow the other given Sone. If the given - * local Sone is the last local Sone that followed the given Sone, its - * following time will be removed. - * - * @param sone - * The local Sone that should unfollow another Sone - * @param unfollowedSone - * The Sone being unfollowed - */ - public void unfollowSone(Sone sone, Sone unfollowedSone) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Unfollowed Sone", unfollowedSone).check(); - sone.removeFriend(unfollowedSone.getId()); + checkNotNull(sone, "sone must not be null"); + checkNotNull(soneId, "soneId must not be null"); + sone.removeFriend(soneId); boolean unfollowedSoneStillFollowed = false; for (Sone localSone : getLocalSones()) { - unfollowedSoneStillFollowed |= localSone.hasFriend(unfollowedSone.getId()); + unfollowedSoneStillFollowed |= localSone.hasFriend(soneId); } if (!unfollowedSoneStillFollowed) { synchronized (soneFollowingTimes) { - soneFollowingTimes.remove(unfollowedSone); + soneFollowingTimes.remove(soneId); } } touchConfiguration(); @@ -1063,7 +902,10 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The trust value (from {@code -100} to {@code 100}) */ public void setTrust(Sone origin, Sone target, int trustValue) { - Validation.begin().isNotNull("Trust Origin", origin).check().isInstanceOf("Trust Origin", origin.getIdentity(), OwnIdentity.class).isNotNull("Trust Target", target).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check(); + checkNotNull(origin, "origin must not be null"); + checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin must be a local Sone"); + checkNotNull(target, "target must not be null"); + checkArgument((trustValue >= -100) && (trustValue <= 100), "trustValue must be within [-100, 100]"); webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), trustValue, preferences.getTrustComment()); } @@ -1076,7 +918,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The trust target */ public void removeTrust(Sone origin, Sone target) { - Validation.begin().isNotNull("Trust Origin", origin).isNotNull("Trust Target", target).check().isInstanceOf("Trust Origin Identity", origin.getIdentity(), OwnIdentity.class).check(); + checkNotNull(origin, "origin must not be null"); + checkNotNull(target, "target must not be null"); + checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin must be a local Sone"); webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), null, null); } @@ -1138,65 +982,54 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * of the age of the given Sone */ public void updateSone(Sone sone, boolean soneRescueMode) { - if (hasSone(sone.getId())) { - Sone storedSone = getSone(sone.getId()); - if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) { + Optional storedSone = getSone(sone.getId()); + if (storedSone.isPresent()) { + if (!soneRescueMode && !(sone.getTime() > storedSone.get().getTime())) { logger.log(Level.FINE, String.format("Downloaded Sone %s is not newer than stored Sone %s.", sone, storedSone)); return; } - synchronized (posts) { - if (!soneRescueMode) { - for (Post post : storedSone.getPosts()) { - posts.remove(post.getId()); - if (!sone.getPosts().contains(post)) { - coreListenerManager.firePostRemoved(post); - } - } + /* find removed posts. */ + Collection existingPosts = database.getPosts(sone.getId()); + for (Post oldPost : existingPosts) { + if (!sone.getPosts().contains(oldPost)) { + eventBus.post(new PostRemovedEvent(oldPost)); } - List storedPosts = storedSone.getPosts(); - synchronized (knownPosts) { - for (Post post : sone.getPosts()) { - post.setSone(storedSone).setKnown(knownPosts.contains(post.getId())); - if (!storedPosts.contains(post)) { - if (post.getTime() < getSoneFollowingTime(sone)) { - knownPosts.add(post.getId()); - } else if (!knownPosts.contains(post.getId())) { - sone.setKnown(false); - coreListenerManager.fireNewPostFound(post); - } - } - posts.put(post.getId(), post); - } + } + /* find new posts. */ + for (Post newPost : sone.getPosts()) { + if (existingPosts.contains(newPost)) { + continue; + } + if (newPost.getTime() < getSoneFollowingTime(sone)) { + newPost.setKnown(true); + } else if (!newPost.isKnown()) { + eventBus.post(new NewPostFoundEvent(newPost)); } } - synchronized (replies) { - if (!soneRescueMode) { - for (PostReply reply : storedSone.getReplies()) { - replies.remove(reply.getId()); - if (!sone.getReplies().contains(reply)) { - coreListenerManager.fireReplyRemoved(reply); - } + /* store posts. */ + database.storePosts(sone, sone.getPosts()); + if (!soneRescueMode) { + for (PostReply reply : storedSone.get().getReplies()) { + if (!sone.getReplies().contains(reply)) { + eventBus.post(new PostReplyRemovedEvent(reply)); } } - Set storedReplies = storedSone.getReplies(); - synchronized (knownReplies) { - for (PostReply reply : sone.getReplies()) { - reply.setSone(storedSone).setKnown(knownReplies.contains(reply.getId())); - if (!storedReplies.contains(reply)) { - if (reply.getTime() < getSoneFollowingTime(sone)) { - knownReplies.add(reply.getId()); - } else if (!knownReplies.contains(reply.getId())) { - reply.setKnown(false); - coreListenerManager.fireNewReplyFound(reply); - } - } - replies.put(reply.getId(), reply); - } + } + Set storedReplies = storedSone.get().getReplies(); + for (PostReply reply : sone.getReplies()) { + if (storedReplies.contains(reply)) { + continue; + } + if (reply.getTime() < getSoneFollowingTime(sone)) { + reply.setKnown(true); + } else if (!reply.isKnown()) { + eventBus.post(new NewPostReplyFoundEvent(reply)); } } + database.storePostReplies(sone, sone.getReplies()); synchronized (albums) { synchronized (images) { - for (Album album : storedSone.getAlbums()) { + for (Album album : storedSone.get().getAlbums()) { albums.remove(album.getId()); for (Image image : album.getImages()) { images.remove(image.getId()); @@ -1210,44 +1043,17 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } } } - synchronized (storedSone) { - if (!soneRescueMode || (sone.getTime() > storedSone.getTime())) { - storedSone.setTime(sone.getTime()); - } - storedSone.setClient(sone.getClient()); - storedSone.setProfile(sone.getProfile()); - if (soneRescueMode) { - for (Post post : sone.getPosts()) { - storedSone.addPost(post); - } - for (PostReply reply : sone.getReplies()) { - storedSone.addReply(reply); - } - for (String likedPostId : sone.getLikedPostIds()) { - storedSone.addLikedPostId(likedPostId); - } - for (String likedReplyId : sone.getLikedReplyIds()) { - storedSone.addLikedReplyId(likedReplyId); - } - for (Album album : sone.getAlbums()) { - storedSone.addAlbum(album); - } - } else { - storedSone.setPosts(sone.getPosts()); - storedSone.setReplies(sone.getReplies()); - storedSone.setLikePostIds(sone.getLikedPostIds()); - storedSone.setLikeReplyIds(sone.getLikedReplyIds()); - storedSone.setAlbums(sone.getAlbums()); - } - storedSone.setLatestEdition(sone.getLatestEdition()); + synchronized (sones) { + sone.setOptions(storedSone.get().getOptions()); + sones.put(sone.getId(), sone); } } } /** * Deletes the given Sone. This will remove the Sone from the - * {@link #getLocalSone(String) local Sones}, stops its {@link SoneInserter} - * and remove the context from its identity. + * {@link #getLocalSones() local Sones}, stop its {@link SoneInserter} and + * remove the context from its identity. * * @param sone * The Sone to delete @@ -1257,14 +1063,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, String.format("Tried to delete Sone of non-own identity: %s", sone)); return; } - synchronized (localSones) { - if (!localSones.containsKey(sone.getId())) { + synchronized (sones) { + if (!getLocalSones().contains(sone)) { logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone)); return; } - localSones.remove(sone.getId()); + sones.remove(sone.getId()); SoneInserter soneInserter = soneInserters.remove(sone); - soneInserter.removeSoneInsertListener(this); soneInserter.stop(); } webOfTrustUpdater.removeContext((OwnIdentity) sone.getIdentity(), "Sone"); @@ -1278,7 +1083,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** * Marks the given Sone as known. If the Sone was not {@link Post#isKnown() - * known} before, a {@link CoreListener#markSoneKnown(Sone)} event is fired. + * known} before, a {@link MarkSoneKnownEvent} is fired. * * @param sone * The Sone to mark as known @@ -1289,7 +1094,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis synchronized (knownSones) { knownSones.add(sone.getId()); } - coreListenerManager.fireMarkSoneKnown(sone); + eventBus.post(new MarkSoneKnownEvent(sone)); touchConfiguration(); } } @@ -1302,7 +1107,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The Sone to load and update */ public void loadSone(Sone sone) { - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to load non-local Sone: %s", sone)); return; } @@ -1359,11 +1164,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Invalid post found, aborting load!"); return; } - Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText); + PostBuilder postBuilder = postBuilder().withId(postId).from(sone.getId()).withTime(postTime).withText(postText); if ((postRecipientId != null) && (postRecipientId.length() == 43)) { - post.setRecipient(getSone(postRecipientId)); + postBuilder.to(postRecipientId); } - posts.add(post); + posts.add(postBuilder.build()); } /* load replies. */ @@ -1381,7 +1186,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis logger.log(Level.WARNING, "Invalid reply found, aborting load!"); return; } - replies.add(getReply(replyId).setSone(sone).setPost(getPost(postId)).setTime(replyTime).setText(replyText)); + PostReplyBuilder postReplyBuilder = postReplyBuilder().withId(replyId).from(sone.getId()).to(postId).withTime(replyTime).withText(replyText); + replies.add(postReplyBuilder.build()); } /* load post likes. */ @@ -1508,15 +1314,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis knownSones.add(friend); } } - synchronized (knownPosts) { - for (Post post : posts) { - knownPosts.add(post.getId()); - } + database.storePosts(sone, posts); + for (Post post : posts) { + post.setKnown(true); } - synchronized (knownReplies) { - for (PostReply reply : replies) { - knownReplies.add(reply.getId()); - } + database.storePostReplies(sone, replies); + for (PostReply reply : replies) { + reply.setKnown(true); } } @@ -1560,7 +1364,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The text of the post * @return The created post */ - public Post createPost(Sone sone, Sone recipient, String text) { + public Post createPost(Sone sone, Optional recipient, String text) { return createPost(sone, recipient, System.currentTimeMillis(), text); } @@ -1578,22 +1382,24 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The text of the post * @return The created post */ - public Post createPost(Sone sone, Sone recipient, long time, String text) { - if (!isLocalSone(sone)) { + public Post createPost(Sone sone, Optional recipient, long time, String text) { + checkNotNull(text, "text must not be null"); + checkArgument(text.trim().length() > 0, "text must not be empty"); + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone)); return null; } - final Post post = new PostImpl(sone, time, text); - if (recipient != null) { - post.setRecipient(recipient); + PostBuilder postBuilder = database.newPostBuilder(); + postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim()); + if (recipient.isPresent()) { + postBuilder.to(recipient.get().getId()); } - synchronized (posts) { - posts.put(post.getId(), post); - } - coreListenerManager.fireNewPostFound(post); + final Post post = postBuilder.build(); + database.storePost(post); + eventBus.post(new NewPostFoundEvent(post)); sone.addPost(post); touchConfiguration(); - localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() { + localElementTicker.schedule(new Runnable() { /** * {@inheritDoc} @@ -1602,7 +1408,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void run() { markPostKnown(post); } - }, "Mark " + post + " read."); + }, 10, TimeUnit.SECONDS); return post; } @@ -1613,15 +1419,12 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The post to delete */ public void deletePost(Post post) { - if (!isLocalSone(post.getSone())) { + if (!post.getSone().isLocal()) { logger.log(Level.WARNING, String.format("Tried to delete post of non-local Sone: %s", post.getSone())); return; } - post.getSone().removePost(post); - synchronized (posts) { - posts.remove(post.getId()); - } - coreListenerManager.firePostRemoved(post); + database.removePost(post); + eventBus.post(new PostRemovedEvent(post)); markPostKnown(post); touchConfiguration(); } @@ -1635,13 +1438,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ public void markPostKnown(Post post) { post.setKnown(true); - synchronized (knownPosts) { - coreListenerManager.fireMarkPostKnown(post); - if (knownPosts.add(post.getId())) { - touchConfiguration(); - } - } - for (PostReply reply : getReplies(post)) { + eventBus.post(new MarkPostKnownEvent(post)); + touchConfiguration(); + for (PostReply reply : getReplies(post.getId())) { markReplyKnown(reply); } } @@ -1702,37 +1501,20 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The created reply */ public PostReply createReply(Sone sone, Post post, String text) { - return createReply(sone, post, System.currentTimeMillis(), text); - } - - /** - * Creates a new reply. - * - * @param sone - * The Sone that creates the reply - * @param post - * The post that this reply refers to - * @param time - * The time of the reply - * @param text - * The text of the reply - * @return The created reply - */ - public PostReply createReply(Sone sone, Post post, long time, String text) { - if (!isLocalSone(sone)) { + checkNotNull(text, "text must not be null"); + checkArgument(text.trim().length() > 0, "text must not be empty"); + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone)); return null; } - final PostReply reply = new PostReply(sone, post, System.currentTimeMillis(), text); - synchronized (replies) { - replies.put(reply.getId(), reply); - } - synchronized (knownReplies) { - coreListenerManager.fireNewReplyFound(reply); - } + PostReplyBuilder postReplyBuilder = postReplyBuilder(); + postReplyBuilder.randomId().from(sone.getId()).to(post.getId()).currentTime().withText(text.trim()); + final PostReply reply = postReplyBuilder.build(); + database.storePostReply(reply); + eventBus.post(new NewPostReplyFoundEvent(reply)); sone.addReply(reply); touchConfiguration(); - localElementTicker.registerEvent(System.currentTimeMillis() + 10 * 1000, new Runnable() { + localElementTicker.schedule(new Runnable() { /** * {@inheritDoc} @@ -1741,7 +1523,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis public void run() { markReplyKnown(reply); } - }, "Mark " + reply + " read."); + }, 10, TimeUnit.SECONDS); return reply; } @@ -1753,17 +1535,12 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ public void deleteReply(PostReply reply) { Sone sone = reply.getSone(); - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to delete non-local reply: %s", reply)); return; } - synchronized (replies) { - replies.remove(reply.getId()); - } - synchronized (knownReplies) { - markReplyKnown(reply); - knownReplies.remove(reply.getId()); - } + database.removePostReply(reply); + markReplyKnown(reply); sone.removeReply(reply); touchConfiguration(); } @@ -1776,12 +1553,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The reply to mark as known */ public void markReplyKnown(PostReply reply) { + boolean previouslyKnown = reply.isKnown(); reply.setKnown(true); - synchronized (knownReplies) { - coreListenerManager.fireMarkReplyKnown(reply); - if (knownReplies.add(reply.getId())) { - touchConfiguration(); - } + eventBus.post(new MarkPostReplyKnownEvent(reply)); + if (!previouslyKnown) { + touchConfiguration(); } } @@ -1828,7 +1604,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The album to remove */ public void deleteAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().is("Local Sone", isLocalSone(album.getSone())).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().isLocal(), "album’s Sone must be a local Sone"); if (!album.isEmpty()) { return; } @@ -1855,7 +1632,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The newly created image */ public Image createImage(Sone sone, Album album, TemporaryImage temporaryImage) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Album", album).isNotNull("Temporary Image", temporaryImage).check().is("Local Sone", isLocalSone(sone)).check().isEqual("Owner and Album Owner", sone, album.getSone()).check(); + checkNotNull(sone, "sone must not be null"); + checkNotNull(album, "album must not be null"); + 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()); album.addImage(image); synchronized (images) { @@ -1874,7 +1655,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The image to delete */ public void deleteImage(Image image) { - Validation.begin().isNotNull("Image", image).check().is("Local Sone", isLocalSone(image.getSone())).check(); + checkNotNull(image, "image must not be null"); + checkArgument(image.getSone().isLocal(), "image must belong to a local Sone"); deleteTemporaryImage(image.getId()); image.getAlbum().removeImage(image); synchronized (images) { @@ -1908,7 +1690,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The temporary image to delete */ public void deleteTemporaryImage(TemporaryImage temporaryImage) { - Validation.begin().isNotNull("Temporary Image", temporaryImage).check(); + checkNotNull(temporaryImage, "temporaryImage must not be null"); deleteTemporaryImage(temporaryImage.getId()); } @@ -1919,7 +1701,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The ID of the temporary image to delete */ public void deleteTemporaryImage(String imageId) { - Validation.begin().isNotNull("Temporary Image ID", imageId).check(); + checkNotNull(imageId, "imageId must not be null"); synchronized (temporaryImages) { temporaryImages.remove(imageId); } @@ -1948,9 +1730,11 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis @Override public void serviceStart() { loadConfiguration(); - updateChecker.addUpdateListener(this); updateChecker.start(); + identityManager.start(); + webOfTrustUpdater.init(); webOfTrustUpdater.start(); + database.start(); } /** @@ -1977,18 +1761,20 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ @Override public void serviceStop() { - synchronized (localSones) { + localElementTicker.shutdownNow(); + synchronized (sones) { for (Entry soneInserter : soneInserters.entrySet()) { - soneInserter.getValue().removeSoneInsertListener(this); soneInserter.getValue().stop(); saveSone(soneInserter.getKey()); } } saveConfiguration(); + database.stop(); webOfTrustUpdater.stop(); updateChecker.stop(); - updateChecker.removeUpdateListener(this); soneDownloader.stop(); + soneDownloaders.shutdown(); + identityManager.stop(); } // @@ -2003,7 +1789,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * The Sone to save */ private synchronized void saveSone(Sone sone) { - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to save non-local Sone: %s", sone)); return; } @@ -2043,7 +1829,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis for (Post post : sone.getPosts()) { String postPrefix = sonePrefix + "/Posts/" + postCounter++; configuration.getStringValue(postPrefix + "/ID").setValue(post.getId()); - configuration.getStringValue(postPrefix + "/Recipient").setValue((post.getRecipient() != null) ? post.getRecipient().getId() : null); + configuration.getStringValue(postPrefix + "/Recipient").setValue(post.getRecipientId().orNull()); configuration.getLongValue(postPrefix + "/Time").setValue(post.getTime()); configuration.getStringValue(postPrefix + "/Text").setValue(post.getText()); } @@ -2054,7 +1840,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis for (PostReply reply : sone.getReplies()) { String replyPrefix = sonePrefix + "/Replies/" + replyCounter++; configuration.getStringValue(replyPrefix + "/ID").setValue(reply.getId()); - configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPost().getId()); + configuration.getStringValue(replyPrefix + "/Post/ID").setValue(reply.getPostId()); configuration.getLongValue(replyPrefix + "/Time").setValue(reply.getTime()); configuration.getStringValue(replyPrefix + "/Text").setValue(reply.getText()); } @@ -2082,7 +1868,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); /* save albums. first, collect in a flat structure, top-level first. */ - List albums = sone.getAllAlbums(); + List albums = FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList(); int albumCounter = 0; for (Album album : albums) { @@ -2172,8 +1958,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /* save Sone following times. */ soneCounter = 0; synchronized (soneFollowingTimes) { - for (Entry soneFollowingTime : soneFollowingTimes.entrySet()) { - configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey().getId()); + for (Entry soneFollowingTime : soneFollowingTimes.entrySet()) { + configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey()); configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").setValue(soneFollowingTime.getValue()); ++soneCounter; } @@ -2181,22 +1967,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } /* save known posts. */ - int postCounter = 0; - synchronized (knownPosts) { - for (String knownPostId : knownPosts) { - configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").setValue(knownPostId); - } - configuration.getStringValue("KnownPosts/" + postCounter + "/ID").setValue(null); - } - - /* save known replies. */ - int replyCounter = 0; - synchronized (knownReplies) { - for (String knownReplyId : knownReplies) { - configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").setValue(knownReplyId); - } - configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null); - } + database.save(); /* save bookmarked posts. */ int bookmarkedPostCounter = 0; @@ -2212,6 +1983,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } catch (ConfigurationException ce1) { logger.log(Level.SEVERE, "Could not store configuration!", ce1); + } catch (DatabaseException de1) { + logger.log(Level.SEVERE, "Could not save database!", de1); } finally { synchronized (configuration) { storingConfiguration = false; @@ -2222,10 +1995,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /** * Loads the configuration. */ - @SuppressWarnings("unchecked") private void loadConfiguration() { /* create options. */ - options.addIntegerOption("InsertionDelay", new DefaultOption(60, new IntegerRangeValidator(0, Integer.MAX_VALUE), new OptionWatcher() { + options.addIntegerOption("InsertionDelay", new DefaultOption(60, new IntegerRangePredicate(0, Integer.MAX_VALUE), new OptionWatcher() { @Override public void optionChanged(Option option, Integer oldValue, Integer newValue) { @@ -2233,13 +2005,13 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } })); - options.addIntegerOption("PostsPerPage", new DefaultOption(10, new IntegerRangeValidator(1, Integer.MAX_VALUE))); - options.addIntegerOption("ImagesPerPage", new DefaultOption(9, new IntegerRangeValidator(1, Integer.MAX_VALUE))); - options.addIntegerOption("CharactersPerPost", new DefaultOption(400, new OrValidator(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator(-1)))); - options.addIntegerOption("PostCutOffLength", new DefaultOption(200, new OrValidator(new IntegerRangeValidator(50, Integer.MAX_VALUE), new EqualityValidator(-1)))); + options.addIntegerOption("PostsPerPage", new DefaultOption(10, new IntegerRangePredicate(1, Integer.MAX_VALUE))); + options.addIntegerOption("ImagesPerPage", new DefaultOption(9, new IntegerRangePredicate(1, Integer.MAX_VALUE))); + options.addIntegerOption("CharactersPerPost", new DefaultOption(400, Predicates. or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1)))); + options.addIntegerOption("PostCutOffLength", new DefaultOption(200, Predicates. or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1)))); options.addBooleanOption("RequireFullAccess", new DefaultOption(false)); - options.addIntegerOption("PositiveTrust", new DefaultOption(75, new IntegerRangeValidator(0, 100))); - options.addIntegerOption("NegativeTrust", new DefaultOption(-25, new IntegerRangeValidator(-100, 100))); + options.addIntegerOption("PositiveTrust", new DefaultOption(75, new IntegerRangePredicate(0, 100))); + options.addIntegerOption("NegativeTrust", new DefaultOption(-25, new IntegerRangePredicate(-100, 100))); options.addStringOption("TrustComment", new DefaultOption("Set from Sone Web Interface")); options.addBooleanOption("ActivateFcpInterface", new DefaultOption(false, new OptionWatcher() { @@ -2291,41 +2063,12 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis break; } long time = configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").getValue(Long.MAX_VALUE); - Sone followedSone = getSone(soneId); - if (followedSone == null) { - logger.log(Level.WARNING, String.format("Ignoring Sone with invalid ID: %s", soneId)); - } else { - synchronized (soneFollowingTimes) { - soneFollowingTimes.put(getSone(soneId), time); - } + synchronized (soneFollowingTimes) { + soneFollowingTimes.put(soneId, time); } ++soneCounter; } - /* load known posts. */ - int postCounter = 0; - while (true) { - String knownPostId = configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").getValue(null); - if (knownPostId == null) { - break; - } - synchronized (knownPosts) { - knownPosts.add(knownPostId); - } - } - - /* load known replies. */ - int replyCounter = 0; - while (true) { - String knownReplyId = configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").getValue(null); - if (knownReplyId == null) { - break; - } - synchronized (knownReplies) { - knownReplies.add(knownReplyId); - } - } - /* load bookmarked posts. */ int bookmarkedPostCounter = 0; while (true) { @@ -2356,63 +2099,57 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis } /** - * Generate a Sone URI from the given URI and latest edition. + * Notifies the core that a new {@link OwnIdentity} was added. * - * @param uriString - * The URI to derive the Sone URI from - * @return The derived URI - */ - private static FreenetURI getSoneUri(String uriString) { - try { - FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]); - return uri; - } catch (MalformedURLException mue1) { - logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uriString), mue1); - return null; - } - } - - // - // INTERFACE IdentityListener - // - - /** - * {@inheritDoc} + * @param ownIdentityAddedEvent + * The event */ - @Override - public void ownIdentityAdded(OwnIdentity ownIdentity) { + @Subscribe + public void ownIdentityAdded(OwnIdentityAddedEvent ownIdentityAddedEvent) { + OwnIdentity ownIdentity = ownIdentityAddedEvent.ownIdentity(); logger.log(Level.FINEST, String.format("Adding OwnIdentity: %s", ownIdentity)); if (ownIdentity.hasContext("Sone")) { - trustedIdentities.put(ownIdentity, Collections.synchronizedSet(new HashSet())); addLocalSone(ownIdentity); } } /** - * {@inheritDoc} + * Notifies the core that an {@link OwnIdentity} was removed. + * + * @param ownIdentityRemovedEvent + * The event */ - @Override - public void ownIdentityRemoved(OwnIdentity ownIdentity) { + @Subscribe + public void ownIdentityRemoved(OwnIdentityRemovedEvent ownIdentityRemovedEvent) { + OwnIdentity ownIdentity = ownIdentityRemovedEvent.ownIdentity(); logger.log(Level.FINEST, String.format("Removing OwnIdentity: %s", ownIdentity)); trustedIdentities.remove(ownIdentity); } /** - * {@inheritDoc} + * Notifies the core that a new {@link Identity} was added. + * + * @param identityAddedEvent + * The event */ - @Override - public void identityAdded(OwnIdentity ownIdentity, Identity identity) { + @Subscribe + public void identityAdded(IdentityAddedEvent identityAddedEvent) { + Identity identity = identityAddedEvent.identity(); logger.log(Level.FINEST, String.format("Adding Identity: %s", identity)); - trustedIdentities.get(ownIdentity).add(identity); + trustedIdentities.get(identityAddedEvent.ownIdentity()).add(identity); addRemoteSone(identity); } /** - * {@inheritDoc} + * Notifies the core that an {@link Identity} was updated. + * + * @param identityUpdatedEvent + * The event */ - @Override - public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) { - new Thread(new Runnable() { + @Subscribe + public void identityUpdated(IdentityUpdatedEvent identityUpdatedEvent) { + final Identity identity = identityUpdatedEvent.identity(); + soneDownloaders.execute(new Runnable() { @Override @SuppressWarnings("synthetic-access") @@ -2423,14 +2160,19 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis soneDownloader.addSone(sone); soneDownloader.fetchSone(sone); } - }).start(); + }); } /** - * {@inheritDoc} + * Notifies the core that an {@link Identity} was removed. + * + * @param identityRemovedEvent + * The event */ - @Override - public void identityRemoved(OwnIdentity ownIdentity, Identity identity) { + @Subscribe + public void identityRemoved(IdentityRemovedEvent identityRemovedEvent) { + OwnIdentity ownIdentity = identityRemovedEvent.ownIdentity(); + Identity identity = identityRemovedEvent.identity(); trustedIdentities.get(ownIdentity).remove(identity); boolean foundIdentity = false; for (Entry> trustedIdentity : trustedIdentities.entrySet()) { @@ -2445,468 +2187,37 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis /* some local identity still trusts this identity, don’t remove. */ return; } - Sone sone = getSone(identity.getId(), false); - if (sone == null) { + Optional sone = getSone(identity.getId()); + if (!sone.isPresent()) { /* TODO - we don’t have the Sone anymore. should this happen? */ return; } - synchronized (posts) { - synchronized (knownPosts) { - for (Post post : sone.getPosts()) { - posts.remove(post.getId()); - coreListenerManager.firePostRemoved(post); - } - } + database.removePosts(sone.get()); + for (Post post : sone.get().getPosts()) { + eventBus.post(new PostRemovedEvent(post)); } - synchronized (replies) { - synchronized (knownReplies) { - for (PostReply reply : sone.getReplies()) { - replies.remove(reply.getId()); - coreListenerManager.fireReplyRemoved(reply); - } - } + database.removePostReplies(sone.get()); + for (PostReply reply : sone.get().getReplies()) { + eventBus.post(new PostReplyRemovedEvent(reply)); } - synchronized (remoteSones) { - remoteSones.remove(identity.getId()); + synchronized (sones) { + sones.remove(identity.getId()); } - coreListenerManager.fireSoneRemoved(sone); - } - - // - // INTERFACE UpdateListener - // - - /** - * {@inheritDoc} - */ - @Override - public void updateFound(Version version, long releaseTime, long latestEdition) { - coreListenerManager.fireUpdateFound(version, releaseTime, latestEdition); + eventBus.post(new SoneRemovedEvent(sone.get())); } - // - // INTERFACE ImageInsertListener - // - /** - * {@inheritDoc} - */ - @Override - public void insertStarted(Sone sone) { - coreListenerManager.fireSoneInserting(sone); - } - - /** - * {@inheritDoc} - */ - @Override - public void insertFinished(Sone sone, long insertDuration) { - coreListenerManager.fireSoneInserted(sone, insertDuration); - } - - /** - * {@inheritDoc} - */ - @Override - public void insertAborted(Sone sone, Throwable cause) { - coreListenerManager.fireSoneInsertAborted(sone, cause); - } - - // - // SONEINSERTLISTENER METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public void imageInsertStarted(Image image) { - logger.log(Level.WARNING, String.format("Image insert started for %s...", image)); - coreListenerManager.fireImageInsertStarted(image); - } - - /** - * {@inheritDoc} - */ - @Override - public void imageInsertAborted(Image image) { - logger.log(Level.WARNING, String.format("Image insert aborted for %s.", image)); - coreListenerManager.fireImageInsertAborted(image); - } - - /** - * {@inheritDoc} + * Deletes the temporary image. + * + * @param imageInsertFinishedEvent + * The event */ - @Override - public void imageInsertFinished(Image image, FreenetURI key) { - logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", image, key)); - image.setKey(key.toString()); - deleteTemporaryImage(image.getId()); + @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()); + deleteTemporaryImage(imageInsertFinishedEvent.image().getId()); touchConfiguration(); - coreListenerManager.fireImageInsertFinished(image); - } - - /** - * {@inheritDoc} - */ - @Override - public void imageInsertFailed(Image image, Throwable cause) { - logger.log(Level.WARNING, String.format("Image insert failed for %s." + image), cause); - coreListenerManager.fireImageInsertFailed(image, cause); - } - - /** - * Convenience interface for external classes that want to access the core’s - * configuration. - * - * @author David ‘Bombe’ Roden - */ - public static class Preferences { - - /** The wrapped options. */ - private final Options options; - - /** - * Creates a new preferences object wrapped around the given options. - * - * @param options - * The options to wrap - */ - public Preferences(Options options) { - this.options = options; - } - - /** - * Returns the insertion delay. - * - * @return The insertion delay - */ - public int getInsertionDelay() { - return options.getIntegerOption("InsertionDelay").get(); - } - - /** - * Validates the given insertion delay. - * - * @param insertionDelay - * The insertion delay to validate - * @return {@code true} if the given insertion delay was valid, - * {@code false} otherwise - */ - public boolean validateInsertionDelay(Integer insertionDelay) { - return options.getIntegerOption("InsertionDelay").validate(insertionDelay); - } - - /** - * Sets the insertion delay - * - * @param insertionDelay - * The new insertion delay, or {@code null} to restore it to - * the default value - * @return This preferences - */ - public Preferences setInsertionDelay(Integer insertionDelay) { - options.getIntegerOption("InsertionDelay").set(insertionDelay); - return this; - } - - /** - * Returns the number of posts to show per page. - * - * @return The number of posts to show per page - */ - public int getPostsPerPage() { - return options.getIntegerOption("PostsPerPage").get(); - } - - /** - * Validates the number of posts per page. - * - * @param postsPerPage - * The number of posts per page - * @return {@code true} if the number of posts per page was valid, - * {@code false} otherwise - */ - public boolean validatePostsPerPage(Integer postsPerPage) { - return options.getIntegerOption("PostsPerPage").validate(postsPerPage); - } - - /** - * Sets the number of posts to show per page. - * - * @param postsPerPage - * The number of posts to show per page - * @return This preferences object - */ - public Preferences setPostsPerPage(Integer postsPerPage) { - options.getIntegerOption("PostsPerPage").set(postsPerPage); - return this; - } - - /** - * Returns the number of images to show per page. - * - * @return The number of images to show per page - */ - public int getImagesPerPage() { - return options.getIntegerOption("ImagesPerPage").get(); - } - - /** - * Validates the number of images per page. - * - * @param imagesPerPage - * The number of images per page - * @return {@code true} if the number of images per page was valid, - * {@code false} otherwise - */ - public boolean validateImagesPerPage(Integer imagesPerPage) { - return options.getIntegerOption("ImagesPerPage").validate(imagesPerPage); - } - - /** - * Sets the number of images per page. - * - * @param imagesPerPage - * The number of images per page - * @return This preferences object - */ - public Preferences setImagesPerPage(Integer imagesPerPage) { - options.getIntegerOption("ImagesPerPage").set(imagesPerPage); - return this; - } - - /** - * Returns the number of characters per post, or -1 if the - * posts should not be cut off. - * - * @return The numbers of characters per post - */ - public int getCharactersPerPost() { - return options.getIntegerOption("CharactersPerPost").get(); - } - - /** - * Validates the number of characters per post. - * - * @param charactersPerPost - * The number of characters per post - * @return {@code true} if the number of characters per post was valid, - * {@code false} otherwise - */ - public boolean validateCharactersPerPost(Integer charactersPerPost) { - return options.getIntegerOption("CharactersPerPost").validate(charactersPerPost); - } - - /** - * Sets the number of characters per post. - * - * @param charactersPerPost - * The number of characters per post, or -1 to - * not cut off the posts - * @return This preferences objects - */ - public Preferences setCharactersPerPost(Integer charactersPerPost) { - options.getIntegerOption("CharactersPerPost").set(charactersPerPost); - return this; - } - - /** - * Returns the number of characters the shortened post should have. - * - * @return The number of characters of the snippet - */ - public int getPostCutOffLength() { - return options.getIntegerOption("PostCutOffLength").get(); - } - - /** - * Validates the number of characters after which to cut off the post. - * - * @param postCutOffLength - * The number of characters of the snippet - * @return {@code true} if the number of characters of the snippet is - * valid, {@code false} otherwise - */ - public boolean validatePostCutOffLength(Integer postCutOffLength) { - return options.getIntegerOption("PostCutOffLength").validate(postCutOffLength); - } - - /** - * Sets the number of characters the shortened post should have. - * - * @param postCutOffLength - * The number of characters of the snippet - * @return This preferences - */ - public Preferences setPostCutOffLength(Integer postCutOffLength) { - options.getIntegerOption("PostCutOffLength").set(postCutOffLength); - return this; - } - - /** - * Returns whether Sone requires full access to be even visible. - * - * @return {@code true} if Sone requires full access, {@code false} - * otherwise - */ - public boolean isRequireFullAccess() { - return options.getBooleanOption("RequireFullAccess").get(); - } - - /** - * Sets whether Sone requires full access to be even visible. - * - * @param requireFullAccess - * {@code true} if Sone requires full access, {@code false} - * otherwise - */ - public void setRequireFullAccess(Boolean requireFullAccess) { - options.getBooleanOption("RequireFullAccess").set(requireFullAccess); - } - - /** - * Returns the positive trust. - * - * @return The positive trust - */ - public int getPositiveTrust() { - return options.getIntegerOption("PositiveTrust").get(); - } - - /** - * Validates the positive trust. - * - * @param positiveTrust - * The positive trust to validate - * @return {@code true} if the positive trust was valid, {@code false} - * otherwise - */ - public boolean validatePositiveTrust(Integer positiveTrust) { - return options.getIntegerOption("PositiveTrust").validate(positiveTrust); - } - - /** - * Sets the positive trust. - * - * @param positiveTrust - * The new positive trust, or {@code null} to restore it to - * the default vlaue - * @return This preferences - */ - public Preferences setPositiveTrust(Integer positiveTrust) { - options.getIntegerOption("PositiveTrust").set(positiveTrust); - return this; - } - - /** - * Returns the negative trust. - * - * @return The negative trust - */ - public int getNegativeTrust() { - return options.getIntegerOption("NegativeTrust").get(); - } - - /** - * Validates the negative trust. - * - * @param negativeTrust - * The negative trust to validate - * @return {@code true} if the negative trust was valid, {@code false} - * otherwise - */ - public boolean validateNegativeTrust(Integer negativeTrust) { - return options.getIntegerOption("NegativeTrust").validate(negativeTrust); - } - - /** - * Sets the negative trust. - * - * @param negativeTrust - * The negative trust, or {@code null} to restore it to the - * default value - * @return The preferences - */ - public Preferences setNegativeTrust(Integer negativeTrust) { - options.getIntegerOption("NegativeTrust").set(negativeTrust); - return this; - } - - /** - * Returns the trust comment. This is the comment that is set in the web - * of trust when a trust value is assigned to an identity. - * - * @return The trust comment - */ - public String getTrustComment() { - return options.getStringOption("TrustComment").get(); - } - - /** - * Sets the trust comment. - * - * @param trustComment - * The trust comment, or {@code null} to restore it to the - * default value - * @return This preferences - */ - public Preferences setTrustComment(String trustComment) { - options.getStringOption("TrustComment").set(trustComment); - return this; - } - - /** - * Returns whether the {@link FcpInterface FCP interface} is currently - * active. - * - * @see FcpInterface#setActive(boolean) - * @return {@code true} if the FCP interface is currently active, - * {@code false} otherwise - */ - public boolean isFcpInterfaceActive() { - return options.getBooleanOption("ActivateFcpInterface").get(); - } - - /** - * Sets whether the {@link FcpInterface FCP interface} is currently - * active. - * - * @see FcpInterface#setActive(boolean) - * @param fcpInterfaceActive - * {@code true} to activate the FCP interface, {@code false} - * to deactivate the FCP interface - * @return This preferences object - */ - public Preferences setFcpInterfaceActive(boolean fcpInterfaceActive) { - options.getBooleanOption("ActivateFcpInterface").set(fcpInterfaceActive); - return this; - } - - /** - * Returns the action level for which full access to the FCP interface - * is required. - * - * @return The action level for which full access to the FCP interface - * is required - */ - public FullAccessRequired getFcpFullAccessRequired() { - return FullAccessRequired.values()[options.getIntegerOption("FcpFullAccessRequired").get()]; - } - - /** - * Sets the action level for which full access to the FCP interface is - * required - * - * @param fcpFullAccessRequired - * The action level - * @return This preferences - */ - public Preferences setFcpFullAccessRequired(FullAccessRequired fcpFullAccessRequired) { - options.getIntegerOption("FcpFullAccessRequired").set((fcpFullAccessRequired != null) ? fcpFullAccessRequired.ordinal() : null); - return this; - } - } } diff --git a/src/main/java/net/pterodactylus/sone/core/CoreListener.java b/src/main/java/net/pterodactylus/sone/core/CoreListener.java deleted file mode 100644 index b56bff5..0000000 --- a/src/main/java/net/pterodactylus/sone/core/CoreListener.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Sone - CoreListener.java - Copyright © 2010–2012 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.core; - -import java.util.EventListener; - -import net.pterodactylus.sone.data.Image; -import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.PostReply; -import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.util.version.Version; - -/** - * Listener interface for objects that want to be notified on certain - * {@link Core} events, such es discovery of new data. - * - * @author David ‘Bombe’ Roden - */ -public interface CoreListener extends EventListener { - - /** - * Notifies a listener that a new Sone has been discovered. - * - * @param sone - * The new Sone - */ - public void newSoneFound(Sone sone); - - /** - * Notifies a listener that a new post has been found. - * - * @param post - * The new post - */ - public void newPostFound(Post post); - - /** - * Notifies a listener that a new reply has been found. - * - * @param reply - * The new reply - */ - public void newReplyFound(PostReply reply); - - /** - * Notifies a listener that the given Sone is now marked as known. - * - * @param sone - * The known Sone - */ - public void markSoneKnown(Sone sone); - - /** - * Notifies a listener that the given post is now marked as known. - * - * @param post - * The known post - */ - public void markPostKnown(Post post); - - /** - * Notifies a listener that the given reply is now marked as known. - * - * @param reply - * The known reply - */ - public void markReplyKnown(PostReply reply); - - /** - * Notifies a listener that the given Sone was removed. - * - * @param sone - * The removed Sone - */ - public void soneRemoved(Sone sone); - - /** - * Notifies a listener that the given post was removed. - * - * @param post - * The removed post - */ - public void postRemoved(Post post); - - /** - * Notifies a listener that the given reply was removed. - * - * @param reply - * The removed reply - */ - public void replyRemoved(PostReply reply); - - /** - * Notifies a listener when a Sone was locked. - * - * @param sone - * The Sone that was locked - */ - public void soneLocked(Sone sone); - - /** - * Notifies a listener that a Sone was unlocked. - * - * @param sone - * The Sone that was unlocked - */ - public void soneUnlocked(Sone sone); - - /** - * Notifies a listener that the insert of the given Sone has started. - * - * @see SoneInsertListener#insertStarted(Sone) - * @param sone - * The Sone that is being inserted - */ - public void soneInserting(Sone sone); - - /** - * Notifies a listener that the insert of the given Sone has finished - * successfully. - * - * @see SoneInsertListener#insertFinished(Sone, long) - * @param sone - * The Sone that has been inserted - * @param insertDuration - * The insert duration (in milliseconds) - */ - public void soneInserted(Sone sone, long insertDuration); - - /** - * Notifies a listener that the insert of the given Sone was aborted. - * - * @see SoneInsertListener#insertAborted(Sone, Throwable) - * @param sone - * The Sone that was inserted - * @param cause - * The cause for the abortion (may be {@code null}) - */ - public void soneInsertAborted(Sone sone, Throwable cause); - - /** - * Notifies a listener that a new version has been found. - * - * @param version - * The version that was found - * @param releaseTime - * The release time of the new version - * @param latestEdition - * The latest edition of the Sone homepage - */ - public void updateFound(Version version, long releaseTime, long latestEdition); - - /** - * Notifies a listener that an image has started being inserted. - * - * @param image - * The image that is now inserted - */ - public void imageInsertStarted(Image image); - - /** - * Notifies a listener that an image insert was aborted by the user. - * - * @param image - * The image that is not inserted anymore - */ - public void imageInsertAborted(Image image); - - /** - * Notifies a listener that an image was successfully inserted. - * - * @param image - * The image that was inserted - */ - public void imageInsertFinished(Image image); - - /** - * Notifies a listener that an image failed to be inserted. - * - * @param image - * The image that could not be inserted - * @param cause - * The reason for the failed insert - */ - public void imageInsertFailed(Image image, Throwable cause); - -} diff --git a/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java b/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java deleted file mode 100644 index 0385698..0000000 --- a/src/main/java/net/pterodactylus/sone/core/CoreListenerManager.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Sone - CoreListenerManager.java - Copyright © 2010–2012 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.core; - -import net.pterodactylus.sone.data.Image; -import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.PostReply; -import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.util.event.AbstractListenerManager; -import net.pterodactylus.util.version.Version; - -/** - * Manager for {@link CoreListener}s. - * - * @author David ‘Bombe’ Roden - */ -public class CoreListenerManager extends AbstractListenerManager { - - /** - * Creates a new core listener manager. - * - * @param source - * The Core - */ - public CoreListenerManager(Core source) { - super(source); - } - - // - // ACTIONS - // - - /** - * Notifies all listeners that a new Sone has been discovered. - * - * @see CoreListener#newSoneFound(Sone) - * @param sone - * The discovered sone - */ - void fireNewSoneFound(Sone sone) { - for (CoreListener coreListener : getListeners()) { - coreListener.newSoneFound(sone); - } - } - - /** - * Notifies all listeners that a new post has been found. - * - * @see CoreListener#newPostFound(Post) - * @param post - * The new post - */ - void fireNewPostFound(Post post) { - for (CoreListener coreListener : getListeners()) { - coreListener.newPostFound(post); - } - } - - /** - * Notifies all listeners that a new reply has been found. - * - * @see CoreListener#newReplyFound(PostReply) - * @param reply - * The new reply - */ - void fireNewReplyFound(PostReply reply) { - for (CoreListener coreListener : getListeners()) { - coreListener.newReplyFound(reply); - } - } - - /** - * Notifies all listeners that the given Sone is now marked as known. - * - * @see CoreListener#markSoneKnown(Sone) - * @param sone - * The known Sone - */ - void fireMarkSoneKnown(Sone sone) { - for (CoreListener coreListener : getListeners()) { - coreListener.markSoneKnown(sone); - } - } - - /** - * Notifies all listeners that the given post is now marked as known. - * - * @param post - * The known post - */ - void fireMarkPostKnown(Post post) { - for (CoreListener coreListener : getListeners()) { - coreListener.markPostKnown(post); - } - } - - /** - * Notifies all listeners that the given reply is now marked as known. - * - * @param reply - * The known reply - */ - void fireMarkReplyKnown(PostReply reply) { - for (CoreListener coreListener : getListeners()) { - coreListener.markReplyKnown(reply); - } - } - - /** - * Notifies all listener that the given Sone was removed. - * - * @see CoreListener#soneRemoved(Sone) - * @param sone - * The removed Sone - */ - void fireSoneRemoved(Sone sone) { - for (CoreListener coreListener : getListeners()) { - coreListener.soneRemoved(sone); - } - } - - /** - * Notifies all listener that the given post was removed. - * - * @see CoreListener#postRemoved(Post) - * @param post - * The removed post - */ - void firePostRemoved(Post post) { - for (CoreListener coreListener : getListeners()) { - coreListener.postRemoved(post); - } - } - - /** - * Notifies all listener that the given reply was removed. - * - * @see CoreListener#replyRemoved(PostReply) - * @param reply - * The removed reply - */ - void fireReplyRemoved(PostReply reply) { - for (CoreListener coreListener : getListeners()) { - coreListener.replyRemoved(reply); - } - } - - /** - * Notifies all listeners that the given Sone was locked. - * - * @see CoreListener#soneLocked(Sone) - * @param sone - * The Sone that was locked - */ - void fireSoneLocked(Sone sone) { - for (CoreListener coreListener : getListeners()) { - coreListener.soneLocked(sone); - } - } - - /** - * Notifies all listeners that the given Sone was unlocked. - * - * @see CoreListener#soneUnlocked(Sone) - * @param sone - * The Sone that was unlocked - */ - void fireSoneUnlocked(Sone sone) { - for (CoreListener coreListener : getListeners()) { - coreListener.soneUnlocked(sone); - } - } - - /** - * Notifies all listeners that the insert of the given Sone has started. - * - * @see SoneInsertListener#insertStarted(Sone) - * @param sone - * The Sone being inserted - */ - void fireSoneInserting(Sone sone) { - for (CoreListener coreListener : getListeners()) { - coreListener.soneInserting(sone); - } - } - - /** - * Notifies all listeners that the insert of the given Sone has finished - * successfully. - * - * @see SoneInsertListener#insertFinished(Sone, long) - * @param sone - * The Sone that was inserted - * @param insertDuration - * The insert duration (in milliseconds) - */ - void fireSoneInserted(Sone sone, long insertDuration) { - for (CoreListener coreListener : getListeners()) { - coreListener.soneInserted(sone, insertDuration); - } - } - - /** - * Notifies all listeners that the insert of the given Sone was aborted. - * - * @see SoneInsertListener#insertStarted(Sone) - * @param sone - * The Sone being inserted - * @param cause - * The cause for the abortion (may be {@code null} - */ - void fireSoneInsertAborted(Sone sone, Throwable cause) { - for (CoreListener coreListener : getListeners()) { - coreListener.soneInsertAborted(sone, cause); - } - } - - /** - * Notifies all listeners that a new version was found. - * - * @see CoreListener#updateFound(Version, long, long) - * @param version - * The new version - * @param releaseTime - * The release time of the new version - * @param latestEdition - * The latest edition of the Sone homepage - */ - void fireUpdateFound(Version version, long releaseTime, long latestEdition) { - for (CoreListener coreListener : getListeners()) { - coreListener.updateFound(version, releaseTime, latestEdition); - } - } - - /** - * Notifies all listeners that an image has started being inserted. - * - * @see CoreListener#imageInsertStarted(Image) - * @param image - * The image that is now inserted - */ - void fireImageInsertStarted(Image image) { - for (CoreListener coreListener : getListeners()) { - coreListener.imageInsertStarted(image); - } - } - - /** - * Notifies all listeners that an image insert was aborted by the user. - * - * @see CoreListener#imageInsertAborted(Image) - * @param image - * The image that is not inserted anymore - */ - void fireImageInsertAborted(Image image) { - for (CoreListener coreListener : getListeners()) { - coreListener.imageInsertAborted(image); - } - } - - /** - * Notifies all listeners that an image was successfully inserted. - * - * @see CoreListener#imageInsertFinished(Image) - * @param image - * The image that was inserted - */ - void fireImageInsertFinished(Image image) { - for (CoreListener coreListener : getListeners()) { - coreListener.imageInsertFinished(image); - } - } - - /** - * Notifies all listeners that an image failed to be inserted. - * - * @see CoreListener#imageInsertFailed(Image, Throwable) - * @param image - * The image that could not be inserted - * @param cause - * The cause of the failure - */ - void fireImageInsertFailed(Image image, Throwable cause) { - for (CoreListener coreListener : getListeners()) { - coreListener.imageInsertFailed(image, cause); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java index a8f3efa..e92cf8e 100644 --- a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java +++ b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetInterface.java - Copyright © 2010–2012 David Roden + * Sone - FreenetInterface.java - Copyright © 2010–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 @@ -18,21 +18,25 @@ package net.pterodactylus.sone.core; import java.net.MalformedURLException; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent; +import net.pterodactylus.sone.core.event.ImageInsertFailedEvent; +import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; +import net.pterodactylus.sone.core.event.ImageInsertStartedEvent; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.TemporaryImage; -import net.pterodactylus.util.collection.Pair; import net.pterodactylus.util.logging.Logging; import com.db4o.ObjectContainer; +import com.google.common.eventbus.EventBus; +import com.google.inject.Inject; import freenet.client.ClientMetadata; import freenet.client.FetchException; @@ -65,6 +69,9 @@ public class FreenetInterface { /** The logger. */ private static final Logger logger = Logging.getLogger(FreenetInterface.class); + /** The event bus. */ + private final EventBus eventBus; + /** The node to interact with. */ private final Node node; @@ -80,10 +87,14 @@ public class FreenetInterface { /** * Creates a new Freenet interface. * + * @param eventBus + * The event bus * @param node * The node to interact with */ - public FreenetInterface(Node node) { + @Inject + public FreenetInterface(EventBus eventBus, Node node) { + this.eventBus = eventBus; this.node = node; this.client = node.clientCore.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS, false, true); } @@ -99,13 +110,13 @@ public class FreenetInterface { * The URI to fetch * @return The result of the fetch, or {@code null} if an error occured */ - public Pair fetchUri(FreenetURI uri) { + public Fetched fetchUri(FreenetURI uri) { FetchResult fetchResult = null; FreenetURI currentUri = new FreenetURI(uri); while (true) { try { fetchResult = client.fetch(currentUri); - return new Pair(currentUri, fetchResult); + return new Fetched(currentUri, fetchResult); } catch (FetchException fe1) { if (fe1.getMode() == FetchException.PERMANENT_REDIRECT) { currentUri = fe1.newURI; @@ -219,7 +230,8 @@ public class FreenetInterface { } }; soneUskCallbacks.put(sone.getId(), uskCallback); - node.clientCore.uskManager.subscribe(USK.create(sone.getRequestUri()), uskCallback, (System.currentTimeMillis() - sone.getTime()) < 7 * 24 * 60 * 60 * 1000, (HighLevelSimpleClientImpl) client); + boolean runBackgroundFetch = (System.currentTimeMillis() - sone.getTime()) < TimeUnit.DAYS.toMillis(7); + node.clientCore.uskManager.subscribe(USK.create(sone.getRequestUri()), uskCallback, runBackgroundFetch, (HighLevelSimpleClientImpl) client); } catch (MalformedURLException mue1) { logger.log(Level.WARNING, String.format("Could not subscribe USK “%s”!", sone.getRequestUri()), mue1); } @@ -300,6 +312,56 @@ public class FreenetInterface { } /** + * Container for a fetched URI and the {@link FetchResult}. + * + * @author David Roden + */ + public static class Fetched { + + /** The fetched URI. */ + private final FreenetURI freenetUri; + + /** The fetch result. */ + private final FetchResult fetchResult; + + /** + * Creates a new fetched URI. + * + * @param freenetUri + * The URI that was fetched + * @param fetchResult + * The fetch result + */ + public Fetched(FreenetURI freenetUri, FetchResult fetchResult) { + this.freenetUri = freenetUri; + this.fetchResult = fetchResult; + } + + // + // ACCESSORS + // + + /** + * Returns the fetched URI. + * + * @return The fetched URI + */ + public FreenetURI getFreenetUri() { + return freenetUri; + } + + /** + * Returns the fetch result. + * + * @return The fetch result + */ + public FetchResult getFetchResult() { + return fetchResult; + } + + } + + /** * Callback for USK watcher events. * * @author David ‘Bombe’ Roden @@ -324,9 +386,12 @@ public class FreenetInterface { } /** - * Insert token that can be used to add {@link ImageInsertListener}s and - * cancel a running insert. + * Insert token that can cancel a running insert and sends events. * + * @see ImageInsertAbortedEvent + * @see ImageInsertStartedEvent + * @see ImageInsertFailedEvent + * @see ImageInsertFinishedEvent * @author David ‘Bombe’ Roden */ public class InsertToken implements ClientPutCallback { @@ -334,9 +399,6 @@ public class FreenetInterface { /** The image being inserted. */ private final Image image; - /** The list of registered image insert listeners. */ - private final List imageInsertListeners = Collections.synchronizedList(new ArrayList()); - /** The client putter. */ private ClientPutter clientPutter; @@ -354,30 +416,6 @@ public class FreenetInterface { } // - // LISTENER MANAGEMENT - // - - /** - * Adds the given listener to the list of registered listener. - * - * @param imageInsertListener - * The listener to add - */ - public void addImageInsertListener(ImageInsertListener imageInsertListener) { - imageInsertListeners.add(imageInsertListener); - } - - /** - * Removes the given listener from the list of registered listener. - * - * @param imageInsertListener - * The listener to remove - */ - public void removeImageInsertListener(ImageInsertListener imageInsertListener) { - imageInsertListeners.remove(imageInsertListener); - } - - // // ACCESSORS // @@ -385,15 +423,13 @@ public class FreenetInterface { * Sets the client putter that is inserting the image. This will also * signal all registered listeners that the image has started. * - * @see ImageInsertListener#imageInsertStarted(Image) * @param clientPutter * The client putter */ + @SuppressWarnings("synthetic-access") public void setClientPutter(ClientPutter clientPutter) { this.clientPutter = clientPutter; - for (ImageInsertListener imageInsertListener : imageInsertListeners) { - imageInsertListener.imageInsertStarted(image); - } + eventBus.post(new ImageInsertStartedEvent(image)); } // @@ -402,15 +438,11 @@ public class FreenetInterface { /** * Cancels the running insert. - * - * @see ImageInsertListener#imageInsertAborted(Image) */ @SuppressWarnings("synthetic-access") public void cancel() { clientPutter.cancel(null, node.clientCore.clientContext); - for (ImageInsertListener imageInsertListener : imageInsertListeners) { - imageInsertListener.imageInsertAborted(image); - } + eventBus.post(new ImageInsertAbortedEvent(image)); } // @@ -429,13 +461,12 @@ public class FreenetInterface { * {@inheritDoc} */ @Override + @SuppressWarnings("synthetic-access") public void onFailure(InsertException insertException, BaseClientPutter clientPutter, ObjectContainer objectContainer) { - for (ImageInsertListener imageInsertListener : imageInsertListeners) { - if ((insertException != null) && ("Cancelled by user".equals(insertException.getMessage()))) { - imageInsertListener.imageInsertAborted(image); - } else { - imageInsertListener.imageInsertFailed(image, insertException); - } + if ((insertException != null) && ("Cancelled by user".equals(insertException.getMessage()))) { + eventBus.post(new ImageInsertAbortedEvent(image)); + } else { + eventBus.post(new ImageInsertFailedEvent(image, insertException)); } } @@ -467,10 +498,9 @@ public class FreenetInterface { * {@inheritDoc} */ @Override + @SuppressWarnings("synthetic-access") public void onSuccess(BaseClientPutter clientPutter, ObjectContainer objectContainer) { - for (ImageInsertListener imageInsertListener : imageInsertListeners) { - imageInsertListener.imageInsertFinished(image, resultingUri); - } + eventBus.post(new ImageInsertFinishedEvent(image, resultingUri)); } } diff --git a/src/main/java/net/pterodactylus/sone/core/ImageInsertListener.java b/src/main/java/net/pterodactylus/sone/core/ImageInsertListener.java deleted file mode 100644 index ca1ab3e..0000000 --- a/src/main/java/net/pterodactylus/sone/core/ImageInsertListener.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Sone - ImageInsertListener.java - Copyright © 2011–2012 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.core; - -import java.util.EventListener; - -import net.pterodactylus.sone.core.FreenetInterface.InsertToken; -import net.pterodactylus.sone.data.Image; -import freenet.keys.FreenetURI; - -/** - * Listener interface for objects that want to be notified about the status of - * an image insert. - * - * @see ImageInserter#insertImage(net.pterodactylus.sone.data.TemporaryImage, - * Image) - * @see FreenetInterface#insertImage(net.pterodactylus.sone.data.TemporaryImage, - * Image, net.pterodactylus.sone.core.FreenetInterface.InsertToken) - * @see InsertToken - * @author David ‘Bombe’ Roden - */ -public interface ImageInsertListener extends EventListener { - - /** - * Notifies a listener that the insert of the given image started. - * - * @param image - * The image that is being inserted - */ - public void imageInsertStarted(Image image); - - /** - * Notifies a listener that the insert of the given image was aborted by the - * user. - * - * @param image - * The image that is no longer being inserted - */ - public void imageInsertAborted(Image image); - - /** - * Notifies a listener that the given image was inserted successfully. - * - * @param image - * The image that was inserted - * @param key - * The final key of the image - */ - public void imageInsertFinished(Image image, FreenetURI key); - - /** - * Notifies a listener that the given image could not be inserted. - * - * @param image - * The image that could not be inserted - * @param cause - * The cause of the insertion failure - */ - public void imageInsertFailed(Image image, Throwable cause); - -} diff --git a/src/main/java/net/pterodactylus/sone/core/ImageInserter.java b/src/main/java/net/pterodactylus/sone/core/ImageInserter.java index 4fc7e89..791663f 100644 --- a/src/main/java/net/pterodactylus/sone/core/ImageInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/ImageInserter.java @@ -1,5 +1,5 @@ /* - * Sone - ImageInserter.java - Copyright © 2011–2012 David Roden + * Sone - ImageInserter.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 @@ -17,6 +17,9 @@ package net.pterodactylus.sone.core; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -27,7 +30,6 @@ import net.pterodactylus.sone.core.FreenetInterface.InsertToken; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.util.logging.Logging; -import net.pterodactylus.util.validation.Validation; /** * The image inserter is responsible for inserting images using @@ -42,9 +44,6 @@ public class ImageInserter { /** The logger. */ private static final Logger logger = Logging.getLogger(ImageInserter.class); - /** The core. */ - private final Core core; - /** The freenet interface. */ private final FreenetInterface freenetInterface; @@ -54,19 +53,15 @@ public class ImageInserter { /** * Creates a new image inserter. * - * @param core - * The Sone core * @param freenetInterface * The freenet interface */ - public ImageInserter(Core core, FreenetInterface freenetInterface) { - this.core = core; + public ImageInserter(FreenetInterface freenetInterface) { this.freenetInterface = freenetInterface; } /** - * Inserts the given image. The {@link #core} will automatically added as - * {@link ImageInsertListener} to the created {@link InsertToken}. + * Inserts the given image. * * @param temporaryImage * The temporary image data @@ -74,11 +69,12 @@ public class ImageInserter { * The image */ public void insertImage(TemporaryImage temporaryImage, Image image) { - Validation.begin().isNotNull("Temporary Image", temporaryImage).isNotNull("Image", image).check().isEqual("Image IDs", image.getId(), temporaryImage.getId()).check(); + checkNotNull(temporaryImage, "temporaryImage must not be null"); + checkNotNull(image, "image must not be null"); + checkArgument(image.getId().equals(temporaryImage.getId()), "image IDs must match"); try { InsertToken insertToken = freenetInterface.new InsertToken(image); insertTokens.put(image.getId(), insertToken); - insertToken.addImageInsertListener(core); freenetInterface.insertImage(temporaryImage, image, insertToken); } catch (SoneException se1) { logger.log(Level.WARNING, "Could not insert image!", se1); @@ -98,7 +94,6 @@ public class ImageInserter { return; } insertToken.cancel(); - insertToken.removeImageInsertListener(core); } } diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java index 0ea895a..c8e3589 100644 --- a/src/main/java/net/pterodactylus/sone/core/Options.java +++ b/src/main/java/net/pterodactylus/sone/core/Options.java @@ -1,5 +1,5 @@ /* - * Sone - Options.java - Copyright © 2010–2012 David Roden + * Sone - Options.java - Copyright © 2010–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 @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import net.pterodactylus.util.validation.Validator; +import com.google.common.base.Predicate; /** * Stores various options that influence Sone’s behaviour. @@ -68,9 +68,8 @@ public class Options { * * @param value * The value to validate - * @return {@code true} if this option does not have a {@link Validator} - * , or the {@link Validator} validates this object, {@code - * false} otherwise + * @return {@code true} if this option does not have a validator, or the + * validator validates this object, {@code false} otherwise */ public boolean validate(T value); @@ -127,7 +126,7 @@ public class Options { private volatile T value; /** The validator. */ - private Validator validator; + private Predicate validator; /** The option watcher. */ private final OptionWatcher optionWatcher; @@ -150,7 +149,7 @@ public class Options { * @param validator * The validator for value validation (may be {@code null}) */ - public DefaultOption(T defaultValue, Validator validator) { + public DefaultOption(T defaultValue, Predicate validator) { this(defaultValue, validator, null); } @@ -176,7 +175,7 @@ public class Options { * @param optionWatcher * The option watcher (may be {@code null}) */ - public DefaultOption(T defaultValue, Validator validator, OptionWatcher optionWatcher) { + public DefaultOption(T defaultValue, Predicate validator, OptionWatcher optionWatcher) { this.defaultValue = defaultValue; this.validator = validator; this.optionWatcher = optionWatcher; @@ -214,7 +213,7 @@ public class Options { */ @Override public boolean validate(T value) { - return (validator == null) || (value == null) || validator.validate(value); + return (validator == null) || (value == null) || validator.apply(value); } /** @@ -222,7 +221,7 @@ public class Options { */ @Override public void set(T value) { - if ((value != null) && (validator != null) && (!validator.validate(value))) { + if ((value != null) && (validator != null) && (!validator.apply(value))) { throw new IllegalArgumentException("New Value (" + value + ") could not be validated."); } T oldValue = this.value; diff --git a/src/main/java/net/pterodactylus/sone/core/PostProvider.java b/src/main/java/net/pterodactylus/sone/core/PostProvider.java deleted file mode 100644 index 6e46326..0000000 --- a/src/main/java/net/pterodactylus/sone/core/PostProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Sone - PostProvider.java - Copyright © 2011–2012 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.core; - -import net.pterodactylus.sone.data.Post; - -/** - * Interface for objects that can provide {@link Post}s by their ID. - * - * @author David ‘Bombe’ Roden - */ -public interface PostProvider { - - /** - * Returns the post with the given ID, if it exists. If it does not exist - * and {@code create} is {@code false}, {@code null} is returned; otherwise, - * a new post with the given ID is created and returned. - * - * @param postId - * The ID of the post to return - * @param create - * {@code true} to create a new post if no post with the given ID - * exists, {@code false} to return {@code null} instead - * @return The post with the given ID, or {@code null} - */ - public Post getPost(String postId, boolean create); - -} diff --git a/src/main/java/net/pterodactylus/sone/core/Preferences.java b/src/main/java/net/pterodactylus/sone/core/Preferences.java new file mode 100644 index 0000000..16d9453 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/Preferences.java @@ -0,0 +1,375 @@ +/* + * Sone - Preferences.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.core; + +import net.pterodactylus.sone.fcp.FcpInterface; +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; + +/** + * Convenience interface for external classes that want to access the core’s + * configuration. + * + * @author David ‘Bombe’ Roden + */ +public class Preferences { + + /** The wrapped options. */ + private final Options options; + + /** + * Creates a new preferences object wrapped around the given options. + * + * @param options + * The options to wrap + */ + public Preferences(Options options) { + this.options = options; + } + + /** + * Returns the insertion delay. + * + * @return The insertion delay + */ + public int getInsertionDelay() { + return options.getIntegerOption("InsertionDelay").get(); + } + + /** + * Validates the given insertion delay. + * + * @param insertionDelay + * The insertion delay to validate + * @return {@code true} if the given insertion delay was valid, + * {@code false} otherwise + */ + public boolean validateInsertionDelay(Integer insertionDelay) { + return options.getIntegerOption("InsertionDelay").validate(insertionDelay); + } + + /** + * Sets the insertion delay + * + * @param insertionDelay + * The new insertion delay, or {@code null} to restore it to + * the default value + * @return This preferences + */ + public Preferences setInsertionDelay(Integer insertionDelay) { + options.getIntegerOption("InsertionDelay").set(insertionDelay); + return this; + } + + /** + * Returns the number of posts to show per page. + * + * @return The number of posts to show per page + */ + public int getPostsPerPage() { + return options.getIntegerOption("PostsPerPage").get(); + } + + /** + * Validates the number of posts per page. + * + * @param postsPerPage + * The number of posts per page + * @return {@code true} if the number of posts per page was valid, + * {@code false} otherwise + */ + public boolean validatePostsPerPage(Integer postsPerPage) { + return options.getIntegerOption("PostsPerPage").validate(postsPerPage); + } + + /** + * Sets the number of posts to show per page. + * + * @param postsPerPage + * The number of posts to show per page + * @return This preferences object + */ + public Preferences setPostsPerPage(Integer postsPerPage) { + options.getIntegerOption("PostsPerPage").set(postsPerPage); + return this; + } + + /** + * Returns the number of images to show per page. + * + * @return The number of images to show per page + */ + public int getImagesPerPage() { + return options.getIntegerOption("ImagesPerPage").get(); + } + + /** + * Validates the number of images per page. + * + * @param imagesPerPage + * The number of images per page + * @return {@code true} if the number of images per page was valid, + * {@code false} otherwise + */ + public boolean validateImagesPerPage(Integer imagesPerPage) { + return options.getIntegerOption("ImagesPerPage").validate(imagesPerPage); + } + + /** + * Sets the number of images per page. + * + * @param imagesPerPage + * The number of images per page + * @return This preferences object + */ + public Preferences setImagesPerPage(Integer imagesPerPage) { + options.getIntegerOption("ImagesPerPage").set(imagesPerPage); + return this; + } + + /** + * Returns the number of characters per post, or -1 if the + * posts should not be cut off. + * + * @return The numbers of characters per post + */ + public int getCharactersPerPost() { + return options.getIntegerOption("CharactersPerPost").get(); + } + + /** + * Validates the number of characters per post. + * + * @param charactersPerPost + * The number of characters per post + * @return {@code true} if the number of characters per post was valid, + * {@code false} otherwise + */ + public boolean validateCharactersPerPost(Integer charactersPerPost) { + return options.getIntegerOption("CharactersPerPost").validate(charactersPerPost); + } + + /** + * Sets the number of characters per post. + * + * @param charactersPerPost + * The number of characters per post, or -1 to + * not cut off the posts + * @return This preferences objects + */ + public Preferences setCharactersPerPost(Integer charactersPerPost) { + options.getIntegerOption("CharactersPerPost").set(charactersPerPost); + return this; + } + + /** + * Returns the number of characters the shortened post should have. + * + * @return The number of characters of the snippet + */ + public int getPostCutOffLength() { + return options.getIntegerOption("PostCutOffLength").get(); + } + + /** + * Validates the number of characters after which to cut off the post. + * + * @param postCutOffLength + * The number of characters of the snippet + * @return {@code true} if the number of characters of the snippet is + * valid, {@code false} otherwise + */ + public boolean validatePostCutOffLength(Integer postCutOffLength) { + return options.getIntegerOption("PostCutOffLength").validate(postCutOffLength); + } + + /** + * Sets the number of characters the shortened post should have. + * + * @param postCutOffLength + * The number of characters of the snippet + * @return This preferences + */ + public Preferences setPostCutOffLength(Integer postCutOffLength) { + options.getIntegerOption("PostCutOffLength").set(postCutOffLength); + return this; + } + + /** + * Returns whether Sone requires full access to be even visible. + * + * @return {@code true} if Sone requires full access, {@code false} + * otherwise + */ + public boolean isRequireFullAccess() { + return options.getBooleanOption("RequireFullAccess").get(); + } + + /** + * Sets whether Sone requires full access to be even visible. + * + * @param requireFullAccess + * {@code true} if Sone requires full access, {@code false} + * otherwise + */ + public void setRequireFullAccess(Boolean requireFullAccess) { + options.getBooleanOption("RequireFullAccess").set(requireFullAccess); + } + + /** + * Returns the positive trust. + * + * @return The positive trust + */ + public int getPositiveTrust() { + return options.getIntegerOption("PositiveTrust").get(); + } + + /** + * Validates the positive trust. + * + * @param positiveTrust + * The positive trust to validate + * @return {@code true} if the positive trust was valid, {@code false} + * otherwise + */ + public boolean validatePositiveTrust(Integer positiveTrust) { + return options.getIntegerOption("PositiveTrust").validate(positiveTrust); + } + + /** + * Sets the positive trust. + * + * @param positiveTrust + * The new positive trust, or {@code null} to restore it to + * the default vlaue + * @return This preferences + */ + public Preferences setPositiveTrust(Integer positiveTrust) { + options.getIntegerOption("PositiveTrust").set(positiveTrust); + return this; + } + + /** + * Returns the negative trust. + * + * @return The negative trust + */ + public int getNegativeTrust() { + return options.getIntegerOption("NegativeTrust").get(); + } + + /** + * Validates the negative trust. + * + * @param negativeTrust + * The negative trust to validate + * @return {@code true} if the negative trust was valid, {@code false} + * otherwise + */ + public boolean validateNegativeTrust(Integer negativeTrust) { + return options.getIntegerOption("NegativeTrust").validate(negativeTrust); + } + + /** + * Sets the negative trust. + * + * @param negativeTrust + * The negative trust, or {@code null} to restore it to the + * default value + * @return The preferences + */ + public Preferences setNegativeTrust(Integer negativeTrust) { + options.getIntegerOption("NegativeTrust").set(negativeTrust); + return this; + } + + /** + * Returns the trust comment. This is the comment that is set in the web + * of trust when a trust value is assigned to an identity. + * + * @return The trust comment + */ + public String getTrustComment() { + return options.getStringOption("TrustComment").get(); + } + + /** + * Sets the trust comment. + * + * @param trustComment + * The trust comment, or {@code null} to restore it to the + * default value + * @return This preferences + */ + public Preferences setTrustComment(String trustComment) { + options.getStringOption("TrustComment").set(trustComment); + return this; + } + + /** + * Returns whether the {@link FcpInterface FCP interface} is currently + * active. + * + * @see FcpInterface#setActive(boolean) + * @return {@code true} if the FCP interface is currently active, + * {@code false} otherwise + */ + public boolean isFcpInterfaceActive() { + return options.getBooleanOption("ActivateFcpInterface").get(); + } + + /** + * Sets whether the {@link FcpInterface FCP interface} is currently + * active. + * + * @see FcpInterface#setActive(boolean) + * @param fcpInterfaceActive + * {@code true} to activate the FCP interface, {@code false} + * to deactivate the FCP interface + * @return This preferences object + */ + public Preferences setFcpInterfaceActive(boolean fcpInterfaceActive) { + options.getBooleanOption("ActivateFcpInterface").set(fcpInterfaceActive); + return this; + } + + /** + * Returns the action level for which full access to the FCP interface + * is required. + * + * @return The action level for which full access to the FCP interface + * is required + */ + public FullAccessRequired getFcpFullAccessRequired() { + return FullAccessRequired.values()[options.getIntegerOption("FcpFullAccessRequired").get()]; + } + + /** + * Sets the action level for which full access to the FCP interface is + * required + * + * @param fcpFullAccessRequired + * The action level + * @return This preferences + */ + public Preferences setFcpFullAccessRequired(FullAccessRequired fcpFullAccessRequired) { + options.getIntegerOption("FcpFullAccessRequired").set((fcpFullAccessRequired != null) ? fcpFullAccessRequired.ordinal() : null); + return this; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java index 72ff008..e0ee363 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java @@ -1,5 +1,5 @@ /* - * Sone - SoneDownloader.java - Copyright © 2010–2012 David Roden + * Sone - SoneDownloader.java - Copyright © 2010–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 @@ -26,6 +26,7 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.core.FreenetInterface.Fetched; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Image; @@ -34,7 +35,8 @@ import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; -import net.pterodactylus.util.collection.Pair; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.PostReplyBuilder; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; @@ -95,10 +97,9 @@ public class SoneDownloader extends AbstractService { * The Sone to add */ public void addSone(Sone sone) { - if (!sones.add(sone)) { - freenetInterface.unregisterUsk(sone); + if (sones.add(sone)) { + freenetInterface.registerUsk(sone, this); } - freenetInterface.registerUsk(sone, this); } /** @@ -155,15 +156,16 @@ public class SoneDownloader extends AbstractService { FreenetURI requestUri = soneUri.setMetaString(new String[] { "sone.xml" }); sone.setStatus(SoneStatus.downloading); try { - Pair fetchResults = freenetInterface.fetchUri(requestUri); + Fetched fetchResults = freenetInterface.fetchUri(requestUri); if (fetchResults == null) { /* TODO - mark Sone as bad. */ return null; } - logger.log(Level.FINEST, String.format("Got %d bytes back.", fetchResults.getRight().size())); - Sone parsedSone = parseSone(sone, fetchResults.getRight(), fetchResults.getLeft()); + logger.log(Level.FINEST, String.format("Got %d bytes back.", fetchResults.getFetchResult().size())); + Sone parsedSone = parseSone(sone, fetchResults.getFetchResult(), fetchResults.getFreenetUri()); if (parsedSone != null) { if (!fetchOnly) { + parsedSone.setStatus((parsedSone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle); core.updateSone(parsedSone); addSone(parsedSone); } @@ -236,7 +238,7 @@ public class SoneDownloader extends AbstractService { return null; } - Sone sone = new Sone(originalSone.getId()).setIdentity(originalSone.getIdentity()); + Sone sone = new Sone(originalSone.getId(), false).setIdentity(originalSone.getIdentity()); SimpleXML soneXml; try { @@ -372,11 +374,13 @@ public class SoneDownloader extends AbstractService { return null; } try { - Post post = core.getPost(postId).setSone(sone).setTime(Long.parseLong(postTime)).setText(postText); + PostBuilder postBuilder = core.postBuilder(); + /* TODO - parse time correctly. */ + postBuilder.withId(postId).from(sone.getId()).withTime(Long.parseLong(postTime)).withText(postText); if ((postRecipientId != null) && (postRecipientId.length() == 43)) { - post.setRecipient(core.getSone(postRecipientId)); + postBuilder.to(postRecipientId); } - posts.add(post); + posts.add(postBuilder.build()); } catch (NumberFormatException nfe1) { /* TODO - mark Sone as bad. */ logger.log(Level.WARNING, String.format("Downloaded post for Sone %s with invalid time: %s", sone, postTime)); @@ -403,7 +407,10 @@ public class SoneDownloader extends AbstractService { return null; } try { - replies.add(core.getReply(replyId).setSone(sone).setPost(core.getPost(replyPostId)).setTime(Long.parseLong(replyTime)).setText(replyText)); + PostReplyBuilder postReplyBuilder = core.postReplyBuilder(); + /* TODO - parse time correctly. */ + postReplyBuilder.withId(replyId).from(sone.getId()).to(replyPostId).withTime(Long.parseLong(replyTime)).withText(replyText); + replies.add(postReplyBuilder.build()); } catch (NumberFormatException nfe1) { /* TODO - mark Sone as bad. */ logger.log(Level.WARNING, String.format("Downloaded reply for Sone %s with invalid time: %s", sone, replyTime)); diff --git a/src/main/java/net/pterodactylus/sone/core/SoneException.java b/src/main/java/net/pterodactylus/sone/core/SoneException.java index 323fd66..f1af354 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneException.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneException.java @@ -1,5 +1,5 @@ /* - * Sone - SoneException.java - Copyright © 2010–2012 David Roden + * Sone - SoneException.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java b/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java index 5cb8669..5c3f7cd 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java @@ -1,5 +1,5 @@ /* - * Sone - SoneInsertException.java - Copyright © 2011–2012 David Roden + * Sone - SoneInsertException.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 diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInsertListener.java b/src/main/java/net/pterodactylus/sone/core/SoneInsertListener.java deleted file mode 100644 index 6f99fe6..0000000 --- a/src/main/java/net/pterodactylus/sone/core/SoneInsertListener.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Sone - SoneInsertListener.java - Copyright © 2011–2012 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.core; - -import java.util.EventListener; - -import net.pterodactylus.sone.data.Sone; - -/** - * Listener for Sone insert events. - * - * @author David ‘Bombe’ Roden - */ -public interface SoneInsertListener extends EventListener { - - /** - * Notifies a listener that a Sone is now being inserted. - * - * @param sone - * The Sone being inserted - */ - public void insertStarted(Sone sone); - - /** - * Notifies a listener that a Sone has been successfully inserted. - * - * @param sone - * The Sone that was inserted - * @param insertDuration - * The duration of the insert (in milliseconds) - */ - public void insertFinished(Sone sone, long insertDuration); - - /** - * Notifies a listener that the insert of the given Sone was aborted. - * - * @param sone - * The Sone that was being inserted - * @param cause - * The cause of the abortion (may be {@code null}) - */ - public void insertAborted(Sone sone, Throwable cause); - -} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInsertListenerManager.java b/src/main/java/net/pterodactylus/sone/core/SoneInsertListenerManager.java deleted file mode 100644 index 6841e7f..0000000 --- a/src/main/java/net/pterodactylus/sone/core/SoneInsertListenerManager.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Sone - SoneInsertListenerManager.java - Copyright © 2011–2012 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.core; - -import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.util.event.AbstractListenerManager; - -/** - * Manager for {@link SoneInsertListener}s. - * - * @author David ‘Bombe’ Roden - */ -public class SoneInsertListenerManager extends AbstractListenerManager { - - /** - * Creates a new Sone insert listener manager. - * - * @param sone - * The sone being inserted - */ - public SoneInsertListenerManager(Sone sone) { - super(sone); - } - - // - // ACTIONS - // - - /** - * Notifies all listeners that the insert of the Sone has started. - * - * @see SoneInsertListener#insertStarted(Sone) - */ - void fireInsertStarted() { - for (SoneInsertListener soneInsertListener : getListeners()) { - soneInsertListener.insertStarted(getSource()); - } - } - - /** - * Notifies all listeners that the insert of the Sone has finished - * successfully. - * - * @see SoneInsertListener#insertFinished(Sone, long) - * @param insertDuration - * The insert duration (in milliseconds) - */ - void fireInsertFinished(long insertDuration) { - for (SoneInsertListener soneInsertListener : getListeners()) { - soneInsertListener.insertFinished(getSource(), insertDuration); - } - } - - /** - * Notifies all listeners that the insert of the Sone was aborted. - * - * @see SoneInsertListener#insertAborted(Sone, Throwable) - * @param cause - * The cause of the abortion (may be {@code null} - */ - void fireInsertAborted(Throwable cause) { - for (SoneInsertListener soneInsertListener : getListeners()) { - soneInsertListener.insertAborted(getSource(), cause); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 534985a..8efcc73 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -1,5 +1,5 @@ /* - * Sone - SoneInserter.java - Copyright © 2010–2012 David Roden + * Sone - SoneInserter.java - Copyright © 2010–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 @@ -20,22 +20,22 @@ package net.pterodactylus.sone.core; import java.io.InputStreamReader; import java.io.StringWriter; import java.nio.charset.Charset; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent; +import net.pterodactylus.sone.core.event.SoneInsertedEvent; +import net.pterodactylus.sone.core.event.SoneInsertingEvent; +import net.pterodactylus.sone.data.Album; 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.Sone.SoneStatus; import net.pterodactylus.sone.freenet.StringBucket; import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.util.collection.ListBuilder; -import net.pterodactylus.util.collection.ReverseComparator; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.service.AbstractService; @@ -47,6 +47,11 @@ import net.pterodactylus.util.template.TemplateContextFactory; import net.pterodactylus.util.template.TemplateException; import net.pterodactylus.util.template.TemplateParser; import net.pterodactylus.util.template.XmlFilter; + +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Ordering; +import com.google.common.eventbus.EventBus; + import freenet.client.async.ManifestElement; import freenet.keys.FreenetURI; @@ -78,15 +83,15 @@ public class SoneInserter extends AbstractService { /** The core. */ private final Core core; + /** The event bus. */ + private final EventBus eventBus; + /** The Freenet interface. */ private final FreenetInterface freenetInterface; /** The Sone to insert. */ private final Sone sone; - /** The insert listener manager. */ - private SoneInsertListenerManager soneInsertListenerManager; - /** Whether a modification has been detected. */ private volatile boolean modified = false; @@ -98,41 +103,19 @@ public class SoneInserter extends AbstractService { * * @param core * The core + * @param eventBus + * The event bus * @param freenetInterface * The freenet interface * @param sone * The Sone to insert */ - public SoneInserter(Core core, FreenetInterface freenetInterface, Sone sone) { + public SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, Sone sone) { super("Sone Inserter for “" + sone.getName() + "”", false); this.core = core; + this.eventBus = eventBus; this.freenetInterface = freenetInterface; this.sone = sone; - this.soneInsertListenerManager = new SoneInsertListenerManager(sone); - } - - // - // LISTENER MANAGEMENT - // - - /** - * Adds a listener for Sone insert events. - * - * @param soneInsertListener - * The Sone insert listener - */ - public void addSoneInsertListener(SoneInsertListener soneInsertListener) { - soneInsertListenerManager.addListener(soneInsertListener); - } - - /** - * Removes a listener for Sone insert events. - * - * @param soneInsertListener - * The Sone insert listener - */ - public void removeSoneInsertListener(SoneInsertListener soneInsertListener) { - soneInsertListenerManager.removeListener(soneInsertListener); } // @@ -192,89 +175,91 @@ public class SoneInserter extends AbstractService { long lastModificationTime = 0; String lastInsertedFingerprint = lastInsertFingerprint; String lastFingerprint = ""; - while (!shouldStop()) { try { - /* check every seconds. */ - sleep(1000); + while (!shouldStop()) { + try { + /* check every seconds. */ + sleep(1000); - /* don’t insert locked Sones. */ - if (core.isLocked(sone)) { - /* trigger redetection when the Sone is unlocked. */ - synchronized (sone) { - modified = !sone.getFingerprint().equals(lastInsertedFingerprint); + /* don’t insert locked Sones. */ + if (core.isLocked(sone)) { + /* trigger redetection when the Sone is unlocked. */ + synchronized (sone) { + modified = !sone.getFingerprint().equals(lastInsertedFingerprint); + } + lastFingerprint = ""; + lastModificationTime = 0; + continue; } - lastFingerprint = ""; - lastModificationTime = 0; - continue; - } - InsertInformation insertInformation = null; - synchronized (sone) { - String fingerprint = sone.getFingerprint(); - if (!fingerprint.equals(lastFingerprint)) { - if (fingerprint.equals(lastInsertedFingerprint)) { - modified = false; - lastModificationTime = 0; - logger.log(Level.FINE, String.format("Sone %s has been reverted to last insert state.", sone)); - } else { - lastModificationTime = System.currentTimeMillis(); - modified = true; - logger.log(Level.FINE, String.format("Sone %s has been modified, waiting %d seconds before inserting.", sone.getName(), insertionDelay)); + InsertInformation insertInformation = null; + synchronized (sone) { + String fingerprint = sone.getFingerprint(); + if (!fingerprint.equals(lastFingerprint)) { + if (fingerprint.equals(lastInsertedFingerprint)) { + modified = false; + lastModificationTime = 0; + logger.log(Level.FINE, String.format("Sone %s has been reverted to last insert state.", sone)); + } else { + lastModificationTime = System.currentTimeMillis(); + modified = true; + logger.log(Level.FINE, String.format("Sone %s has been modified, waiting %d seconds before inserting.", sone.getName(), insertionDelay)); + } + lastFingerprint = fingerprint; + } + if (modified && (lastModificationTime > 0) && ((System.currentTimeMillis() - lastModificationTime) > (insertionDelay * 1000))) { + lastInsertedFingerprint = fingerprint; + insertInformation = new InsertInformation(sone); } - lastFingerprint = fingerprint; - } - if (modified && (lastModificationTime > 0) && ((System.currentTimeMillis() - lastModificationTime) > (insertionDelay * 1000))) { - lastInsertedFingerprint = fingerprint; - insertInformation = new InsertInformation(sone); } - } - if (insertInformation != null) { - logger.log(Level.INFO, String.format("Inserting Sone “%s”…", sone.getName())); - - boolean success = false; - try { - sone.setStatus(SoneStatus.inserting); - long insertTime = System.currentTimeMillis(); - insertInformation.setTime(insertTime); - soneInsertListenerManager.fireInsertStarted(); - FreenetURI finalUri = freenetInterface.insertDirectory(insertInformation.getInsertUri(), insertInformation.generateManifestEntries(), "index.html"); - soneInsertListenerManager.fireInsertFinished(System.currentTimeMillis() - insertTime); - /* at this point we might already be stopped. */ - if (shouldStop()) { - /* if so, bail out, don’t change anything. */ - break; + if (insertInformation != null) { + logger.log(Level.INFO, String.format("Inserting Sone “%s”…", sone.getName())); + + boolean success = false; + try { + sone.setStatus(SoneStatus.inserting); + long insertTime = System.currentTimeMillis(); + insertInformation.setTime(insertTime); + eventBus.post(new SoneInsertingEvent(sone)); + FreenetURI finalUri = freenetInterface.insertDirectory(insertInformation.getInsertUri(), insertInformation.generateManifestEntries(), "index.html"); + eventBus.post(new SoneInsertedEvent(sone, System.currentTimeMillis() - insertTime)); + /* at this point we might already be stopped. */ + if (shouldStop()) { + /* if so, bail out, don’t change anything. */ + break; + } + sone.setTime(insertTime); + sone.setLatestEdition(finalUri.getEdition()); + core.touchConfiguration(); + success = true; + logger.log(Level.INFO, String.format("Inserted Sone “%s” at %s.", sone.getName(), finalUri)); + } catch (SoneException se1) { + eventBus.post(new SoneInsertAbortedEvent(sone, se1)); + logger.log(Level.WARNING, String.format("Could not insert Sone “%s”!", sone.getName()), se1); + } finally { + sone.setStatus(SoneStatus.idle); } - sone.setTime(insertTime); - sone.setLatestEdition(finalUri.getEdition()); - core.touchConfiguration(); - success = true; - logger.log(Level.INFO, String.format("Inserted Sone “%s” at %s.", sone.getName(), finalUri)); - } catch (SoneException se1) { - soneInsertListenerManager.fireInsertAborted(se1); - logger.log(Level.WARNING, String.format("Could not insert Sone “%s”!", sone.getName()), se1); - } finally { - sone.setStatus(SoneStatus.idle); - } - /* - * reset modification counter if Sone has not been modified - * while it was inserted. - */ - if (success) { - synchronized (sone) { - if (lastInsertedFingerprint.equals(sone.getFingerprint())) { - logger.log(Level.FINE, String.format("Sone “%s” was not modified further, resetting counter…", sone)); - lastModificationTime = 0; - lastInsertFingerprint = lastInsertedFingerprint; - core.touchConfiguration(); - modified = false; + /* + * reset modification counter if Sone has not been modified + * while it was inserted. + */ + if (success) { + synchronized (sone) { + if (lastInsertedFingerprint.equals(sone.getFingerprint())) { + logger.log(Level.FINE, String.format("Sone “%s” was not modified further, resetting counter…", sone)); + lastModificationTime = 0; + lastInsertFingerprint = lastInsertedFingerprint; + core.touchConfiguration(); + modified = false; + } } } } + } catch (Throwable t1) { + logger.log(Level.SEVERE, "SoneInserter threw an Exception!", t1); } - } catch (Throwable t1) { - logger.log(Level.SEVERE, "SoneInserter threw an Exception!", t1); - }} + } } /** @@ -302,11 +287,11 @@ public class SoneInserter extends AbstractService { soneProperties.put("requestUri", sone.getRequestUri()); soneProperties.put("insertUri", sone.getInsertUri()); soneProperties.put("profile", sone.getProfile()); - soneProperties.put("posts", new ListBuilder(new ArrayList(sone.getPosts())).sort(Post.TIME_COMPARATOR).get()); - soneProperties.put("replies", new ListBuilder(new ArrayList(sone.getReplies())).sort(new ReverseComparator>(Reply.TIME_COMPARATOR)).get()); + soneProperties.put("posts", Ordering.from(Post.TIME_COMPARATOR).sortedCopy(sone.getPosts())); + soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet(sone.getLikedReplyIds())); - soneProperties.put("albums", sone.getAllAlbums()); + soneProperties.put("albums", FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList()); } // diff --git a/src/main/java/net/pterodactylus/sone/core/SoneProvider.java b/src/main/java/net/pterodactylus/sone/core/SoneProvider.java deleted file mode 100644 index 25f4d8b..0000000 --- a/src/main/java/net/pterodactylus/sone/core/SoneProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Sone - SoneProvider.java - Copyright © 2011–2012 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.core; - -import net.pterodactylus.sone.data.Sone; - -/** - * Interface for objects that can provide {@link Sone}s by their ID. - * - * @author David ‘Bombe’ Roden - */ -public interface SoneProvider { - - /** - * Returns the Sone with the given ID, if it exists. If it does not exist - * and {@code create} is {@code false}, {@code null} is returned; otherwise, - * a new Sone with the given ID is created and returned. - * - * @param soneId - * The ID of the Sone to return - * @param create - * {@code true} to create a new Sone if no Sone with the given ID - * exists, {@code false} to return {@code null} instead - * @return The Sone with the given ID, or {@code null} - */ - public Sone getSone(String soneId, boolean create); - -} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java b/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java index 0c1e64e..ed3d819 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java @@ -1,5 +1,5 @@ /* - * Sone - SoneRescuer.java - Copyright © 2011–2012 David Roden + * Sone - SoneRescuer.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 diff --git a/src/main/java/net/pterodactylus/sone/core/SoneUri.java b/src/main/java/net/pterodactylus/sone/core/SoneUri.java new file mode 100644 index 0000000..6c5bf16 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/SoneUri.java @@ -0,0 +1,55 @@ +/* + * Sone - SoneUri.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.core; + +import java.net.MalformedURLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.pterodactylus.util.logging.Logging; +import freenet.keys.FreenetURI; + +/** + * Helper class that creates {@link FreenetURI}s for Sone to insert to and + * request from. + * + * @author David ‘Bombe’ Roden + */ +public class SoneUri { + + /** The logger. */ + private static final Logger logger = Logging.getLogger(SoneUri.class); + + /** + * Generate a Sone URI from the given URI. + * + * @param uri + * The URI to derive the Sone URI from + * @return The derived URI + */ + public static FreenetURI create(String uri) { + try { + return new FreenetURI(uri).setDocName("Sone").setMetaString(new String[0]); + } catch (MalformedURLException mue1) { + /* this should never happen. */ + logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uri), mue1); + return null; + } + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java b/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java index 3f4d235..b1248ab 100644 --- a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java +++ b/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java @@ -1,5 +1,5 @@ /* - * Sone - UpdateChecker.java - Copyright © 2011–2012 David Roden + * Sone - UpdateChecker.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 @@ -26,12 +26,16 @@ import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.core.FreenetInterface.Fetched; +import net.pterodactylus.sone.core.event.UpdateFoundEvent; import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.util.collection.Pair; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.version.Version; -import freenet.client.FetchResult; + +import com.google.common.eventbus.EventBus; +import com.google.inject.Inject; + import freenet.keys.FreenetURI; import freenet.support.api.Bucket; @@ -49,14 +53,14 @@ public class UpdateChecker { private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/"; /** The current latest known edition. */ - private static final int LATEST_EDITION = 55; + private static final int LATEST_EDITION = 56; + + /** The event bus. */ + private final EventBus eventBus; /** The Freenet interface. */ private final FreenetInterface freenetInterface; - /** The update listener manager. */ - private final UpdateListenerManager updateListenerManager = new UpdateListenerManager(); - /** The current URI of the homepage. */ private FreenetURI currentUri; @@ -72,38 +76,18 @@ public class UpdateChecker { /** * Creates a new update checker. * + * @param eventBus + * The event bus * @param freenetInterface * The freenet interface to use */ - public UpdateChecker(FreenetInterface freenetInterface) { + @Inject + public UpdateChecker(EventBus eventBus, FreenetInterface freenetInterface) { + this.eventBus = eventBus; this.freenetInterface = freenetInterface; } // - // EVENT LISTENER MANAGEMENT - // - - /** - * Adds the given listener to the list of registered listeners. - * - * @param updateListener - * The listener to add - */ - public void addUpdateListener(UpdateListener updateListener) { - updateListenerManager.addListener(updateListener); - } - - /** - * Removes the given listener from the list of registered listeners. - * - * @param updateListener - * The listener to remove - */ - public void removeUpdateListener(UpdateListener updateListener) { - updateListenerManager.removeListener(updateListener); - } - - // // ACCESSORS // @@ -168,12 +152,12 @@ public class UpdateChecker { public void editionFound(FreenetURI uri, long edition, boolean newKnownGood, boolean newSlot) { logger.log(Level.FINEST, String.format("Found update for %s: %d, %s, %s", uri, edition, newKnownGood, newSlot)); if (newKnownGood || newSlot) { - Pair uriResult = freenetInterface.fetchUri(uri.setMetaString(new String[] { "sone.properties" })); + Fetched uriResult = freenetInterface.fetchUri(uri.setMetaString(new String[] { "sone.properties" })); if (uriResult == null) { logger.log(Level.WARNING, String.format("Could not fetch properties of latest homepage: %s", uri)); return; } - Bucket resultBucket = uriResult.getRight().asBucket(); + Bucket resultBucket = uriResult.getFetchResult().asBucket(); try { parseProperties(resultBucket.getInputStream(), edition); latestEdition = edition; @@ -202,8 +186,7 @@ public class UpdateChecker { * Parses the properties of the latest version and fires events, if * necessary. * - * @see UpdateListener#updateFound(Version, long, long) - * @see UpdateListenerManager#fireUpdateFound(Version, long, long) + * @see UpdateFoundEvent * @param propertiesInputStream * The input stream to parse * @param edition @@ -241,7 +224,7 @@ public class UpdateChecker { currentLatestVersion = version; latestVersionDate = releaseTime; logger.log(Level.INFO, String.format("Found new version: %s (%tc)", version, new Date(releaseTime))); - updateListenerManager.fireUpdateFound(version, releaseTime, edition); + eventBus.post(new UpdateFoundEvent(version, releaseTime, edition)); } } diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateListener.java b/src/main/java/net/pterodactylus/sone/core/UpdateListener.java deleted file mode 100644 index 2d52f16..0000000 --- a/src/main/java/net/pterodactylus/sone/core/UpdateListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Sone - UpdateListener.java - Copyright © 2011–2012 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.core; - -import java.util.EventListener; - -import net.pterodactylus.util.version.Version; - -/** - * Listener interface for {@link UpdateChecker} events. - * - * @author David ‘Bombe’ Roden - */ -public interface UpdateListener extends EventListener { - - /** - * Notifies a listener that a newer version than the current version was - * found. - * - * @param version - * The version that was found - * @param releaseTime - * The release time of the version - * @param latestEdition - * The latest edition of the Sone homepage - */ - public void updateFound(Version version, long releaseTime, long latestEdition); - -} diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateListenerManager.java b/src/main/java/net/pterodactylus/sone/core/UpdateListenerManager.java deleted file mode 100644 index 31fdbed..0000000 --- a/src/main/java/net/pterodactylus/sone/core/UpdateListenerManager.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Sone - UpdateListenerManager.java - Copyright © 2011–2012 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.core; - -import net.pterodactylus.util.event.AbstractListenerManager; -import net.pterodactylus.util.version.Version; - -/** - * Listener manager for {@link UpdateListener} events. - * - * @author David ‘Bombe’ Roden - */ -public class UpdateListenerManager extends AbstractListenerManager { - - /** - * Creates a new update listener manager. - */ - public UpdateListenerManager() { - super(null); - } - - // - // ACTIONS - // - - /** - * Notifies all listeners that a new version has been found. - * - * @param version - * The new version - * @param releaseTime - * The release time of the new version - * @param latestEdition - * The latest edition of the Sone homepage - */ - void fireUpdateFound(Version version, long releaseTime, long latestEdition) { - for (UpdateListener updateListener : getListeners()) { - updateListener.updateFound(version, releaseTime, latestEdition); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java index bdfc1ae..d449835 100644 --- a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java +++ b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java @@ -1,5 +1,5 @@ /* - * Sone - WebOfTrustUpdater.java - Copyright © 2012 David Roden + * Sone - WebOfTrustUpdater.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.core; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; @@ -31,7 +33,8 @@ import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector; import net.pterodactylus.sone.freenet.wot.WebOfTrustException; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.service.AbstractService; -import net.pterodactylus.util.validation.Validation; + +import com.google.inject.Inject; /** * Updates WebOfTrust identity data in a background thread because communicating @@ -60,6 +63,7 @@ public class WebOfTrustUpdater extends AbstractService { * @param webOfTrustConnector * The web of trust connector */ + @Inject public WebOfTrustUpdater(WebOfTrustConnector webOfTrustConnector) { super("Trust Updater"); this.webOfTrustConnector = webOfTrustConnector; @@ -290,7 +294,6 @@ public class WebOfTrustUpdater extends AbstractService { * * @return {@code true} if this job finished successfully, {@code false} * otherwise - * * @see WebOfTrustUpdater#stop() */ @SuppressWarnings("synthetic-access") @@ -471,9 +474,8 @@ public class WebOfTrustUpdater extends AbstractService { */ @SuppressWarnings("synthetic-access") public WebOfTrustContextUpdateJob(OwnIdentity ownIdentity, String context) { - Validation.begin().isNotNull("OwnIdentity", ownIdentity).isNotNull("Context", context).check(); - this.ownIdentity = ownIdentity; - this.context = context; + this.ownIdentity = checkNotNull(ownIdentity, "ownIdentity must not be null"); + this.context = checkNotNull(context, "context must not be null"); } // diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageEvent.java new file mode 100644 index 0000000..2af6408 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/ImageEvent.java @@ -0,0 +1,55 @@ +/* + * Sone - ImageEvent.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.core.event; + +import net.pterodactylus.sone.data.Image; + +/** + * Base class for {@link Image} events. + * + * @author David ‘Bombe’ Roden + */ +public abstract class ImageEvent { + + /** The image this event is about. */ + private final Image image; + + /** + * Creates a new image event. + * + * @param image + * The image this event is about + */ + protected ImageEvent(Image image) { + this.image = image; + } + + // + // ACCESSORS + // + + /** + * Returns the image this event is about. + * + * @return The image this event is about + */ + public Image image() { + return image; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.java new file mode 100644 index 0000000..2431cf6 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - ImageInsertAbortedEvent.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.core.event; + +import net.pterodactylus.sone.data.Image; + +/** + * Event that signals that an {@link Image} insert is aborted. + * + * @author David ‘Bombe’ Roden + */ +public class ImageInsertAbortedEvent extends ImageEvent { + + /** + * Creates a new “image insert aborted” event. + * + * @param image + * The image whose insert aborted + */ + public ImageInsertAbortedEvent(Image image) { + super(image); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.java new file mode 100644 index 0000000..7800537 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.java @@ -0,0 +1,58 @@ +/* + * Sone - ImageInsertFailedEvent.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.core.event; + +import net.pterodactylus.sone.data.Image; + +/** + * Event that signals that an {@link Image} insert has failed. + * + * @author David ‘Bombe’ Roden + */ +public class ImageInsertFailedEvent extends ImageEvent { + + /** The cause of the insert failure. */ + private final Throwable cause; + + /** + * Creates a new “image insert failed” event. + * + * @param image + * The image whose insert failed + * @param cause + * The cause of the insert failure + */ + public ImageInsertFailedEvent(Image image, Throwable cause) { + super(image); + this.cause = cause; + } + + // + // ACCESSORS + // + + /** + * Returns the cause of the insert failure. + * + * @return The cause of the insert failure + */ + public Throwable cause() { + return cause; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.java new file mode 100644 index 0000000..53023b0 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.java @@ -0,0 +1,59 @@ +/* + * Sone - ImageInsertFinishedEvent.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.core.event; + +import net.pterodactylus.sone.data.Image; +import freenet.keys.FreenetURI; + +/** + * Event that signals that an {@link Image} insert is finished. + * + * @author David ‘Bombe’ Roden + */ +public class ImageInsertFinishedEvent extends ImageEvent { + + /** The URI of the image. */ + private final FreenetURI resultingUri; + + /** + * Creates a new “image insert finished” event. + * + * @param image + * The image whose insert finished + * @param resultingUri + * The resulting URI of the image + */ + public ImageInsertFinishedEvent(Image image, FreenetURI resultingUri) { + super(image); + this.resultingUri = resultingUri; + } + + // + // ACCESSORS + // + + /** + * Returns the URI of the image. + * + * @return The URI of the image + */ + public FreenetURI resultingUri() { + return resultingUri; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.java new file mode 100644 index 0000000..3b60277 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - ImageInsertStartedEvent.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.core.event; + +import net.pterodactylus.sone.data.Image; + +/** + * Event that signals that an {@link Image} is not being inserted. + * + * @author David ‘Bombe’ Roden + */ +public class ImageInsertStartedEvent extends ImageEvent { + + /** + * Creates a new “image is inserted” event. + * + * @param image + * The image that is being inserted + */ + public ImageInsertStartedEvent(Image image) { + super(image); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/MarkPostKnownEvent.java b/src/main/java/net/pterodactylus/sone/core/event/MarkPostKnownEvent.java new file mode 100644 index 0000000..b8faac9 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/MarkPostKnownEvent.java @@ -0,0 +1,40 @@ +/* + * Sone - MarkPostKnownEvent.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.core.event; + +import net.pterodactylus.sone.data.Post; + +/** + * Event that signals that a {@link Post} has been marked as + * {@link Post#isKnown() known}. + * + * @author David ‘Bombe’ Roden + */ +public class MarkPostKnownEvent extends PostEvent { + + /** + * Creates a new “post marked known” event. + * + * @param post + * The post that was marked as known + */ + public MarkPostKnownEvent(Post post) { + super(post); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.java b/src/main/java/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.java new file mode 100644 index 0000000..e49fb31 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.java @@ -0,0 +1,40 @@ +/* + * Sone - MarkPostReplyKnownEvent.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.core.event; + +import net.pterodactylus.sone.data.PostReply; + +/** + * Event that signals that a {@link PostReply} has been marked as + * {@link PostReply#isKnown() known}. + * + * @author David ‘Bombe’ Roden + */ +public class MarkPostReplyKnownEvent extends PostReplyEvent { + + /** + * Creates a new “post reply marked known” event. + * + * @param postReply + * The post reply that was marked as known + */ + public MarkPostReplyKnownEvent(PostReply postReply) { + super(postReply); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.java b/src/main/java/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.java new file mode 100644 index 0000000..48a5109 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.java @@ -0,0 +1,40 @@ +/* + * Sone - MarkSoneKnownEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} has been marked as + * {@link Sone#isKnown() known}. + * + * @author David ‘Bombe’ Roden + */ +public class MarkSoneKnownEvent extends SoneEvent { + + /** + * Creates a new “Sone marked known” event. + * + * @param sone + * The Sone that was marked as known + */ + public MarkSoneKnownEvent(Sone sone) { + super(sone); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/NewPostFoundEvent.java b/src/main/java/net/pterodactylus/sone/core/event/NewPostFoundEvent.java new file mode 100644 index 0000000..a7529a8 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/NewPostFoundEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - NewPostFoundEvent.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.core.event; + +import net.pterodactylus.sone.data.Post; + +/** + * Event that signals that a new post was found. + * + * @author David ‘Bombe’ Roden + */ +public class NewPostFoundEvent extends PostEvent { + + /** + * Creates a new “new post found” event. + * + * @param post + * The post that was found + */ + public NewPostFoundEvent(Post post) { + super(post); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.java b/src/main/java/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.java new file mode 100644 index 0000000..fb52bcc --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - NewPostReplyFoundEvent.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.core.event; + +import net.pterodactylus.sone.data.PostReply; + +/** + * Event that signals that a new {@link PostReply} was found. + * + * @author David ‘Bombe’ Roden + */ +public class NewPostReplyFoundEvent extends PostReplyEvent { + + /** + * Creates a new “new post found” event. + * + * @param postReply + * The post reply that was found + */ + public NewPostReplyFoundEvent(PostReply postReply) { + super(postReply); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/NewSoneFoundEvent.java b/src/main/java/net/pterodactylus/sone/core/event/NewSoneFoundEvent.java new file mode 100644 index 0000000..32c6b71 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/NewSoneFoundEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - NewSoneFoundEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a new remote Sone was found. + * + * @author David ‘Bombe’ Roden + */ +public class NewSoneFoundEvent extends SoneEvent { + + /** + * Creates a new “new Sone found” event. + * + * @param sone + * The Sone that was found + */ + public NewSoneFoundEvent(Sone sone) { + super(sone); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/PostEvent.java b/src/main/java/net/pterodactylus/sone/core/event/PostEvent.java new file mode 100644 index 0000000..aa47170 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/PostEvent.java @@ -0,0 +1,55 @@ +/* + * Sone - PostEvent.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.core.event; + +import net.pterodactylus.sone.data.Post; + +/** + * Base class for post events. + * + * @author David ‘Bombe’ Roden + */ +public class PostEvent { + + /** The post the event is about. */ + private final Post post; + + /** + * Creates a new post event. + * + * @param post + * The post the event is about + */ + protected PostEvent(Post post) { + this.post = post; + } + + // + // ACCESSORS + // + + /** + * Returns the post the event is about. + * + * @return The post the event is about + */ + public Post post() { + return post; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/PostRemovedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/PostRemovedEvent.java new file mode 100644 index 0000000..7e07df7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/PostRemovedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - PostRemovedEvent.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.core.event; + +import net.pterodactylus.sone.data.Post; + +/** + * Event that signals that a {@link Post} was removed. + * + * @author David ‘Bombe’ Roden + */ +public class PostRemovedEvent extends PostEvent { + + /** + * Creates a new “post removed” event. + * + * @param post + * The post that was removed + */ + public PostRemovedEvent(Post post) { + super(post); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/PostReplyEvent.java b/src/main/java/net/pterodactylus/sone/core/event/PostReplyEvent.java new file mode 100644 index 0000000..b5e9a1f --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/PostReplyEvent.java @@ -0,0 +1,55 @@ +/* + * Sone - PostReplyEvent.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.core.event; + +import net.pterodactylus.sone.data.PostReply; + +/** + * Base class for {@link PostReply} events. + * + * @author David ‘Bombe’ Roden + */ +public class PostReplyEvent { + + /** The post reply the event is about. */ + private final PostReply postReply; + + /** + * Creates a new post reply event. + * + * @param postReply + * The post reply the event is about + */ + protected PostReplyEvent(PostReply postReply) { + this.postReply = postReply; + } + + // + // ACCESSORS + // + + /** + * Returns the post reply the event is about. + * + * @return The post reply the event is about + */ + public PostReply postReply() { + return postReply; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.java new file mode 100644 index 0000000..1ed4f9b --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - PostReplyRemovedEvent.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.core.event; + +import net.pterodactylus.sone.data.PostReply; + +/** + * Event that signals that a {@link PostReply} was removed. + * + * @author David ‘Bombe’ Roden + */ +public class PostReplyRemovedEvent extends PostReplyEvent { + + /** + * Creates a new “post reply removed” event. + * + * @param postReply + * The post reply that was removed + */ + public PostReplyRemovedEvent(PostReply postReply) { + super(postReply); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneEvent.java new file mode 100644 index 0000000..68691ed --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneEvent.java @@ -0,0 +1,55 @@ +/* + * Sone - SoneEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Base class for Sone events. + * + * @author David ‘Bombe’ Roden + */ +public abstract class SoneEvent { + + /** The Sone this event is about. */ + private final Sone sone; + + /** + * Creates a new Sone event. + * + * @param sone + * The Sone this event is about + */ + protected SoneEvent(Sone sone) { + this.sone = sone; + } + + // + // ACCESSORS + // + + /** + * Returns the Sone this event is about. + * + * @return The Sone this event is about + */ + public Sone sone() { + return sone; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.java new file mode 100644 index 0000000..5884839 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.java @@ -0,0 +1,58 @@ +/* + * Sone - SoneInsertAbortedEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} insert was aborted. + * + * @author David ‘Bombe’ Roden + */ +public class SoneInsertAbortedEvent extends SoneEvent { + + /** The cause of the abortion. */ + private final Throwable cause; + + /** + * Creates a new “Sone was inserted” event. + * + * @param sone + * The Sone that was inserted + * @param cause + * The cause of the abortion + */ + public SoneInsertAbortedEvent(Sone sone, Throwable cause) { + super(sone); + this.cause = cause; + } + + // + // ACCESSORS + // + + /** + * Returns the cause of the abortion. + * + * @return The cause of the abortion (may be {@code null}) + */ + public Throwable cause() { + return cause; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertedEvent.java new file mode 100644 index 0000000..8a4280d --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertedEvent.java @@ -0,0 +1,58 @@ +/* + * Sone - SoneInsertedEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} was inserted. + * + * @author David ‘Bombe’ Roden + */ +public class SoneInsertedEvent extends SoneEvent { + + /** The duration of the insert. */ + private final long insertDuration; + + /** + * Creates a new “Sone was inserted” event. + * + * @param sone + * The Sone that was inserted + * @param insertDuration + * The duration of the insert (in milliseconds) + */ + public SoneInsertedEvent(Sone sone, long insertDuration) { + super(sone); + this.insertDuration = insertDuration; + } + + // + // ACCESSORS + // + + /** + * Returns the duration of the insert. + * + * @return The duration of the insert (in milliseconds) + */ + public long insertDuration() { + return insertDuration; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertingEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertingEvent.java new file mode 100644 index 0000000..37effe9 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertingEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - SoneInsertingEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} is now being inserted. + * + * @author David ‘Bombe’ Roden + */ +public class SoneInsertingEvent extends SoneEvent { + + /** + * Creates a new “Sone is being inserted” event. + * + * @param sone + * The Sone that is being inserted + */ + public SoneInsertingEvent(Sone sone) { + super(sone); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneLockedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneLockedEvent.java new file mode 100644 index 0000000..a707aca --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneLockedEvent.java @@ -0,0 +1,40 @@ +/* + * Sone - SoneLockedEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} was locked. Only + * {@link Sone#isLocal() local Sones} can be locked. + * + * @author David ‘Bombe’ Roden + */ +public class SoneLockedEvent extends SoneEvent { + + /** + * Creates a new “Sone locked” event. + * + * @param sone + * The Sone that was locked + */ + public SoneLockedEvent(Sone sone) { + super(sone); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneRemovedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneRemovedEvent.java new file mode 100644 index 0000000..5b2e5d8 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneRemovedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - SoneRemovedEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} was removed. + * + * @author David ‘Bombe’ Roden + */ +public class SoneRemovedEvent extends SoneEvent { + + /** + * Creates a new “Sone removed” event. + * + * @param sone + * The Sone that was removed + */ + public SoneRemovedEvent(Sone sone) { + super(sone); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneUnlockedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneUnlockedEvent.java new file mode 100644 index 0000000..86100b4 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/SoneUnlockedEvent.java @@ -0,0 +1,40 @@ +/* + * Sone - SoneUnlockedEvent.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.core.event; + +import net.pterodactylus.sone.data.Sone; + +/** + * Event that signals that a {@link Sone} was unlocked. Only + * {@link Sone#isLocal() local Sones} can be locked. + * + * @author David ‘Bombe’ Roden + */ +public class SoneUnlockedEvent extends SoneEvent { + + /** + * Creates a new “Sone unlocked” event. + * + * @param sone + * The Sone that was unlocked + */ + public SoneUnlockedEvent(Sone sone) { + super(sone); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/core/event/UpdateFoundEvent.java b/src/main/java/net/pterodactylus/sone/core/event/UpdateFoundEvent.java new file mode 100644 index 0000000..2e7b341 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/event/UpdateFoundEvent.java @@ -0,0 +1,86 @@ +/* + * Sone - UpdateFoundEvent.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.core.event; + +import net.pterodactylus.util.version.Version; + +/** + * Event that signals that an update for Sone was found. + * + * @author David ‘Bombe’ Roden + */ +public class UpdateFoundEvent { + + /** The version that was found. */ + private final Version version; + + /** The time the update was released. */ + private final long releaseTime; + + /** The latest edition of the update page. */ + private final long latestEdition; + + /** + * Creates a new “update found” event. + * + * @param version + * The version of the update + * @param releaseTime + * The release time of the update + * @param latestEdition + * The latest edition of the update page + */ + public UpdateFoundEvent(Version version, long releaseTime, long latestEdition) { + this.version = version; + this.releaseTime = releaseTime; + this.latestEdition = latestEdition; + } + + // + // ACCESSORS + // + + /** + * Returns the version of the update. + * + * @return The version of the update + */ + public Version version() { + return version; + } + + /** + * Returns the release time of the update. + * + * @return The releae time of the update (in milliseconds since Jan 1, 1970 + * UTC) + */ + public long releaseTime() { + return releaseTime; + } + + /** + * Returns the latest edition of the update page. + * + * @return The latest edition of the update page + */ + public long latestEdition() { + return latestEdition; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java index 44b37b7..5375d98 100644 --- a/src/main/java/net/pterodactylus/sone/data/Album.java +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -1,5 +1,5 @@ /* - * Sone - Album.java - Copyright © 2011–2012 David Roden + * Sone - Album.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 @@ -17,6 +17,10 @@ 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.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -24,11 +28,14 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import net.pterodactylus.util.collection.IterableWrapper; -import net.pterodactylus.util.collection.filter.NotNullFilter; -import net.pterodactylus.util.collection.mapper.Mapper; -import net.pterodactylus.util.object.Default; -import net.pterodactylus.util.validation.Validation; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; /** * Container for images that can also contain nested {@link Album}s. @@ -46,6 +53,20 @@ public class Album implements Fingerprintable { } }; + /** Function that flattens the given album and all albums beneath it. */ + public static final Function> FLATTENER = new Function>() { + + @Override + public List apply(Album album) { + List albums = new ArrayList(); + albums.add(album); + for (Album subAlbum : album.getAlbums()) { + albums.addAll(FluentIterable.from(ImmutableList.of(subAlbum)).transformAndConcat(FLATTENER).toList()); + } + return albums; + } + }; + /** The ID of this album. */ private final String id; @@ -87,8 +108,7 @@ public class Album implements Fingerprintable { * The ID of the album */ public Album(String id) { - Validation.begin().isNotNull("Album ID", id).check(); - this.id = id; + this.id = checkNotNull(id, "id must not be null"); } // @@ -122,7 +142,8 @@ public class Album implements Fingerprintable { * @return This album */ public Album setSone(Sone sone) { - Validation.begin().isNotNull("New Album Owner", sone).isEither("Old Album Owner", this.sone, null, sone).check(); + checkNotNull(sone, "sone must not be null"); + checkState((this.sone == null) || (this.sone.equals(sone)), "album owner must not already be set to some other Sone"); this.sone = sone; return this; } @@ -143,7 +164,9 @@ public class Album implements Fingerprintable { * The album to add */ public void addAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEither("Old Album Parent", this.parent, null, album.parent).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().equals(sone), "album must belong to the same Sone as this album"); + checkState((this.parent == null) || (this.parent.equals(album.parent)), "album must not already be set to some other Sone"); album.setParent(this); if (!albums.contains(album)) { albums.add(album); @@ -157,7 +180,9 @@ public class Album implements Fingerprintable { * The album to remove */ public void removeAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.sone.equals(sone), "album must belong this album’s Sone"); + checkArgument(equals(album.parent), "album must belong to this album"); albums.remove(album); album.removeParent(); } @@ -172,7 +197,9 @@ public class Album implements Fingerprintable { * null if the album did not change its place */ public Album moveAlbumUp(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.sone.equals(sone), "album must belong to the same Sone as this album"); + checkArgument(equals(album.parent), "album must belong to this album"); int oldIndex = albums.indexOf(album); if (oldIndex <= 0) { return null; @@ -192,7 +219,9 @@ public class Album implements Fingerprintable { * null if the album did not change its place */ public Album moveAlbumDown(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.sone, sone).isEqual("Album Parent", album.parent, this).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.sone.equals(sone), "album must belong to the same Sone as this album"); + checkArgument(equals(album.parent), "album must belong to this album"); int oldIndex = albums.indexOf(album); if ((oldIndex < 0) || (oldIndex >= (albums.size() - 1))) { return null; @@ -208,15 +237,14 @@ public class Album implements Fingerprintable { * @return The images in this album */ public List getImages() { - return IterableWrapper.wrap(imageIds).map(new Mapper() { + return new ArrayList(Collections2.filter(Collections2.transform(imageIds, new Function() { @Override @SuppressWarnings("synthetic-access") - public Image map(String imageId) { + public Image apply(String imageId) { return images.get(imageId); } - - }).filter(new NotNullFilter()).list(); + }), Predicates.notNull())); } /** @@ -226,7 +254,9 @@ public class Album implements Fingerprintable { * The image to add */ public void addImage(Image image) { - Validation.begin().isNotNull("Image", image).check().isNotNull("Image Owner", image.getSone()).check().isEqual("Image Owner", image.getSone(), sone).check(); + checkNotNull(image, "image must not be null"); + checkNotNull(image.getSone(), "image must have an owner"); + checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album"); if (image.getAlbum() != null) { image.getAlbum().removeImage(image); } @@ -247,7 +277,9 @@ public class Album implements Fingerprintable { * The image to remove */ public void removeImage(Image image) { - Validation.begin().isNotNull("Image", image).check().isEqual("Image Owner", image.getSone(), sone).check(); + checkNotNull(image, "image must not be null"); + checkNotNull(image.getSone(), "image must have an owner"); + checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album"); imageIds.remove(image.getId()); images.remove(image.getId()); if (image.getId().equals(albumImage)) { @@ -269,7 +301,10 @@ public class Album implements Fingerprintable { * null if the image did not change its place */ public Image moveImageUp(Image image) { - Validation.begin().isNotNull("Image", image).check().isEqual("Image Album", image.getAlbum(), this).isEqual("Album Owner", image.getAlbum().getSone(), sone).check(); + checkNotNull(image, "image must not be null"); + checkNotNull(image.getSone(), "image must have an owner"); + checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album"); + checkArgument(image.getAlbum().equals(this), "image must belong to this album"); int oldIndex = imageIds.indexOf(image.getId()); if (oldIndex <= 0) { return null; @@ -289,7 +324,10 @@ public class Album implements Fingerprintable { * null if the image did not change its place */ public Image moveImageDown(Image image) { - Validation.begin().isNotNull("Image", image).check().isEqual("Image Album", image.getAlbum(), this).isEqual("Album Owner", image.getAlbum().getSone(), sone).check(); + checkNotNull(image, "image must not be null"); + checkNotNull(image.getSone(), "image must have an owner"); + checkArgument(image.getSone().equals(sone), "image must belong to the same Sone as this album"); + checkArgument(image.getAlbum().equals(this), "image must belong to this album"); int oldIndex = imageIds.indexOf(image.getId()); if ((oldIndex == -1) || (oldIndex >= (imageIds.size() - 1))) { return null; @@ -309,7 +347,7 @@ public class Album implements Fingerprintable { if (albumImage == null) { return null; } - return Default.forNull(images.get(albumImage), images.values().iterator().next()); + return Optional.fromNullable(images.get(albumImage)).or(images.values().iterator().next()); } /** @@ -351,8 +389,7 @@ public class Album implements Fingerprintable { * @return This album */ protected Album setParent(Album parent) { - Validation.begin().isNotNull("Album Parent", parent).check(); - this.parent = parent; + this.parent = checkNotNull(parent, "parent must not be null"); return this; } @@ -383,8 +420,7 @@ public class Album implements Fingerprintable { * @return This album */ public Album setTitle(String title) { - Validation.begin().isNotNull("Album Title", title).check(); - this.title = title; + this.title = checkNotNull(title, "title must not be null"); return this; } @@ -405,8 +441,7 @@ public class Album implements Fingerprintable { * @return This album */ public Album setDescription(String description) { - Validation.begin().isNotNull("Album Description", description).check(); - this.description = description; + this.description = checkNotNull(description, "description must not be null"); return this; } @@ -419,33 +454,33 @@ public class Album implements Fingerprintable { */ @Override public String getFingerprint() { - StringBuilder fingerprint = new StringBuilder(); - fingerprint.append("Album("); - fingerprint.append("ID(").append(id).append(')'); - fingerprint.append("Title(").append(title).append(')'); - fingerprint.append("Description(").append(description).append(')'); + Hasher hash = Hashing.sha256().newHasher(); + hash.putString("Album("); + hash.putString("ID(").putString(id).putString(")"); + hash.putString("Title(").putString(title).putString(")"); + hash.putString("Description(").putString(description).putString(")"); if (albumImage != null) { - fingerprint.append("AlbumImage(").append(albumImage).append(')'); + hash.putString("AlbumImage(").putString(albumImage).putString(")"); } /* add nested albums. */ - fingerprint.append("Albums("); + hash.putString("Albums("); for (Album album : albums) { - fingerprint.append(album.getFingerprint()); + hash.putString(album.getFingerprint()); } - fingerprint.append(')'); + hash.putString(")"); /* add images. */ - fingerprint.append("Images("); + hash.putString("Images("); for (Image image : getImages()) { if (image.isInserted()) { - fingerprint.append(image.getFingerprint()); + hash.putString(image.getFingerprint()); } } - fingerprint.append(')'); + hash.putString(")"); - fingerprint.append(')'); - return fingerprint.toString(); + hash.putString(")"); + return hash.hash().toString(); } // diff --git a/src/main/java/net/pterodactylus/sone/data/Client.java b/src/main/java/net/pterodactylus/sone/data/Client.java index 93b8dbf..b77b9f2 100644 --- a/src/main/java/net/pterodactylus/sone/data/Client.java +++ b/src/main/java/net/pterodactylus/sone/data/Client.java @@ -1,5 +1,5 @@ /* - * Sone - Client.java - Copyright © 2010–2012 David Roden + * Sone - Client.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/data/Fingerprintable.java b/src/main/java/net/pterodactylus/sone/data/Fingerprintable.java index 96e6bc0..2cf34ed 100644 --- a/src/main/java/net/pterodactylus/sone/data/Fingerprintable.java +++ b/src/main/java/net/pterodactylus/sone/data/Fingerprintable.java @@ -1,5 +1,5 @@ /* - * Sone - Fingerprintable.java - Copyright © 2011–2012 David Roden + * Sone - Fingerprintable.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 diff --git a/src/main/java/net/pterodactylus/sone/data/Image.java b/src/main/java/net/pterodactylus/sone/data/Image.java index 25a36e3..952d540 100644 --- a/src/main/java/net/pterodactylus/sone/data/Image.java +++ b/src/main/java/net/pterodactylus/sone/data/Image.java @@ -1,5 +1,5 @@ /* - * Sone - Image.java - Copyright © 2011–2012 David Roden + * Sone - Image.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 @@ -17,9 +17,14 @@ 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 net.pterodactylus.util.validation.Validation; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; /** * Container for image metadata. @@ -70,8 +75,7 @@ public class Image implements Fingerprintable { * The ID of the image */ public Image(String id) { - Validation.begin().isNotNull("Image ID", id).check(); - this.id = id; + this.id = checkNotNull(id, "id must not be null"); } // @@ -105,7 +109,8 @@ public class Image implements Fingerprintable { * @return This image */ public Image setSone(Sone sone) { - Validation.begin().isNotNull("New Image Owner", sone).isEither("Old Image Owner", this.sone, null, sone).check(); + 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; } @@ -128,7 +133,8 @@ public class Image implements Fingerprintable { * @return This image */ public Image setAlbum(Album album) { - Validation.begin().isNotNull("New Album", album).check().isEqual("Album Owner and Image Owner", album.getSone(), getSone()).check(); + 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; } @@ -151,7 +157,8 @@ public class Image implements Fingerprintable { * @return This image */ public Image setKey(String key) { - Validation.begin().isNotNull("New Image Key", key).isEither("Old Image Key", this.key, null, key).check(); + 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; } @@ -187,7 +194,8 @@ public class Image implements Fingerprintable { * @return This image */ public Image setCreationTime(long creationTime) { - Validation.begin().isGreater("New Image Creation Time", creationTime, 0).isEither("Old Image Creation Time", this.creationTime, 0L, creationTime).check(); + 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; } @@ -210,7 +218,8 @@ public class Image implements Fingerprintable { * @return This image */ public Image setWidth(int width) { - Validation.begin().isGreater("New Image Width", width, 0).isEither("Old Image Width", this.width, 0, width).check(); + 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; } @@ -233,7 +242,8 @@ public class Image implements Fingerprintable { * @return This image */ public Image setHeight(int height) { - Validation.begin().isGreater("New Image Height", height, 0).isEither("Old Image Height", this.height, 0, height).check(); + 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; } @@ -255,8 +265,7 @@ public class Image implements Fingerprintable { * @return This image */ public Image setTitle(String title) { - Validation.begin().isNotNull("Image Title", title).check(); - this.title = title; + this.title = checkNotNull(title, "title must not be null"); return this; } @@ -277,8 +286,7 @@ public class Image implements Fingerprintable { * @return This image */ public Image setDescription(String description) { - Validation.begin().isNotNull("Image Description", description).check(); - this.description = description; + this.description = checkNotNull(description, "description must not be null"); return this; } @@ -291,13 +299,13 @@ public class Image implements Fingerprintable { */ @Override public String getFingerprint() { - StringBuilder fingerprint = new StringBuilder(); - fingerprint.append("Image("); - fingerprint.append("ID(").append(id).append(')'); - fingerprint.append("Title(").append(title).append(')'); - fingerprint.append("Description(").append(description).append(')'); - fingerprint.append(')'); - return fingerprint.toString(); + 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(); } // diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index f171475..6523e9b 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -1,5 +1,5 @@ /* - * Sone - Post.java - Copyright © 2010–2012 David Roden + * Sone - Post.java - Copyright © 2010–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 @@ -19,7 +19,8 @@ package net.pterodactylus.sone.data; import java.util.Comparator; -import net.pterodactylus.util.collection.filter.Filter; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; /** * A post is a short message that a user writes in his Sone to let other users @@ -40,10 +41,10 @@ public interface Post { }; /** Filter for posts with timestamps from the future. */ - public static final Filter FUTURE_POSTS_FILTER = new Filter() { + public static final Predicate FUTURE_POSTS_FILTER = new Predicate() { @Override - public boolean filterObject(Post post) { + public boolean apply(Post post) { return post.getTime() <= System.currentTimeMillis(); } @@ -68,29 +69,23 @@ public interface Post { public Sone getSone(); /** - * Sets the Sone of this post. + * Returns the ID of the recipient {@link Sone}, or + * {@link Optional#absent()} if this post does not have a recipient. * - * @param sone - * The Sone of this post - * @return This post (for method chaining) + * @return The ID of the recipient, or {@link Optional#absent()} */ - public Post setSone(Sone sone); + public Optional getRecipientId(); /** - * Returns the recipient of this post, if any. + * Returns the recipient of this post, if any. As this method can return + * {@link Optional#absent()} if the post has a recipient which has not yet + * been loaded, it is recommended to use {@link #hasRecipient()} to check + * for the presence of a recipient. * - * @return The recipient of this post, or {@code null} + * @return The recipient of this post, or {@link Optional#absent()} if there + * is no recipient */ - public Sone getRecipient(); - - /** - * Sets the recipient of this post. - * - * @param recipient - * The recipient of this post, or {@code null} - * @return This post (for method chaining) - */ - public Post setRecipient(Sone recipient); + public Optional getRecipient(); /** * Returns the time of the post. @@ -100,15 +95,6 @@ public interface Post { public long getTime(); /** - * Sets the time of this post. - * - * @param time - * The time of this post (in milliseconds since Jan 1, 1970 UTC) - * @return This post (for method chaining) - */ - public Post setTime(long time); - - /** * Returns the text of the post. * * @return The text of the post @@ -116,15 +102,6 @@ public interface Post { public String getText(); /** - * Sets the text of this post. - * - * @param text - * The text of this post - * @return This post (for method chaining) - */ - public Post setText(String text); - - /** * Returns whether this post is known. * * @return {@code true} if this post is known, {@code false} otherwise diff --git a/src/main/java/net/pterodactylus/sone/data/PostReply.java b/src/main/java/net/pterodactylus/sone/data/PostReply.java index 0f63be1..1e870b9 100644 --- a/src/main/java/net/pterodactylus/sone/data/PostReply.java +++ b/src/main/java/net/pterodactylus/sone/data/PostReply.java @@ -1,5 +1,5 @@ /* - * Sone - PostReply.java - Copyright © 2010–2012 David Roden + * Sone - PostReply.java - Copyright © 2010–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 @@ -17,7 +17,8 @@ package net.pterodactylus.sone.data; -import java.util.UUID; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; /** * A reply is like a {@link Post} but can never be posted on its own, it always @@ -25,93 +26,32 @@ import java.util.UUID; * * @author David ‘Bombe’ Roden */ -public class PostReply extends Reply { - - /** The Post this reply refers to. */ - private volatile Post post; +public interface PostReply extends Reply { /** - * Creates a new reply. - * - * @param id - * The ID of the reply + * Filter that selects {@link PostReply}s that have a + * {@link Optional#isPresent() present} {@link #getPost() post}. */ - public PostReply(String id) { - this(id, null, null, 0, null); - } + public static final Predicate HAS_POST_FILTER = new Predicate() { - /** - * Creates a new reply. - * - * @param sone - * The sone that posted the reply - * @param post - * The post to reply to - * @param text - * The text of the reply - */ - public PostReply(Sone sone, Post post, String text) { - this(sone, post, System.currentTimeMillis(), text); - } - - /** - * Creates a new reply- - * - * @param sone - * The sone that posted the reply - * @param post - * The post to reply to - * @param time - * The time of the reply - * @param text - * The text of the reply - */ - public PostReply(Sone sone, Post post, long time, String text) { - this(UUID.randomUUID().toString(), sone, post, time, text); - } + @Override + public boolean apply(PostReply postReply) { + return postReply.getPost().isPresent(); + } + }; /** - * Creates a new reply- + * Returns the ID of the post this reply refers to. * - * @param sone - * The sone that posted the reply - * @param id - * The ID of the reply - * @param post - * The post to reply to - * @param time - * The time of the reply - * @param text - * The text of the reply + * @return The ID of the post this reply refers to */ - public PostReply(String id, Sone sone, Post post, long time, String text) { - super(id, sone, time, text); - this.post = post; - } - - // - // ACCESSORS - // + public String getPostId(); /** * Returns the post this reply refers to. * * @return The post this reply refers to */ - public Post getPost() { - return post; - } - - /** - * Sets the post this reply refers to. - * - * @param post - * The post this reply refers to - * @return This reply (for method chaining) - */ - public PostReply setPost(Post post) { - this.post = post; - return this; - } + public Optional getPost(); } diff --git a/src/main/java/net/pterodactylus/sone/data/Profile.java b/src/main/java/net/pterodactylus/sone/data/Profile.java index 6936409..5a4fde0 100644 --- a/src/main/java/net/pterodactylus/sone/data/Profile.java +++ b/src/main/java/net/pterodactylus/sone/data/Profile.java @@ -1,5 +1,5 @@ /* - * Sone - Profile.java - Copyright © 2010–2012 David Roden + * Sone - Profile.java - Copyright © 2010–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 @@ -17,12 +17,17 @@ 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.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; -import net.pterodactylus.util.validation.Validation; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; /** * A profile stores personal information about a {@link Sone}. All information @@ -237,7 +242,7 @@ public class Profile implements Fingerprintable { this.avatar = null; return this; } - Validation.begin().isEqual("Image Owner", avatar.getSone(), sone).check(); + checkArgument(avatar.getSone().equals(sone), "avatar must belong to Sone"); this.avatar = avatar.getId(); return this; } @@ -283,7 +288,7 @@ public class Profile implements Fingerprintable { * field with the given ID */ public Field getFieldById(String fieldId) { - Validation.begin().isNotNull("Field ID", fieldId).check(); + checkNotNull(fieldId, "fieldId must not be null"); for (Field field : fields) { if (field.getId().equals(fieldId)) { return field; @@ -319,7 +324,9 @@ public class Profile implements Fingerprintable { * if the name is not valid */ public Field addField(String fieldName) throws IllegalArgumentException { - Validation.begin().isNotNull("Field Name", fieldName).check().isGreater("Field Name Length", fieldName.length(), 0).isNull("Field Name Unique", getFieldByName(fieldName)).check(); + checkNotNull(fieldName, "fieldName must not be null"); + checkArgument(fieldName.length() > 0, "fieldName must not be empty"); + checkState(getFieldByName(fieldName) == null, "fieldName must be unique"); @SuppressWarnings("synthetic-access") Field field = new Field().setName(fieldName); fields.add(field); @@ -335,7 +342,9 @@ public class Profile implements Fingerprintable { * The field to move up */ public void moveFieldUp(Field field) { - Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).isGreater("Field Index", getFieldIndex(field), 0).check(); + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + checkArgument(getFieldIndex(field) > 0, "field index must be > 0"); int fieldIndex = getFieldIndex(field); fields.remove(field); fields.add(fieldIndex - 1, field); @@ -350,7 +359,9 @@ public class Profile implements Fingerprintable { * The field to move down */ public void moveFieldDown(Field field) { - Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).isLess("Field Index", getFieldIndex(field), fields.size() - 1).check(); + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); + checkArgument(getFieldIndex(field) < fields.size() - 1, "field index must be < " + (fields.size() - 1)); int fieldIndex = getFieldIndex(field); fields.remove(field); fields.add(fieldIndex + 1, field); @@ -363,7 +374,8 @@ public class Profile implements Fingerprintable { * The field to remove */ public void removeField(Field field) { - Validation.begin().isNotNull("Field", field).check().is("Field Existing", hasField(field)).check(); + checkNotNull(field, "field must not be null"); + checkArgument(hasField(field), "field must belong to this profile"); fields.remove(field); } @@ -392,37 +404,37 @@ public class Profile implements Fingerprintable { */ @Override public String getFingerprint() { - StringBuilder fingerprint = new StringBuilder(); - fingerprint.append("Profile("); + Hasher hash = Hashing.sha256().newHasher(); + hash.putString("Profile("); if (firstName != null) { - fingerprint.append("FirstName(").append(firstName).append(')'); + hash.putString("FirstName(").putString(firstName).putString(")"); } if (middleName != null) { - fingerprint.append("MiddleName(").append(middleName).append(')'); + hash.putString("MiddleName(").putString(middleName).putString(")"); } if (lastName != null) { - fingerprint.append("LastName(").append(lastName).append(')'); + hash.putString("LastName(").putString(lastName).putString(")"); } if (birthDay != null) { - fingerprint.append("BirthDay(").append(birthDay).append(')'); + hash.putString("BirthDay(").putInt(birthDay).putString(")"); } if (birthMonth != null) { - fingerprint.append("BirthMonth(").append(birthMonth).append(')'); + hash.putString("BirthMonth(").putInt(birthMonth).putString(")"); } if (birthYear != null) { - fingerprint.append("BirthYear(").append(birthYear).append(')'); + hash.putString("BirthYear(").putInt(birthYear).putString(")"); } if (avatar != null) { - fingerprint.append("Avatar(").append(avatar).append(')'); + hash.putString("Avatar(").putString(avatar).putString(")"); } - fingerprint.append("ContactInformation("); + hash.putString("ContactInformation("); for (Field field : fields) { - fingerprint.append(field.getName()).append('(').append(field.getValue()).append(')'); + hash.putString(field.getName()).putString("(").putString(field.getValue()).putString(")"); } - fingerprint.append(")"); - fingerprint.append(")"); + hash.putString(")"); + hash.putString(")"); - return fingerprint.toString(); + return hash.hash().toString(); } /** @@ -455,8 +467,7 @@ public class Profile implements Fingerprintable { * The ID of the field */ private Field(String id) { - Validation.begin().isNotNull("Field ID", id).check(); - this.id = id; + this.id = checkNotNull(id, "id must not be null"); } /** @@ -487,7 +498,8 @@ public class Profile implements Fingerprintable { * @return This field */ public Field setName(String name) { - Validation.begin().isNotNull("Field Name", name).check().is("Field Unique", (getFieldByName(name) == null) || equals(getFieldByName(name))).check(); + checkNotNull(name, "name must not be null"); + checkArgument(getFieldByName(name) == null, "name must be unique"); this.name = name; return this; } diff --git a/src/main/java/net/pterodactylus/sone/data/Reply.java b/src/main/java/net/pterodactylus/sone/data/Reply.java index be60b88..a686023 100644 --- a/src/main/java/net/pterodactylus/sone/data/Reply.java +++ b/src/main/java/net/pterodactylus/sone/data/Reply.java @@ -1,5 +1,5 @@ /* - * Sone - Reply.java - Copyright © 2011–2012 David Roden + * Sone - Reply.java - Copyright © 2010–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 @@ -18,21 +18,20 @@ package net.pterodactylus.sone.data; import java.util.Comparator; -import java.util.UUID; -import net.pterodactylus.util.collection.filter.Filter; +import com.google.common.base.Predicate; /** - * Abstract base class for all replies. + * Defines methods common for all replies. * * @param * The type of the reply * @author David ‘Bombe’ Roden */ -public abstract class Reply> { +public interface Reply> { /** Comparator that sorts replies ascending by time. */ - public static final Comparator> TIME_COMPARATOR = new Comparator>() { + public static final Comparator> TIME_COMPARATOR = new Comparator>() { /** * {@inheritDoc} @@ -45,159 +44,52 @@ public abstract class Reply> { }; /** Filter for replies with timestamps from the future. */ - public static final Filter> FUTURE_REPLY_FILTER = new Filter>() { + public static final Predicate> FUTURE_REPLY_FILTER = new Predicate>() { /** * {@inheritDoc} */ @Override - public boolean filterObject(Reply reply) { + public boolean apply(Reply reply) { return reply.getTime() <= System.currentTimeMillis(); } }; - /** The ID of the reply. */ - private final String id; - - /** The Sone that created this reply. */ - private volatile Sone sone; - - /** The time of the reply. */ - private volatile long time; - - /** The text of the reply. */ - private volatile String text; - - /** Whether the reply is known. */ - private volatile boolean known; - - /** - * Creates a new reply with the given ID. - * - * @param id - * The ID of the reply - */ - protected Reply(String id) { - this(id, null, 0, null); - } - - /** - * Creates a new reply with a new random ID. - * - * @param sone - * The Sone of the reply - * @param time - * The time of the reply - * @param text - * The text of the reply - */ - protected Reply(Sone sone, long time, String text) { - this(UUID.randomUUID().toString(), sone, time, text); - } - - /** - * Creates a new reply. - * - * @param id - * The ID of the reply - * @param sone - * The Sone of the reply - * @param time - * The time of the reply - * @param text - * The text of the reply - */ - protected Reply(String id, Sone sone, long time, String text) { - this.id = id; - this.sone = sone; - this.time = time; - this.text = text; - } - /** * Returns the ID of the reply. * * @return The ID of the reply */ - public String getId() { - return id; - } + public String getId(); /** * Returns the Sone that posted this reply. * * @return The Sone that posted this reply */ - public Sone getSone() { - return sone; - } - - /** - * Sets the Sone that posted this reply. - * - * @param sone - * The Sone that posted this reply - * @return This reply (for method chaining) - */ - @SuppressWarnings("unchecked") - public T setSone(Sone sone) { - this.sone = sone; - return (T) this; - } + public Sone getSone(); /** * Returns the time of the reply. * * @return The time of the reply (in milliseconds since Jan 1, 1970 UTC) */ - public long getTime() { - return time; - } - - /** - * Sets the time of this reply. - * - * @param time - * The time of this reply (in milliseconds since Jan 1, 1970 UTC) - * @return This reply (for method chaining) - */ - @SuppressWarnings("unchecked") - public T setTime(long time) { - this.time = time; - return (T) this; - } + public long getTime(); /** * Returns the text of the reply. * * @return The text of the reply */ - public String getText() { - return text; - } - - /** - * Sets the text of this reply. - * - * @param text - * The text of this reply - * @return This reply (for method chaining) - */ - @SuppressWarnings("unchecked") - public T setText(String text) { - this.text = text; - return (T) this; - } + public String getText(); /** * Returns whether this reply is known. * * @return {@code true} if this reply is known, {@code false} otherwise */ - public boolean isKnown() { - return known; - } + public boolean isKnown(); /** * Sets whether this reply is known. @@ -206,42 +98,6 @@ public abstract class Reply> { * {@code true} if this reply is known, {@code false} otherwise * @return This reply */ - @SuppressWarnings("unchecked") - public T setKnown(boolean known) { - this.known = known; - return (T) this; - } - - // - // OBJECT METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return id.hashCode(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object object) { - if (!(object instanceof Reply)) { - return false; - } - Reply reply = (Reply) object; - return reply.id.equals(id); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return getClass().getName() + "[id=" + id + ",sone=" + sone + ",time=" + time + ",text=" + text + "]"; - } + public T setKnown(boolean known); } diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 99d65ec..4f4fd98 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -1,5 +1,5 @@ /* - * Sone - Sone.java - Copyright © 2010–2012 David Roden + * Sone - Sone.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.data; +import static com.google.common.base.Preconditions.*; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -28,20 +30,23 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; import java.util.logging.Logger; -import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.Options; import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.template.SoneAccessor; -import net.pterodactylus.util.collection.filter.Filter; import net.pterodactylus.util.logging.Logging; -import net.pterodactylus.util.validation.Validation; + import freenet.keys.FreenetURI; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + /** * A Sone defines everything about a user: her profile, her status updates, her * replies, her likes and dislikes, etc. - *

+ *

* Operations that modify the Sone need to synchronize on the Sone in question. * * @author David ‘Bombe’ Roden @@ -106,9 +111,7 @@ public class Sone implements Fingerprintable, Comparable { }; - /** - * Comparator that sorts Sones by last activity (least recent active first). - */ + /** Comparator that sorts Sones by last activity (least recent active first). */ public static final Comparator LAST_ACTIVITY_COMPARATOR = new Comparator() { @Override @@ -142,29 +145,29 @@ public class Sone implements Fingerprintable, Comparable { }; /** Filter to remove Sones that have not been downloaded. */ - public static final Filter EMPTY_SONE_FILTER = new Filter() { + public static final Predicate EMPTY_SONE_FILTER = new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return sone.getTime() != 0; } }; - /** Filter that matches all {@link Core#isLocalSone(Sone) local Sones}. */ - public static final Filter LOCAL_SONE_FILTER = new Filter() { + /** Filter that matches all {@link Sone#isLocal() local Sones}. */ + public static final Predicate LOCAL_SONE_FILTER = new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return sone.getIdentity() instanceof OwnIdentity; } }; /** Filter that matches Sones that have at least one album. */ - public static final Filter HAS_ALBUM_FILTER = new Filter() { + public static final Predicate HAS_ALBUM_FILTER = new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return !sone.getAlbums().isEmpty(); } }; @@ -175,6 +178,9 @@ public class Sone implements Fingerprintable, Comparable { /** The ID of this Sone. */ private final String id; + /** Whether the Sone is local. */ + private final boolean local; + /** The identity of this Sone. */ private Identity identity; @@ -222,16 +228,19 @@ public class Sone implements Fingerprintable, Comparable { private final List albums = new CopyOnWriteArrayList(); /** Sone-specific options. */ - private final Options options = new Options(); + private Options options = new Options(); /** * Creates a new Sone. * * @param id - * The ID of the Sone + * The ID of the Sone + * @param local + * {@code true} if the Sone is a local Sone, {@code false} otherwise */ - public Sone(String id) { + public Sone(String id, boolean local) { this.id = id; + this.local = local; } // @@ -261,10 +270,10 @@ public class Sone implements Fingerprintable, Comparable { * identity has to match this Sone’s {@link #getId()}. * * @param identity - * The identity of this Sone + * The identity of this Sone * @return This Sone (for method chaining) * @throws IllegalArgumentException - * if the ID of the identity does not match this Sone’s ID + * if the ID of the identity does not match this Sone’s ID */ public Sone setIdentity(Identity identity) throws IllegalArgumentException { if (!identity.getId().equals(id)) { @@ -284,6 +293,15 @@ public class Sone implements Fingerprintable, Comparable { } /** + * Returns whether this Sone is a local Sone. + * + * @return {@code true} if this Sone is a local Sone, {@code false} otherwise + */ + public boolean isLocal() { + return local; + } + + /** * Returns the request URI of this Sone. * * @return The request URI of this Sone @@ -296,7 +314,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the request URI of this Sone. * * @param requestUri - * The request URI of this Sone + * The request URI of this Sone * @return This Sone (for method chaining) */ public Sone setRequestUri(FreenetURI requestUri) { @@ -324,7 +342,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the insert URI of this Sone. * * @param insertUri - * The insert URI of this Sone + * The insert URI of this Sone * @return This Sone (for method chaining) */ public Sone setInsertUri(FreenetURI insertUri) { @@ -350,11 +368,11 @@ public class Sone implements Fingerprintable, Comparable { /** * Sets the latest edition of this Sone. If the given latest edition is not - * greater than the current latest edition, the latest edition of this Sone - * is not changed. + * greater than the current latest edition, the latest edition of this Sone is + * not changed. * * @param latestEdition - * The latest edition of this Sone + * The latest edition of this Sone */ public void setLatestEdition(long latestEdition) { if (!(latestEdition > this.latestEdition)) { @@ -377,7 +395,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the time of the last inserted update of this Sone. * * @param time - * The time of the update (in milliseconds since Jan 1, 1970 UTC) + * The time of the update (in milliseconds since Jan 1, 1970 UTC) * @return This Sone (for method chaining) */ public Sone setTime(long time) { @@ -398,21 +416,20 @@ public class Sone implements Fingerprintable, Comparable { * Sets the new status of this Sone. * * @param status - * The new status of this Sone + * The new status of this Sone * @return This Sone * @throws IllegalArgumentException - * if {@code status} is {@code null} + * if {@code status} is {@code null} */ public Sone setStatus(SoneStatus status) { - Validation.begin().isNotNull("Sone Status", status).check(); - this.status = status; + this.status = checkNotNull(status, "status must not be null"); return this; } /** - * Returns a copy of the profile. If you want to update values in the - * profile of this Sone, update the values in the returned {@link Profile} - * and use {@link #setProfile(Profile)} to change the profile in this Sone. + * Returns a copy of the profile. If you want to update values in the profile + * of this Sone, update the values in the returned {@link Profile} and use + * {@link #setProfile(Profile)} to change the profile in this Sone. * * @return A copy of the profile */ @@ -421,12 +438,12 @@ public class Sone implements Fingerprintable, Comparable { } /** - * Sets the profile of this Sone. A copy of the given profile is stored so - * that subsequent modifications of the given profile are not reflected in - * this Sone! + * Sets the profile of this Sone. A copy of the given profile is stored so that + * subsequent modifications of the given profile are not reflected in this + * Sone! * * @param profile - * The profile to set + * The profile to set */ public void setProfile(Profile profile) { this.profile = new Profile(profile); @@ -445,7 +462,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the client used by this Sone. * * @param client - * The client used by this Sone, or {@code null} + * The client used by this Sone, or {@code null} * @return This Sone (for method chaining) */ public Sone setClient(Client client) { @@ -466,7 +483,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets whether this Sone is known. * * @param known - * {@code true} if this Sone is known, {@code false} otherwise + * {@code true} if this Sone is known, {@code false} otherwise * @return This Sone */ public Sone setKnown(boolean known) { @@ -488,9 +505,9 @@ public class Sone implements Fingerprintable, Comparable { * Returns whether this Sone has the given Sone as a friend Sone. * * @param friendSoneId - * The ID of the Sone to check for - * @return {@code true} if this Sone has the given Sone as a friend, - * {@code false} otherwise + * The ID of the Sone to check for + * @return {@code true} if this Sone has the given Sone as a friend, {@code + * false} otherwise */ public boolean hasFriend(String friendSoneId) { return friendSones.contains(friendSoneId); @@ -500,7 +517,7 @@ public class Sone implements Fingerprintable, Comparable { * Adds the given Sone as a friend Sone. * * @param friendSone - * The friend Sone to add + * The friend Sone to add * @return This Sone (for method chaining) */ public Sone addFriend(String friendSone) { @@ -514,7 +531,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given Sone as a friend Sone. * * @param friendSoneId - * The ID of the friend Sone to remove + * The ID of the friend Sone to remove * @return This Sone (for method chaining) */ public Sone removeFriend(String friendSoneId) { @@ -540,7 +557,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets all posts of this Sone at once. * * @param posts - * The new (and only) posts of this Sone + * The new (and only) posts of this Sone * @return This Sone (for method chaining) */ public Sone setPosts(Collection posts) { @@ -552,11 +569,11 @@ public class Sone implements Fingerprintable, Comparable { } /** - * Adds the given post to this Sone. The post will not be added if its - * {@link Post#getSone() Sone} is not this Sone. + * Adds the given post to this Sone. The post will not be added if its {@link + * Post#getSone() Sone} is not this Sone. * * @param post - * The post to add + * The post to add */ public void addPost(Post post) { if (post.getSone().equals(this) && posts.add(post)) { @@ -568,7 +585,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given post from this Sone. * * @param post - * The post to remove + * The post to remove */ public void removePost(Post post) { if (post.getSone().equals(this)) { @@ -589,7 +606,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets all replies of this Sone at once. * * @param replies - * The new (and only) replies of this Sone + * The new (and only) replies of this Sone * @return This Sone (for method chaining) */ public Sone setReplies(Collection replies) { @@ -603,7 +620,7 @@ public class Sone implements Fingerprintable, Comparable { * nothing is added to this Sone. * * @param reply - * The reply to add + * The reply to add */ public void addReply(PostReply reply) { if (reply.getSone().equals(this)) { @@ -615,7 +632,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes a reply from this Sone. * * @param reply - * The reply to remove + * The reply to remove */ public void removeReply(PostReply reply) { if (reply.getSone().equals(this)) { @@ -636,7 +653,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the IDs of all liked posts. * * @param likedPostIds - * All liked posts’ IDs + * All liked posts’ IDs * @return This Sone (for method chaining) */ public Sone setLikePostIds(Set likedPostIds) { @@ -649,7 +666,7 @@ public class Sone implements Fingerprintable, Comparable { * Checks whether the given post ID is liked by this Sone. * * @param postId - * The ID of the post + * The ID of the post * @return {@code true} if this Sone likes the given post, {@code false} * otherwise */ @@ -661,7 +678,7 @@ public class Sone implements Fingerprintable, Comparable { * Adds the given post ID to the list of posts this Sone likes. * * @param postId - * The ID of the post + * The ID of the post * @return This Sone (for method chaining) */ public Sone addLikedPostId(String postId) { @@ -673,7 +690,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given post ID from the list of posts this Sone likes. * * @param postId - * The ID of the post + * The ID of the post * @return This Sone (for method chaining) */ public Sone removeLikedPostId(String postId) { @@ -694,7 +711,7 @@ public class Sone implements Fingerprintable, Comparable { * Sets the IDs of all liked replies. * * @param likedReplyIds - * All liked replies’ IDs + * All liked replies’ IDs * @return This Sone (for method chaining) */ public Sone setLikeReplyIds(Set likedReplyIds) { @@ -707,7 +724,7 @@ public class Sone implements Fingerprintable, Comparable { * Checks whether the given reply ID is liked by this Sone. * * @param replyId - * The ID of the reply + * The ID of the reply * @return {@code true} if this Sone likes the given reply, {@code false} * otherwise */ @@ -719,7 +736,7 @@ public class Sone implements Fingerprintable, Comparable { * Adds the given reply ID to the list of replies this Sone likes. * * @param replyId - * The ID of the reply + * The ID of the reply * @return This Sone (for method chaining) */ public Sone addLikedReplyId(String replyId) { @@ -731,7 +748,7 @@ public class Sone implements Fingerprintable, Comparable { * Removes the given post ID from the list of replies this Sone likes. * * @param replyId - * The ID of the reply + * The ID of the reply * @return This Sone (for method chaining) */ public Sone removeLikedReplyId(String replyId) { @@ -749,35 +766,14 @@ public class Sone implements Fingerprintable, Comparable { } /** - * Returns a flattened list of all albums of this Sone. The resulting list - * contains parent albums before child albums so that the resulting list can - * be parsed in a single pass. - * - * @return The flattened albums - */ - public List getAllAlbums() { - List flatAlbums = new ArrayList(); - flatAlbums.addAll(albums); - int lastAlbumIndex = 0; - while (lastAlbumIndex < flatAlbums.size()) { - int previousAlbumCount = flatAlbums.size(); - for (Album album : new ArrayList(flatAlbums.subList(lastAlbumIndex, flatAlbums.size()))) { - flatAlbums.addAll(album.getAlbums()); - } - lastAlbumIndex = previousAlbumCount; - } - return flatAlbums; - } - - /** - * Returns all images of a Sone. Images of a album are inserted into this - * list before images of all child albums. + * Returns all images of a Sone. Images of a album are inserted into this list + * before images of all child albums. * * @return The list of all images */ public List getAllImages() { List allImages = new ArrayList(); - for (Album album : getAllAlbums()) { + for (Album album : FluentIterable.from(getAlbums()).transformAndConcat(Album.FLATTENER).toList()) { allImages.addAll(album.getImages()); } return allImages; @@ -787,10 +783,11 @@ public class Sone implements Fingerprintable, Comparable { * Adds an album to this Sone. * * @param album - * The album to add + * The album to add */ public void addAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().equals(this), "album must belong to this Sone"); if (!albums.contains(album)) { albums.add(album); } @@ -800,10 +797,10 @@ public class Sone implements Fingerprintable, Comparable { * Sets the albums of this Sone. * * @param albums - * The albums of this Sone + * The albums of this Sone */ public void setAlbums(Collection albums) { - Validation.begin().isNotNull("Albums", albums).check(); + checkNotNull(albums, "albums must not be null"); this.albums.clear(); for (Album album : albums) { addAlbum(album); @@ -814,24 +811,27 @@ public class Sone implements Fingerprintable, Comparable { * Removes an album from this Sone. * * @param album - * The album to remove + * The album to remove */ public void removeAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().equals(this), "album must belong to this Sone"); albums.remove(album); } /** - * Moves the given album up in this album’s albums. If the album is already - * the first album, nothing happens. + * Moves the given album up in this album’s albums. If the album is already the + * first album, nothing happens. * * @param album - * The album to move up + * The album to move up * @return The album that the given album swapped the place with, or * null if the album did not change its place */ public Album moveAlbumUp(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).isNull("Album Parent", album.getParent()).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().equals(this), "album must belong to this Sone"); + checkArgument(album.getParent() == null, "album must not have a parent"); int oldIndex = albums.indexOf(album); if (oldIndex <= 0) { return null; @@ -842,16 +842,18 @@ public class Sone implements Fingerprintable, Comparable { } /** - * Moves the given album down in this album’s albums. If the album is - * already the last album, nothing happens. + * Moves the given album down in this album’s albums. If the album is already + * the last album, nothing happens. * * @param album - * The album to move down + * The album to move down * @return The album that the given album swapped the place with, or * null if the album did not change its place */ public Album moveAlbumDown(Album album) { - Validation.begin().isNotNull("Album", album).check().isEqual("Album Owner", album.getSone(), this).isNull("Album Parent", album.getParent()).check(); + checkNotNull(album, "album must not be null"); + checkArgument(album.getSone().equals(this), "album must belong to this Sone"); + checkArgument(album.getParent() == null, "album must not have a parent"); int oldIndex = albums.indexOf(album); if ((oldIndex < 0) || (oldIndex >= (albums.size() - 1))) { return null; @@ -870,64 +872,71 @@ public class Sone implements Fingerprintable, Comparable { return options; } + /** + * Sets the options of this Sone. + * + * @param options + * The options of this Sone + */ + /* TODO - remove this method again, maybe add an option provider */ + public void setOptions(Options options) { + this.options = options; + } + // // FINGERPRINTABLE METHODS // - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public synchronized String getFingerprint() { - StringBuilder fingerprint = new StringBuilder(); - fingerprint.append(profile.getFingerprint()); + Hasher hash = Hashing.sha256().newHasher(); + hash.putString(profile.getFingerprint()); - fingerprint.append("Posts("); + hash.putString("Posts("); for (Post post : getPosts()) { - fingerprint.append("Post(").append(post.getId()).append(')'); + hash.putString("Post(").putString(post.getId()).putString(")"); } - fingerprint.append(")"); + hash.putString(")"); List replies = new ArrayList(getReplies()); Collections.sort(replies, Reply.TIME_COMPARATOR); - fingerprint.append("Replies("); + hash.putString("Replies("); for (PostReply reply : replies) { - fingerprint.append("Reply(").append(reply.getId()).append(')'); + hash.putString("Reply(").putString(reply.getId()).putString(")"); } - fingerprint.append(')'); + hash.putString(")"); List likedPostIds = new ArrayList(getLikedPostIds()); Collections.sort(likedPostIds); - fingerprint.append("LikedPosts("); + hash.putString("LikedPosts("); for (String likedPostId : likedPostIds) { - fingerprint.append("Post(").append(likedPostId).append(')'); + hash.putString("Post(").putString(likedPostId).putString(")"); } - fingerprint.append(')'); + hash.putString(")"); List likedReplyIds = new ArrayList(getLikedReplyIds()); Collections.sort(likedReplyIds); - fingerprint.append("LikedReplies("); + hash.putString("LikedReplies("); for (String likedReplyId : likedReplyIds) { - fingerprint.append("Reply(").append(likedReplyId).append(')'); + hash.putString("Reply(").putString(likedReplyId).putString(")"); } - fingerprint.append(')'); + hash.putString(")"); - fingerprint.append("Albums("); + hash.putString("Albums("); for (Album album : albums) { - fingerprint.append(album.getFingerprint()); + hash.putString(album.getFingerprint()); } - fingerprint.append(')'); + hash.putString(")"); - return fingerprint.toString(); + return hash.hash().toString(); } // // INTERFACE Comparable // - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public int compareTo(Sone sone) { return NICE_NAME_COMPARATOR.compare(this, sone); @@ -937,17 +946,13 @@ public class Sone implements Fingerprintable, Comparable { // OBJECT METHODS // - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public int hashCode() { return id.hashCode(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public boolean equals(Object object) { if (!(object instanceof Sone)) { @@ -956,9 +961,7 @@ public class Sone implements Fingerprintable, Comparable { return ((Sone) object).id.equals(id); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public String toString() { return getClass().getName() + "[identity=" + identity + ",requestUri=" + requestUri + ",insertUri(" + String.valueOf(insertUri).length() + "),friends(" + friendSones.size() + "),posts(" + posts.size() + "),replies(" + replies.size() + ")]"; diff --git a/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java b/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java index 5e7c78d..5c657ac 100644 --- a/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java +++ b/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java @@ -1,5 +1,5 @@ /* - * Sone - TemporaryImage.java - Copyright © 2011–2012 David Roden + * Sone - TemporaryImage.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 @@ -17,9 +17,10 @@ package net.pterodactylus.sone.data; -import java.util.UUID; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; -import net.pterodactylus.util.validation.Validation; +import java.util.UUID; /** * A temporary image stores an uploaded image in memory until it has been @@ -82,7 +83,8 @@ public class TemporaryImage { * @return This temporary image */ public TemporaryImage setMimeType(String mimeType) { - Validation.begin().isNotNull("MIME Type", mimeType).isNull("Previous MIME Type", this.mimeType).check(); + checkNotNull(mimeType, "mimeType must not be null"); + checkState(this.mimeType == null, "mime type must not already be set"); this.mimeType = mimeType; return this; } @@ -105,7 +107,8 @@ public class TemporaryImage { * @return This temporary image */ public TemporaryImage setImageData(byte[] imageData) { - Validation.begin().isNotNull("Image Data", imageData).isNull("Previous Image Data", this.imageData).check(); + checkNotNull(imageData, "imageData must not be null"); + checkState(this.imageData == null, "image data must not already be set"); this.imageData = imageData; return this; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java new file mode 100644 index 0000000..3a56dc9 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java @@ -0,0 +1,170 @@ +/* + * Sone - AbstractPostBuilder.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 org.apache.commons.lang.StringUtils; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.SoneProvider; + +/** + * Abstract {@link PostBuilder} implementation. It stores the state of the new + * post and performs validation, you only need to implement {@link #build()}. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractPostBuilder implements PostBuilder { + + /** The Sone provider for the created posts. */ + protected final SoneProvider soneProvider; + + /** Wether to create a post with a random ID. */ + protected boolean randomId; + + /** The ID of the post. */ + protected String id; + + /** The sender of the post. */ + protected String senderId; + + /** Whether to use the current time when creating the post. */ + protected boolean currentTime; + + /** The time of the post. */ + protected long time; + + /** The text of the post. */ + protected String text; + + /** The (optional) recipient of the post. */ + protected String recipientId; + + /** + * Creates a new abstract post builder. + * + * @param soneProvider + * The Sone provider + */ + public AbstractPostBuilder(SoneProvider soneProvider) { + this.soneProvider = soneProvider; + } + + // + // POSTBUILDER METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder copyPost(Post post) { + this.randomId = false; + this.id = post.getId(); + this.senderId = post.getSone().getId(); + this.currentTime = false; + this.time = post.getTime(); + this.text = post.getText(); + this.recipientId = post.getRecipientId().orNull(); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder randomId() { + randomId = true; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder withId(String id) { + this.id = id; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder from(String senderId) { + this.senderId = senderId; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder currentTime() { + currentTime = true; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder withTime(long time) { + this.time = time; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder withText(String text) { + this.text = text; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder to(String recipientId) { + this.recipientId = recipientId; + return this; + } + + // + // PROTECTED METHODS + // + + /** + * Validates the state of this post builder. + * + * @throws IllegalStateException + * if the state is not valid for building a new post + */ + protected void validate() throws IllegalStateException { + checkState((randomId && (id == null)) || (!randomId && (id != null)), "exactly one of random ID or custom ID must be set"); + checkState(senderId != null, "sender must not be null"); + checkState((currentTime && (time == 0)) || (!currentTime && (time > 0)), "one of current time or custom time must be set"); + checkState(!StringUtils.isBlank(text), "text must not be empty"); + checkState((recipientId == null) || !recipientId.equals(senderId), "sender and recipient must not be the same"); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java new file mode 100644 index 0000000..952a94e --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java @@ -0,0 +1,65 @@ +/* + * Sone - AbstractPostReplyBuilder.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 org.apache.commons.lang.StringUtils; + +import net.pterodactylus.sone.database.PostReplyBuilder; + +/** + * Abstract {@link PostReplyBuilder} implementation. It stores the state of the + * new post and performs validation, implementations only need to implement + * {@link #build()}. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractPostReplyBuilder extends AbstractReplyBuilder implements PostReplyBuilder { + + /** The ID of the post the created reply refers to. */ + protected String postId; + + /** + * {@inheritDoc} + */ + @Override + public PostReplyBuilder to(String postId) { + this.postId = postId; + return this; + } + + // + // PROTECTED METHODS + // + + /** + * Validates the state of this post reply builder. + * + * @throws IllegalStateException + * if the state is not valid for building a new post reply + */ + protected void validate() throws IllegalStateException { + checkState((randomId && (id == null)) || (!randomId && (id != null)), "either random ID nor custom ID must be set"); + checkState(senderId != null, "sender must not be null"); + checkState((currentTime && (time == 0)) || (!currentTime && (time >= 0)), "either current time or custom time must be set"); + checkState(!StringUtils.isBlank(text), "text must not be empty"); + checkState(postId != null, "post must not be null"); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java new file mode 100644 index 0000000..2a68a13 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java @@ -0,0 +1,109 @@ +/* + * Sone - ReplyBuilder.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.database.ReplyBuilder; + +/** + * Abstract implementation of a {@link ReplyBuilder}. + * + * @param + * The interface implemented and exposed by the builder + * @author David ‘Bombe’ Roden + */ +public class AbstractReplyBuilder> implements ReplyBuilder { + + /** Whether to use a random ID for the reply. */ + protected boolean randomId; + + /** The ID of the reply. */ + protected String id; + + /** The sender of the reply. */ + protected String senderId; + + /** Whether to use the current time when creating the reply. */ + protected boolean currentTime; + + /** The time of the reply. */ + protected long time; + + /** The text of the reply. */ + protected String text; + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B randomId() { + this.randomId = true; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B withId(String id) { + this.id = id; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B from(String senderId) { + this.senderId = senderId; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B currentTime() { + this.currentTime = true; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B withTime(long time) { + this.time = time; + return (B) this; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public B withText(String text) { + this.text = text; + return (B) this; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java new file mode 100644 index 0000000..4df8897 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostBuilderFactory.java @@ -0,0 +1,56 @@ +/* + * Sone - DefaultPostBuilderFactory.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.database.PostBuilder; +import net.pterodactylus.sone.database.PostBuilderFactory; +import net.pterodactylus.sone.database.SoneProvider; + +import com.google.inject.Inject; + +/** + * {@link PostBuilderFactory} implementation that creates + * {@link PostBuilderImpl}s. + * + * @author David ‘Bombe’ Roden + */ +public class DefaultPostBuilderFactory implements PostBuilderFactory { + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** + * Creates a new default post builder factory. + * + * @param soneProvider + * The Sone provider + */ + @Inject + public DefaultPostBuilderFactory(SoneProvider soneProvider) { + this.soneProvider = soneProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public PostBuilder newPostBuilder() { + return new PostBuilderImpl(soneProvider); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostReplyBuilderFactory.java b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostReplyBuilderFactory.java new file mode 100644 index 0000000..e326304 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/DefaultPostReplyBuilderFactory.java @@ -0,0 +1,62 @@ +/* + * Sone - DefaultPostReplyBuilderFactory.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.database.PostProvider; +import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.PostReplyBuilderFactory; +import net.pterodactylus.sone.database.SoneProvider; + +import com.google.inject.Inject; + +/** + * {@link PostReplyBuilderFactory} that creates {@link PostReplyBuilderImpl}s. + * + * @author David ‘Bombe’ Roden + */ +public class DefaultPostReplyBuilderFactory implements PostReplyBuilderFactory { + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The post provider. */ + private final PostProvider postProvider; + + /** + * Creates a new default post reply builder factory. + * + * @param soneProvider + * The Sone provider + * @param postProvider + * The post provider + */ + @Inject + public DefaultPostReplyBuilderFactory(SoneProvider soneProvider, PostProvider postProvider) { + this.soneProvider = soneProvider; + this.postProvider = postProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public PostReplyBuilder newPostReplyBuilder() { + return new PostReplyBuilderImpl(soneProvider, postProvider); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java new file mode 100644 index 0000000..88a2d71 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostBuilderImpl.java @@ -0,0 +1,52 @@ +/* + * Sone - PostBuilderImpl.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 java.util.UUID; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.SoneProvider; + +/** + * {@link PostBuilder} implementation that creates {@link PostImpl} objects. + * + * @author David ‘Bombe’ Roden + */ +public class PostBuilderImpl extends AbstractPostBuilder { + + /** + * Creates a new post builder. + * + * @param soneProvider + * The Sone provider + */ + public PostBuilderImpl(SoneProvider soneProvider) { + super(soneProvider); + } + + /** + * {@inheritDoc} + */ + @Override + public Post build() { + validate(); + return new PostImpl(soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java index 2269738..2d25715 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostImpl.java @@ -1,5 +1,5 @@ /* - * Sone - PostImpl.java - Copyright © 2010–2012 David Roden + * Sone - PostImpl.java - Copyright © 2010–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 @@ -21,6 +21,9 @@ import java.util.UUID; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.SoneProvider; + +import com.google.common.base.Optional; /** * A post is a short message that a user writes in his Sone to let other users @@ -30,20 +33,23 @@ import net.pterodactylus.sone.data.Sone; */ public class PostImpl implements Post { + /** The Sone provider. */ + private final SoneProvider soneProvider; + /** The GUID of the post. */ private final UUID id; - /** The Sone this post belongs to. */ - private volatile Sone sone; + /** The ID of the owning Sone. */ + private final String soneId; - /** The Sone of the recipient. */ - private volatile Sone recipient; + /** The ID of the recipient Sone. */ + private final String recipientId; /** The time of the post (in milliseconds since Jan 1, 1970 UTC). */ - private volatile long time; + private final long time; /** The text of the post. */ - private volatile String text; + private final String text; /** Whether the post is known. */ private volatile boolean known; @@ -51,54 +57,24 @@ public class PostImpl implements Post { /** * Creates a new post. * + * @param soneProvider + * The Sone provider * @param id * The ID of the post - */ - public PostImpl(String id) { - this(id, null, 0, null); - } - - /** - * Creates a new post. - * - * @param sone - * The Sone this post belongs to - * @param text - * The text of the post - */ - public PostImpl(Sone sone, String text) { - this(sone, System.currentTimeMillis(), text); - } - - /** - * Creates a new post. - * - * @param sone - * The Sone this post belongs to - * @param time - * The time of the post (in milliseconds since Jan 1, 1970 UTC) - * @param text - * The text of the post - */ - public PostImpl(Sone sone, long time, String text) { - this(UUID.randomUUID().toString(), sone, time, text); - } - - /** - * Creates a new post. - * - * @param id - * The ID of the post - * @param sone - * The Sone this post belongs to + * @param soneId + * The ID of the Sone this post belongs to + * @param recipientId + * The ID of the recipient of the post * @param time * The time of the post (in milliseconds since Jan 1, 1970 UTC) * @param text * The text of the post */ - public PostImpl(String id, Sone sone, long time, String text) { + public PostImpl(SoneProvider soneProvider, String id, String soneId, String recipientId, long time, String text) { + this.soneProvider = soneProvider; this.id = UUID.fromString(id); - this.sone = sone; + this.soneId = soneId; + this.recipientId = recipientId; this.time = time; this.text = text; } @@ -120,35 +96,23 @@ public class PostImpl implements Post { */ @Override public Sone getSone() { - return sone; - } - - /** - * {@inheritDoc} - */ - @Override - public PostImpl setSone(Sone sone) { - this.sone = sone; - return this; + return soneProvider.getSone(soneId).get(); } /** - * {@inheritDoc} + * {@inheritDocs} */ @Override - public Sone getRecipient() { - return recipient; + public Optional getRecipientId() { + return Optional.fromNullable(recipientId); } /** * {@inheritDoc} */ @Override - public PostImpl setRecipient(Sone recipient) { - if (!sone.equals(recipient)) { - this.recipient = recipient; - } - return this; + public Optional getRecipient() { + return soneProvider.getSone(recipientId); } /** @@ -163,15 +127,6 @@ public class PostImpl implements Post { * {@inheritDoc} */ @Override - public PostImpl setTime(long time) { - this.time = time; - return this; - } - - /** - * {@inheritDoc} - */ - @Override public String getText() { return text; } @@ -180,15 +135,6 @@ public class PostImpl implements Post { * {@inheritDoc} */ @Override - public PostImpl setText(String text) { - this.text = text; - return this; - } - - /** - * {@inheritDoc} - */ - @Override public boolean isKnown() { return known; } @@ -231,7 +177,7 @@ public class PostImpl implements Post { */ @Override public String toString() { - return getClass().getName() + "[id=" + id + ",sone=" + sone + ",time=" + time + ",text=" + text + "]"; + return String.format("%s[id=%s,sone=%s,recipient=%s,time=%d,text=%s]", getClass().getName(), id, soneId, recipientId, time, text); } } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostReplyBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyBuilderImpl.java new file mode 100644 index 0000000..dac84ad --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyBuilderImpl.java @@ -0,0 +1,73 @@ +/* + * Sone - PostReplyBuilderImpl.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 java.util.UUID; + +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.database.PostProvider; +import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneProvider; + +import org.apache.commons.lang.StringUtils; + +/** + * {@link PostReplyBuilder} implementation that creates {@link PostReplyImpl} + * objects. + * + * @author David ‘Bombe’ Roden + */ +public class PostReplyBuilderImpl extends AbstractPostReplyBuilder { + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The post provider. */ + private final PostProvider postProvider; + + /** + * Creates a new post reply builder. + * + * @param soneProvider + * The Sone provider + * @param postProvider + * The post provider + */ + public PostReplyBuilderImpl(SoneProvider soneProvider, PostProvider postProvider) { + this.soneProvider = soneProvider; + this.postProvider = postProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public PostReply build() { + checkState((randomId && (id == null)) || (!randomId && (id != null)), "either random ID nor custom ID must be set"); + checkState(senderId != null, "sender must not be null"); + checkState((currentTime && (time == 0)) || (!currentTime && (time >= 0)), "either current time or custom time must be set"); + checkState(!StringUtils.isBlank(text), "text must not be empty"); + checkState(postId != null, "post must not be null"); + + /* create new post reply. */ + return new PostReplyImpl(soneProvider, postProvider, randomId ? UUID.randomUUID().toString() : id, senderId, currentTime ? System.currentTimeMillis() : time, text, postId); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java new file mode 100644 index 0000000..30badf7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/PostReplyImpl.java @@ -0,0 +1,84 @@ +/* + * Sone - PostReplyImpl.java - Copyright © 2010–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.Post; +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.database.PostProvider; +import net.pterodactylus.sone.database.SoneProvider; + +import com.google.common.base.Optional; + +/** + * Simple {@link PostReply} implementation. + * + * @author David ‘Bombe’ Roden + */ +public class PostReplyImpl extends ReplyImpl implements PostReply { + + /** The post provider. */ + private final PostProvider postProvider; + + /** The Post this reply refers to. */ + private final String postId; + + /** + * Creates a new reply. + * + * @param soneProvider + * The Sone provider + * @param postProvider + * The post provider + * @param id + * The ID of the reply + * @param soneId + * The ID of the Sone of the reply + * @param time + * The time of the reply + * @param text + * The text of the reply + * @param postId + * The ID of the post this reply refers to + */ + public PostReplyImpl(SoneProvider soneProvider, PostProvider postProvider, String id, String soneId, long time, String text, String postId) { + super(soneProvider, id, soneId, time, text); + this.postProvider = postProvider; + this.postId = postId; + } + + // + // ACCESSORS + // + + /** + * {@inheritDocs} + */ + @Override + public String getPostId() { + return postId; + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getPost() { + return postProvider.getPost(postId); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java new file mode 100644 index 0000000..a67081f --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/ReplyImpl.java @@ -0,0 +1,155 @@ +/* + * Sone - ReplyImpl.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.impl; + +import net.pterodactylus.sone.data.Reply; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.SoneProvider; + +/** + * Abstract base class for all replies. + * + * @param + * The type of the reply + * @author David ‘Bombe’ Roden + */ +public abstract class ReplyImpl> implements Reply { + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The ID of the reply. */ + private final String id; + + /** The Sone that created this reply. */ + private final String soneId; + + /** The time of the reply. */ + private final long time; + + /** The text of the reply. */ + private final String text; + + /** Whether the reply is known. */ + private volatile boolean known; + + /** + * Creates a new reply. + * + * @param soneProvider + * The Sone provider + * @param id + * The ID of the reply + * @param soneId + * The ID of the Sone of the reply + * @param time + * The time of the reply + * @param text + * The text of the reply + */ + protected ReplyImpl(SoneProvider soneProvider, String id, String soneId, long time, String text) { + this.soneProvider = soneProvider; + this.id = id; + this.soneId = soneId; + this.time = time; + this.text = text; + } + + /** + * {@inheritDoc} + */ + @Override + public String getId() { + return id; + } + + /** + * {@inheritDoc} + */ + @Override + public Sone getSone() { + return soneProvider.getSone(soneId).get(); + } + + /** + * {@inheritDoc} + */ + @Override + public long getTime() { + return time; + } + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return text; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isKnown() { + return known; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public T setKnown(boolean known) { + this.known = known; + return (T) this; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof Reply)) { + return false; + } + Reply reply = (Reply) object; + return reply.getId().equals(id); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("%s[id=%s,sone=%s,time=%d,text=%s]", getClass().getName(), id, soneId, time, text); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/Database.java b/src/main/java/net/pterodactylus/sone/database/Database.java new file mode 100644 index 0000000..c93c061 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/Database.java @@ -0,0 +1,39 @@ +/* + * Sone - Database.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 com.google.common.util.concurrent.Service; + +/** + * Database for Sone data. This interface combines the various provider, store, + * and builder factory interfaces into a single interface and adds some methods + * necessary for lifecycle management. + * + * @author David ‘Bombe’ Roden + */ +public interface Database extends Service, PostDatabase, PostReplyDatabase { + + /** + * Saves the database. + * + * @throws DatabaseException + * if an error occurs while saving + */ + public void save() throws DatabaseException; + +} diff --git a/src/main/java/net/pterodactylus/sone/database/DatabaseException.java b/src/main/java/net/pterodactylus/sone/database/DatabaseException.java new file mode 100644 index 0000000..43d3e77 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/DatabaseException.java @@ -0,0 +1,66 @@ +/* + * Sone - DatabaseException.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; + +/** + * Exception that signals a database error. + * + * @author David ‘Bombe’ Roden + */ +public class DatabaseException extends Exception { + + /** + * Creates a new database exception. + */ + public DatabaseException() { + super(); + } + + /** + * Creates a new database exception. + * + * @param message + * The message of the exception + */ + public DatabaseException(String message) { + super(message); + } + + /** + * Creates a new database exception. + * + * @param cause + * The cause of the exception + */ + public DatabaseException(Throwable cause) { + super(cause); + } + + /** + * Creates a new database exception. + * + * @param message + * The message of the exception + * @param cause + * The cause of the exception + */ + public DatabaseException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostBuilder.java b/src/main/java/net/pterodactylus/sone/database/PostBuilder.java new file mode 100644 index 0000000..449147a --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostBuilder.java @@ -0,0 +1,144 @@ +/* + * Sone - PostBuilder.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.Post; +import net.pterodactylus.sone.data.Sone; + +/** + * Builder for {@link Post} objects. + *

+ * A {@link Post} consists of the following elements: + *

    + *
  • an ID,
  • + *
  • a {@link Sone sender},
  • + *
  • an optional {@link Sone recipient},
  • + *
  • a time,
  • + *
  • and a text.
  • + *
+ * Except for the recipient, all this elements have to be configured on this + * builder. For the ID you have the possibility to configure either a random ID + * (which should be used for new posts) or a custom ID you specify (for creating + * an existing post). For the time you can use the current time (again, for + * creating new posts) or the given time (for loading posts). It is an error to + * specify both ways for either the ID or the time. + * + * @author David ‘Bombe’ Roden + */ +public interface PostBuilder { + + /** + * Copies all attributes of the given post to this post builder. + * + * @param post + * The post whose attributes to copy into this builder + * @return This builder + * @throws NullPointerException + * if {@code post} is {@code null} + */ + public PostBuilder copyPost(Post post) throws NullPointerException; + + /** + * Configures this builder to use the given Sone as sender of the new post. + * + * @param senderId + * The ID of the sender of the post + * @return This post builder + */ + public PostBuilder from(String senderId); + + /** + * Configures this builder to use a random ID for the new post. If this + * method is used, {@link #withId(String)} must not be used. + * + * @return This post builder + */ + public PostBuilder randomId(); + + /** + * Configures this builder to use the given ID as ID for the new post. If + * this method is used, {@link #randomId()} must not be used. + * + * @param id + * The ID to use for the post + * @return This post builder + */ + public PostBuilder withId(String id); + + /** + * Configures this builder to use the current time when creating the post. + * If this method is used, {@link #withTime(long)} must not be used. + * + * @return This post builder + */ + public PostBuilder currentTime(); + + /** + * Configures the builder to use the given time as time for the new post. If + * this method is used, {@link #currentTime()} must not be used. + * + * @param time + * The time to use for the post + * @return This post builder + */ + public PostBuilder withTime(long time); + + /** + * Configures the builder to use the given text for the new post. + * + * @param text + * The text to use for the post + * @return This post builder + */ + public PostBuilder withText(String text); + + /** + * Configures the builder to use the given {@link Sone} as recipient for the + * post. + * + * @param recipientId + * The ID of the recipient of the post + * @return This post builder + */ + public PostBuilder to(String recipientId); + + /** + * Verifies this builder’s configuration and creates a new post. + *

+ * The following conditions must be met in order for this builder to be + * configured correctly: + *

    + *
  • Exactly one of {@link #randomId()} or {@link #withId(String)} must + * have been called.
  • + *
  • The {@link #from(String) sender} must not be {@code null}.
  • + *
  • Exactly one of {@link #currentTime()} or {@link #withTime(long)} must + * have been called.
  • + *
  • The {@link #withText(String) text} must not be {@code null} and must + * contain something other than whitespace.
  • + *
  • The {@link #to(String) recipient} must either not have been set, or + * it must have been set to a {@link Sone} other than {@link #from(String) + * the sender}.
  • + *
+ * + * @return A new post + * @throws IllegalStateException + * if this builder’s configuration is not valid + */ + public Post build() throws IllegalStateException; + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostBuilderFactory.java b/src/main/java/net/pterodactylus/sone/database/PostBuilderFactory.java new file mode 100644 index 0000000..b89ae28 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostBuilderFactory.java @@ -0,0 +1,34 @@ +/* + * Sone - PostBuilderFactory.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 PostBuilder}s. + * + * @author David ‘Bombe’ Roden + */ +public interface PostBuilderFactory { + + /** + * Creates a new post builder. + * + * @return A new post builder + */ + public PostBuilder newPostBuilder(); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostDatabase.java b/src/main/java/net/pterodactylus/sone/database/PostDatabase.java new file mode 100644 index 0000000..40e6290 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostDatabase.java @@ -0,0 +1,30 @@ +/* + * Sone - PostDatabase.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 a {@link PostProvider}, a {@link PostBuilderFactory}, and a + * {@link PostStore} into a complete post database. + * + * @author David ‘Bombe’ Roden + */ +public interface PostDatabase extends PostProvider, PostBuilderFactory, PostStore { + + /* nothing here. */ + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostProvider.java b/src/main/java/net/pterodactylus/sone/database/PostProvider.java new file mode 100644 index 0000000..740373e --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostProvider.java @@ -0,0 +1,61 @@ +/* + * Sone - PostProvider.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.database; + +import java.util.Collection; + +import net.pterodactylus.sone.data.Post; + +import com.google.common.base.Optional; + +/** + * Interface for objects that can provide {@link Post}s by their ID. + * + * @author David ‘Bombe’ Roden + */ +public interface PostProvider { + + /** + * Returns the post with the given ID. + * + * @param postId + * The ID of the post to return + * @return The post with the given ID, or {@code null} + */ + public Optional getPost(String postId); + + /** + * Returns all posts from the given Sone. + * + * @param soneId + * The ID of the Sone + * @return All posts from the given Sone + */ + public Collection getPosts(String soneId); + + /** + * Returns all posts that have the given Sone as recipient. + * + * @see Post#getRecipient() + * @param recipientId + * The ID of the recipient of the posts + * @return All posts that have the given Sone as recipient + */ + public Collection getDirectedPosts(String recipientId); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyBuilder.java b/src/main/java/net/pterodactylus/sone/database/PostReplyBuilder.java new file mode 100644 index 0000000..c031443 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostReplyBuilder.java @@ -0,0 +1,61 @@ +/* + * Sone - PostReplyBuilder.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.PostReply; + +/** + * Builder for a {@link PostReply} object. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyBuilder extends ReplyBuilder { + + /** + * Configures this builder to set the given post as post the created reply + * refers to. + * + * @param postId + * The ID of the post the reply refers to + * @return This builder + */ + public PostReplyBuilder to(String postId); + + /** + * Verifies the configuration of this builder and creates a new post reply. + *

+ * The following conditions must be met in order for the configuration to be + * considered valid: + *

    + *
  • Exactly one of {@link #randomId()} or {@link #withId(String)} must + * have been called.
  • + *
  • The {@link #from(String) sender} must not be {@code null}.
  • + *
  • Exactly one of {@link #currentTime()} or {@link #withTime(long)} must + * have been called.
  • + *
  • The {@link #withText(String) text} must not be {@code null} and must + * contain something other than whitespace.
  • + *
  • The {@link #to(String) post} have been set.
  • + *
+ * + * @return The created post reply + * @throws IllegalStateException + * if this builder’s configuration is not valid + */ + public PostReply build() throws IllegalStateException; + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyBuilderFactory.java b/src/main/java/net/pterodactylus/sone/database/PostReplyBuilderFactory.java new file mode 100644 index 0000000..7fd4ae1 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostReplyBuilderFactory.java @@ -0,0 +1,34 @@ +/* + * Sone - PostReplyBuilderFactory.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 PostReplyBuilder}s. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyBuilderFactory { + + /** + * Creates a new post reply builder. + * + * @return A new post reply builder + */ + public PostReplyBuilder newPostReplyBuilder(); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyDatabase.java b/src/main/java/net/pterodactylus/sone/database/PostReplyDatabase.java new file mode 100644 index 0000000..c9a809f --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostReplyDatabase.java @@ -0,0 +1,30 @@ +/* + * Sone - PostReplyDatabase.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 a {@link PostReplyProvider}, a {@link PostReplyBuilderFactory}, and + * a {@link PostReplyStore} into a complete post reply database. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyDatabase extends PostReplyProvider, PostReplyBuilderFactory, PostReplyStore { + + /* nothing here. */ + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyProvider.java b/src/main/java/net/pterodactylus/sone/database/PostReplyProvider.java new file mode 100644 index 0000000..e186e5b --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostReplyProvider.java @@ -0,0 +1,51 @@ +/* + * Sone - PostReplyProvider.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 java.util.List; + +import net.pterodactylus.sone.data.PostReply; + +import com.google.common.base.Optional; + +/** + * Interface for objects that can provide {@link PostReply}s. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyProvider { + + /** + * Returns the reply with the given ID. + * + * @param id + * The ID of the reply to get + * @return The reply, or {@code null} if there is no such reply + */ + public Optional getPostReply(String id); + + /** + * Returns all replies for the given post, order ascending by time. + * + * @param postId + * The ID of the post to get all replies for + * @return All replies for the given post + */ + public List getReplies(String postId); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostReplyStore.java b/src/main/java/net/pterodactylus/sone/database/PostReplyStore.java new file mode 100644 index 0000000..a3cefb3 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostReplyStore.java @@ -0,0 +1,69 @@ +/* + * Sone - PostReplyStore.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 java.util.Collection; + +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.data.Sone; + +/** + * Defines a store for {@link PostReply post replies}. + * + * @author David ‘Bombe’ Roden + */ +public interface PostReplyStore { + + /** + * Stores the given post reply. + * + * @param postReply + * The post reply + */ + public void storePostReply(PostReply postReply); + + /** + * Stores the given post replies as exclusive collection of post replies for + * the given Sone. This will remove all other post replies from this Sone! + * + * @param sone + * The Sone to store all post replies for + * @param postReplies + * The post replies of the Sone + * @throws IllegalArgumentException + * if one of the replies does not belong to the given Sone + */ + public void storePostReplies(Sone sone, Collection postReplies) throws IllegalArgumentException; + + /** + * Removes the given post reply from this store. + * + * @param postReply + * The post reply to remove + */ + public void removePostReply(PostReply postReply); + + /** + * Removes all post replies of the given Sone. + * + * @param sone + * The Sone to remove all post replies for + */ + public void removePostReplies(Sone sone); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/PostStore.java b/src/main/java/net/pterodactylus/sone/database/PostStore.java new file mode 100644 index 0000000..9c2ca42 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/PostStore.java @@ -0,0 +1,69 @@ +/* + * Sone - PostStore.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 java.util.Collection; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.Sone; + +/** + * Interface for a store for posts. + * + * @author David ‘Bombe’ Roden + */ +public interface PostStore { + + /** + * Adds the given post to the store. + * + * @param post + * The post to store + */ + public void storePost(Post post); + + /** + * Removes the given post. + * + * @param post + * The post to remove + */ + public void removePost(Post post); + + /** + * Stores the given posts as all posts of a single {@link Sone}. This method + * will removed all other posts from the Sone! + * + * @param sone + * The Sone to store the posts for + * @param posts + * The posts to store + * @throws IllegalArgumentException + * if posts do not all belong to the same Sone + */ + public void storePosts(Sone sone, Collection posts) throws IllegalArgumentException; + + /** + * Removes all posts of the given {@link Sone} + * + * @param sone + * The Sone to remove all posts for + */ + public void removePosts(Sone sone); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/ReplyBuilder.java b/src/main/java/net/pterodactylus/sone/database/ReplyBuilder.java new file mode 100644 index 0000000..d83e7ce --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/ReplyBuilder.java @@ -0,0 +1,88 @@ +/* + * Sone - ReplyBuilder.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.Reply; +import net.pterodactylus.sone.data.Sone; + +/** + * Methods that all reply builders need to implement in order to be able to + * create any kind of {@link Reply}. + * + * @param + * The type of the builder + * @author David ‘Bombe’ Roden + */ +public interface ReplyBuilder> { + + /** + * Configures this builder to use a random ID when creating the reply. If + * this method is used, {@link #withId(String)} must not be used. + * + * @return This builder + */ + public B randomId(); + + /** + * Configures this builder to use the given ID when creating the reply. If + * this method is used, {@link #randomId()} must not be used. + * + * @param id + * The ID of the reply + * @return This builder + */ + public B withId(String id); + + /** + * Configures this builder to use the ID of the given {@link Sone} as sender + * of the reply. + * + * @param senderId + * The ID of the sender of the reply + * @return This builder + */ + public B from(String senderId); + + /** + * Configures this builder to use the current time when creating the reply. + * If this method is used, {@link #withTime(long)} must not be used. + * + * @return This builder + */ + public B currentTime(); + + /** + * Configures this builder to use the given time when creating the reply. If + * this method is used, {@link #currentTime()} must not be used. + * + * @param time + * The time of the reply + * @return This builder + */ + public B withTime(long time); + + /** + * Configures this builder to use the given text when creating the reply. + * + * @param text + * The text of the reply + * @return This builder + */ + public B withText(String text); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/SoneProvider.java b/src/main/java/net/pterodactylus/sone/database/SoneProvider.java new file mode 100644 index 0000000..993804f --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/SoneProvider.java @@ -0,0 +1,64 @@ +/* + * Sone - SoneProvider.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.database; + +import java.util.Collection; + +import net.pterodactylus.sone.data.Sone; + +import com.google.common.base.Optional; + +/** + * Interface for objects that can provide {@link Sone}s by their ID. + * + * @author David ‘Bombe’ Roden + */ +public interface SoneProvider { + + /** + * Returns the Sone with the given ID, or {@link Optional#absent()} if it + * does not exist. + * + * @param soneId + * The ID of the Sone to return + * @return The Sone with the given ID, or {@link Optional#absent()} + */ + public Optional getSone(String soneId); + + /** + * Returns all Sones. + * + * @return All Sones + */ + public Collection getSones(); + + /** + * Returns all local Sones. + * + * @return All local Sones + */ + public Collection getLocalSones(); + + /** + * Returns all remote Sones. + * + * @return All remote Sones + */ + public Collection getRemoteSones(); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java new file mode 100644 index 0000000..3f30d11 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java @@ -0,0 +1,654 @@ +/* + * Sone - MemoryDatabase.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.memory; + +import static com.google.common.base.Preconditions.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +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.database.Database; +import net.pterodactylus.sone.database.DatabaseException; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.PostDatabase; +import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneProvider; +import net.pterodactylus.util.config.Configuration; +import net.pterodactylus.util.config.ConfigurationException; + +import com.google.common.base.Optional; +import com.google.common.collect.SortedSetMultimap; +import com.google.common.collect.TreeMultimap; +import com.google.common.util.concurrent.AbstractService; +import com.google.inject.Inject; + +/** + * Memory-based {@link PostDatabase} implementation. + * + * @author David ‘Bombe’ Roden + */ +public class MemoryDatabase extends AbstractService implements Database { + + /** The lock. */ + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The configuration. */ + private final Configuration configuration; + + /** All posts by their ID. */ + private final Map allPosts = new HashMap(); + + /** All posts by their Sones. */ + private final Map> sonePosts = new HashMap>(); + + /** All posts by their recipient. */ + private final Map> recipientPosts = new HashMap>(); + + /** Whether posts are known. */ + private final Set knownPosts = new HashSet(); + + /** All post replies by their ID. */ + private final Map allPostReplies = new HashMap(); + + /** Replies sorted by Sone. */ + private final SortedSetMultimap sonePostReplies = TreeMultimap.create(new Comparator() { + + @Override + public int compare(String leftString, String rightString) { + return leftString.compareTo(rightString); + } + }, PostReply.TIME_COMPARATOR); + + /** Replies by post. */ + private final Map> postReplies = new HashMap>(); + + /** Whether post replies are known. */ + private final Set knownPostReplies = new HashSet(); + + /** + * Creates a new memory database. + * + * @param soneProvider + * The Sone provider + * @param configuration + * The configuration for loading and saving elements + */ + @Inject + public MemoryDatabase(SoneProvider soneProvider, Configuration configuration) { + this.soneProvider = soneProvider; + this.configuration = configuration; + } + + // + // DATABASE METHODS + // + + /** + * Saves the database. + * + * @throws DatabaseException + * if an error occurs while saving + */ + @Override + public void save() throws DatabaseException { + saveKnownPosts(); + saveKnownPostReplies(); + } + + // + // SERVICE METHODS + // + + /** {@inheritDocs} */ + @Override + protected void doStart() { + loadKnownPosts(); + loadKnownPostReplies(); + notifyStarted(); + } + + /** {@inheritDocs} */ + @Override + protected void doStop() { + try { + save(); + notifyStopped(); + } catch (DatabaseException de1) { + notifyFailed(de1); + } + } + + // + // POSTPROVIDER METHODS + // + + /** {@inheritDocs} */ + @Override + public Optional getPost(String postId) { + lock.readLock().lock(); + try { + return Optional.fromNullable(allPosts.get(postId)); + } finally { + lock.readLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public Collection getPosts(String soneId) { + return new HashSet(getPostsFrom(soneId)); + } + + /** {@inheritDocs} */ + @Override + public Collection getDirectedPosts(String recipientId) { + lock.readLock().lock(); + try { + Collection posts = recipientPosts.get(recipientId); + return (posts == null) ? Collections.emptySet() : new HashSet(posts); + } finally { + lock.readLock().unlock(); + } + } + + // + // POSTBUILDERFACTORY METHODS + // + + /** {@inheritDocs} */ + @Override + public PostBuilder newPostBuilder() { + return new MemoryPostBuilder(this, soneProvider); + } + + // + // POSTSTORE METHODS + // + + /** {@inheritDocs} */ + @Override + public void storePost(Post post) { + checkNotNull(post, "post must not be null"); + lock.writeLock().lock(); + try { + allPosts.put(post.getId(), post); + getPostsFrom(post.getSone().getId()).add(post); + if (post.getRecipientId().isPresent()) { + getPostsTo(post.getRecipientId().get()).add(post); + } + } finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public void removePost(Post post) { + checkNotNull(post, "post must not be null"); + lock.writeLock().lock(); + try { + allPosts.remove(post.getId()); + getPostsFrom(post.getSone().getId()).remove(post); + if (post.getRecipientId().isPresent()) { + getPostsTo(post.getRecipientId().get()).remove(post); + } + post.getSone().removePost(post); + } finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public void storePosts(Sone sone, Collection posts) throws IllegalArgumentException { + checkNotNull(sone, "sone must not be null"); + /* verify that all posts are from the same Sone. */ + for (Post post : posts) { + if (!sone.equals(post.getSone())) { + throw new IllegalArgumentException(String.format("Post from different Sone found: %s", post)); + } + } + + lock.writeLock().lock(); + try { + /* remove all posts by the Sone. */ + getPostsFrom(sone.getId()).clear(); + for (Post post : posts) { + allPosts.remove(post.getId()); + if (post.getRecipientId().isPresent()) { + getPostsTo(post.getRecipientId().get()).remove(post); + } + } + + /* add new posts. */ + getPostsFrom(sone.getId()).addAll(posts); + for (Post post : posts) { + allPosts.put(post.getId(), post); + if (post.getRecipientId().isPresent()) { + getPostsTo(post.getRecipientId().get()).add(post); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public void removePosts(Sone sone) { + checkNotNull(sone, "sone must not be null"); + lock.writeLock().lock(); + try { + /* remove all posts by the Sone. */ + getPostsFrom(sone.getId()).clear(); + for (Post post : sone.getPosts()) { + allPosts.remove(post.getId()); + if (post.getRecipientId().isPresent()) { + getPostsTo(post.getRecipientId().get()).remove(post); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + // + // POSTREPLYPROVIDER METHODS + // + + /** {@inheritDocs} */ + @Override + public Optional getPostReply(String id) { + lock.readLock().lock(); + try { + return Optional.fromNullable(allPostReplies.get(id)); + } finally { + lock.readLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public List getReplies(String postId) { + lock.readLock().lock(); + try { + if (!postReplies.containsKey(postId)) { + return Collections.emptyList(); + } + return new ArrayList(postReplies.get(postId)); + } finally { + lock.readLock().unlock(); + } + } + + // + // POSTREPLYBUILDERFACTORY METHODS + // + + /** {@inheritDocs} */ + @Override + public PostReplyBuilder newPostReplyBuilder() { + return new MemoryPostReplyBuilder(this, soneProvider); + } + + // + // POSTREPLYSTORE METHODS + // + + /** {@inheritDocs} */ + @Override + public void storePostReply(PostReply postReply) { + lock.writeLock().lock(); + try { + allPostReplies.put(postReply.getId(), postReply); + if (postReplies.containsKey(postReply.getPostId())) { + postReplies.get(postReply.getPostId()).add(postReply); + } else { + TreeSet replies = new TreeSet(Reply.TIME_COMPARATOR); + replies.add(postReply); + postReplies.put(postReply.getPostId(), replies); + } + } finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public void storePostReplies(Sone sone, Collection postReplies) { + checkNotNull(sone, "sone must not be null"); + /* verify that all posts are from the same Sone. */ + for (PostReply postReply : postReplies) { + if (!sone.equals(postReply.getSone())) { + throw new IllegalArgumentException(String.format("PostReply from different Sone found: %s", postReply)); + } + } + + lock.writeLock().lock(); + try { + /* remove all post replies of the Sone. */ + for (PostReply postReply : getRepliesFrom(sone.getId())) { + removePostReply(postReply); + } + for (PostReply postReply : postReplies) { + allPostReplies.put(postReply.getId(), postReply); + sonePostReplies.put(postReply.getSone().getId(), postReply); + if (this.postReplies.containsKey(postReply.getPostId())) { + this.postReplies.get(postReply.getPostId()).add(postReply); + } else { + TreeSet replies = new TreeSet(Reply.TIME_COMPARATOR); + replies.add(postReply); + this.postReplies.put(postReply.getPostId(), replies); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public void removePostReply(PostReply postReply) { + lock.writeLock().lock(); + try { + allPostReplies.remove(postReply.getId()); + if (postReplies.containsKey(postReply.getPostId())) { + postReplies.get(postReply.getPostId()).remove(postReply); + if (postReplies.get(postReply.getPostId()).isEmpty()) { + postReplies.remove(postReply.getPostId()); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDocs} */ + @Override + public void removePostReplies(Sone sone) { + checkNotNull(sone, "sone must not be null"); + + lock.writeLock().lock(); + try { + for (PostReply postReply : sone.getReplies()) { + removePostReply(postReply); + } + } finally { + lock.writeLock().unlock(); + } + } + + // + // PACKAGE-PRIVATE METHODS + // + + /** + * Returns whether the given post is known. + * + * @param post + * The post + * @return {@code true} if the post is known, {@code false} otherwise + */ + boolean isPostKnown(Post post) { + lock.readLock().lock(); + try { + return knownPosts.contains(post.getId()); + } finally { + lock.readLock().unlock(); + } + } + + /** + * Sets whether the given post is known. + * + * @param post + * The post + * @param known + * {@code true} if the post is known, {@code false} otherwise + */ + void setPostKnown(Post post, boolean known) { + lock.writeLock().lock(); + try { + if (known) { + knownPosts.add(post.getId()); + } else { + knownPosts.remove(post.getId()); + } + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Returns whether the given post reply is known. + * + * @param postReply + * The post reply + * @return {@code true} if the given post reply is known, {@code false} + * otherwise + */ + boolean isPostReplyKnown(PostReply postReply) { + lock.readLock().lock(); + try { + return knownPostReplies.contains(postReply.getId()); + } finally { + lock.readLock().unlock(); + } + } + + /** + * Sets whether the given post reply is known. + * + * @param postReply + * The post reply + * @param known + * {@code true} if the post reply is known, {@code false} otherwise + */ + void setPostReplyKnown(PostReply postReply, boolean known) { + lock.writeLock().lock(); + try { + if (known) { + knownPostReplies.add(postReply.getId()); + } else { + knownPostReplies.remove(postReply.getId()); + } + } finally { + lock.writeLock().unlock(); + } + } + + // + // PRIVATE METHODS + // + + /** + * Gets all posts for the given Sone, creating a new collection if there is + * none yet. + * + * @param soneId + * The ID of the Sone to get the posts for + * @return All posts + */ + private Collection getPostsFrom(String soneId) { + Collection posts = null; + lock.readLock().lock(); + try { + posts = sonePosts.get(soneId); + } finally { + lock.readLock().unlock(); + } + if (posts != null) { + return posts; + } + + posts = new HashSet(); + lock.writeLock().lock(); + try { + sonePosts.put(soneId, posts); + } finally { + lock.writeLock().unlock(); + } + + return posts; + } + + /** + * Gets all posts that are directed the given Sone, creating a new collection + * if there is none yet. + * + * @param recipientId + * The ID of the Sone to get the posts for + * @return All posts + */ + private Collection getPostsTo(String recipientId) { + Collection posts = null; + lock.readLock().lock(); + try { + posts = recipientPosts.get(recipientId); + } finally { + lock.readLock().unlock(); + } + if (posts != null) { + return posts; + } + + posts = new HashSet(); + lock.writeLock().lock(); + try { + recipientPosts.put(recipientId, posts); + } finally { + lock.writeLock().unlock(); + } + + return posts; + } + + /** Loads the known posts. */ + private void loadKnownPosts() { + lock.writeLock().lock(); + try { + int postCounter = 0; + while (true) { + String knownPostId = configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").getValue(null); + if (knownPostId == null) { + break; + } + knownPosts.add(knownPostId); + } + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Saves the known posts to the configuration. + * + * @throws DatabaseException + * if a configuration error occurs + */ + private void saveKnownPosts() throws DatabaseException { + lock.readLock().lock(); + try { + int postCounter = 0; + for (String knownPostId : knownPosts) { + configuration.getStringValue("KnownPosts/" + postCounter++ + "/ID").setValue(knownPostId); + } + configuration.getStringValue("KnownPosts/" + postCounter + "/ID").setValue(null); + } catch (ConfigurationException ce1) { + throw new DatabaseException("Could not save database.", ce1); + } finally { + lock.readLock().unlock(); + } + } + + /** + * Returns all replies by the given Sone. + * + * @param id + * The ID of the Sone + * @return The post replies of the Sone, sorted by time (newest first) + */ + private Collection getRepliesFrom(String id) { + lock.readLock().lock(); + try { + if (sonePostReplies.containsKey(id)) { + return Collections.unmodifiableCollection(sonePostReplies.get(id)); + } + return Collections.emptySet(); + } finally { + lock.readLock().unlock(); + } + } + + /** Loads the known post replies. */ + private void loadKnownPostReplies() { + lock.writeLock().lock(); + try { + int replyCounter = 0; + while (true) { + String knownReplyId = configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").getValue(null); + if (knownReplyId == null) { + break; + } + knownPostReplies.add(knownReplyId); + } + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Saves the known post replies to the configuration. + * + * @throws DatabaseException + * if a configuration error occurs + */ + private void saveKnownPostReplies() throws DatabaseException { + lock.readLock().lock(); + try { + int replyCounter = 0; + for (String knownReplyId : knownPostReplies) { + configuration.getStringValue("KnownReplies/" + replyCounter++ + "/ID").setValue(knownReplyId); + } + configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null); + } catch (ConfigurationException ce1) { + throw new DatabaseException("Could not save database.", ce1); + } finally { + lock.readLock().unlock(); + } + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java new file mode 100644 index 0000000..22fa7e6 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java @@ -0,0 +1,186 @@ +/* + * Sone - PostImpl.java - Copyright © 2010–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.memory; + +import java.util.UUID; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.SoneProvider; + +import com.google.common.base.Optional; + +/** + * A post is a short message that a user writes in his Sone to let other users + * know what is going on. + * + * @author David ‘Bombe’ Roden + */ +class MemoryPost implements Post { + + /** The post database. */ + private final MemoryDatabase postDatabase; + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The GUID of the post. */ + private final UUID id; + + /** The ID of the owning Sone. */ + private final String soneId; + + /** The ID of the recipient Sone. */ + private final String recipientId; + + /** The time of the post (in milliseconds since Jan 1, 1970 UTC). */ + private final long time; + + /** The text of the post. */ + private final String text; + + /** + * Creates a new post. + * + * @param postDatabase + * The post database + * @param soneProvider + * The Sone provider + * @param id + * The ID of the post + * @param soneId + * The ID of the Sone this post belongs to + * @param recipientId + * The ID of the recipient of the post + * @param time + * The time of the post (in milliseconds since Jan 1, 1970 UTC) + * @param text + * The text of the post + */ + public MemoryPost(MemoryDatabase postDatabase, SoneProvider soneProvider, String id, String soneId, String recipientId, long time, String text) { + this.postDatabase = postDatabase; + this.soneProvider = soneProvider; + this.id = UUID.fromString(id); + this.soneId = soneId; + this.recipientId = recipientId; + this.time = time; + this.text = text; + } + + // + // ACCESSORS + // + + /** + * {@inheritDoc} + */ + @Override + public String getId() { + return id.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public Sone getSone() { + return soneProvider.getSone(soneId).get(); + } + + /** + * {@inheritDocs} + */ + @Override + public Optional getRecipientId() { + return Optional.fromNullable(recipientId); + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getRecipient() { + return soneProvider.getSone(recipientId); + } + + /** + * {@inheritDoc} + */ + @Override + public long getTime() { + return time; + } + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return text; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isKnown() { + return postDatabase.isPostKnown(this); + } + + /** + * {@inheritDoc} + */ + @Override + public MemoryPost setKnown(boolean known) { + postDatabase.setPostKnown(this, known); + return this; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof MemoryPost)) { + return false; + } + MemoryPost post = (MemoryPost) object; + return post.id.equals(id); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("%s[id=%s,sone=%s,recipient=%s,time=%d,text=%s]", getClass().getName(), id, soneId, recipientId, time, text); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java new file mode 100644 index 0000000..d0b2c9f --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java @@ -0,0 +1,61 @@ +/* + * Sone - MemoryPostBuilder.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.memory; + +import java.util.UUID; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.impl.AbstractPostBuilder; +import net.pterodactylus.sone.database.PostBuilder; +import net.pterodactylus.sone.database.SoneProvider; + +/** + * {@link PostBuilder} implementation that creates a {@link MemoryPost}. + * + * @author David ‘Bombe’ Roden + */ +class MemoryPostBuilder extends AbstractPostBuilder { + + /** The database. */ + private final MemoryDatabase database; + + /** + * Creates a new memory post builder. + * + * @param memoryDatabase + * The database + * @param soneProvider + * The Sone provider + */ + public MemoryPostBuilder(MemoryDatabase memoryDatabase, SoneProvider soneProvider) { + super(soneProvider); + database = memoryDatabase; + } + + /** + * {@inheritDocs} + */ + @Override + public Post build() throws IllegalStateException { + validate(); + Post post = new MemoryPost(database, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text); + post.setKnown(database.isPostKnown(post)); + return post; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java new file mode 100644 index 0000000..a6686ca --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java @@ -0,0 +1,180 @@ +/* + * Sone - MemoryPostReply.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.memory; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.SoneProvider; + +import com.google.common.base.Optional; + +/** + * Memory-based {@link PostReply} implementation. + * + * @author David ‘Bombe’ Roden + */ +class MemoryPostReply implements PostReply { + + /** The database. */ + private final MemoryDatabase database; + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The ID of the post reply. */ + private final String id; + + /** The ID of the owning Sone. */ + private final String soneId; + + /** The time of the post reply. */ + private final long time; + + /** The text of the post reply. */ + private final String text; + + /** The ID of the post this post reply refers to. */ + private final String postId; + + /** + * Creates a new memory-based {@link PostReply} implementation. + * + * @param database + * The database + * @param soneProvider + * The Sone provider + * @param id + * The ID of the post reply + * @param soneId + * The ID of the owning Sone + * @param time + * The time of the post reply + * @param text + * The text of the post reply + * @param postId + * The ID of the post this post reply refers to + */ + public MemoryPostReply(MemoryDatabase database, SoneProvider soneProvider, String id, String soneId, long time, String text, String postId) { + this.database = database; + this.soneProvider = soneProvider; + this.id = id; + this.soneId = soneId; + this.time = time; + this.text = text; + this.postId = postId; + } + + // + // REPLY METHODS + // + + /** + * {@inheritDocs} + */ + @Override + public String getId() { + return id; + } + + /** + * {@inheritDocs} + */ + @Override + public Sone getSone() { + return soneProvider.getSone(soneId).get(); + } + + /** + * {@inheritDocs} + */ + @Override + public long getTime() { + return time; + } + + /** + * {@inheritDocs} + */ + @Override + public String getText() { + return text; + } + + /** + * {@inheritDocs} + */ + @Override + public boolean isKnown() { + return database.isPostReplyKnown(this); + } + + /** + * {@inheritDocs} + */ + @Override + public PostReply setKnown(boolean known) { + database.setPostReplyKnown(this, known); + return this; + } + + // + // POSTREPLY METHODS + // + + /** + * {@inheritDocs} + */ + @Override + public String getPostId() { + return postId; + } + + /** + * {@inheritDocs} + */ + @Override + public Optional getPost() { + return database.getPost(postId); + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDocs} + */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** + * {@inheritDocs} + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof MemoryPostReply)) { + return false; + } + MemoryPostReply memoryPostReply = (MemoryPostReply) object; + return memoryPostReply.id.equals(id); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java new file mode 100644 index 0000000..32ab1e0 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java @@ -0,0 +1,66 @@ +/* + * Sone - MemoryPostReplyBuilder.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.memory; + +import java.util.UUID; + +import net.pterodactylus.sone.data.PostReply; +import net.pterodactylus.sone.data.impl.AbstractPostReplyBuilder; +import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneProvider; + +/** + * {@link PostReplyBuilder} implementation that creates {@link MemoryPostReply} + * objects. + * + * @author David ‘Bombe’ Roden + */ +class MemoryPostReplyBuilder extends AbstractPostReplyBuilder { + + /** The database. */ + private final MemoryDatabase database; + + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** + * Creates a new {@link MemoryPostReply} builder. + * + * @param database + * The database + * @param soneProvider + * The Sone provider + */ + public MemoryPostReplyBuilder(MemoryDatabase database, SoneProvider soneProvider) { + this.database = database; + this.soneProvider = soneProvider; + } + + /** + * {@inheritDocs} + */ + @Override + public PostReply build() throws IllegalStateException { + validate(); + + PostReply postReply = new MemoryPostReply(database, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, currentTime ? System.currentTimeMillis() : time, text, postId); + postReply.setKnown(database.isPostReplyKnown(postReply)); + return postReply; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java index 1944570..d71ffc4 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java @@ -1,5 +1,5 @@ /* - * Sone - FcpInterface.java - Copyright © 2011–2012 David Roden + * Sone - AbstractSoneCommand.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 @@ -32,7 +32,10 @@ import net.pterodactylus.sone.freenet.fcp.AbstractCommand; import net.pterodactylus.sone.freenet.fcp.Command; import net.pterodactylus.sone.freenet.fcp.FcpException; import net.pterodactylus.sone.template.SoneAccessor; -import net.pterodactylus.util.collection.filter.Filters; + +import com.google.common.base.Optional; +import com.google.common.collect.Collections2; + import freenet.node.FSParseException; import freenet.support.SimpleFieldSet; @@ -131,7 +134,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * or if the Sone ID is invalid */ protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly) throws FcpException { - return getSone(simpleFieldSet, parameterName, localOnly, true); + return getSone(simpleFieldSet, parameterName, localOnly, true).get(); } /** @@ -155,13 +158,13 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * or if {@code mandatory} is {@code true} and the Sone ID is * invalid */ - protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly, boolean mandatory) throws FcpException { + protected Optional getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly, boolean mandatory) throws FcpException { String soneId = simpleFieldSet.get(parameterName); if (mandatory && (soneId == null)) { throw new FcpException("Could not load Sone ID from “" + parameterName + "”."); } - Sone sone = localOnly ? core.getLocalSone(soneId, false) : core.getSone(soneId, false); - if (mandatory && (sone == null)) { + Optional sone = core.getSone(soneId); + if ((mandatory && !sone.isPresent()) || (mandatory && sone.isPresent() && (localOnly && !sone.get().isLocal()))) { throw new FcpException("Could not load Sone from “" + soneId + "”."); } return sone; @@ -183,11 +186,11 @@ public abstract class AbstractSoneCommand extends AbstractCommand { protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { try { String postId = simpleFieldSet.getString(parameterName); - Post post = core.getPost(postId, false); - if (post == null) { + Optional post = core.getPost(postId); + if (!post.isPresent()) { throw new FcpException("Could not load post from “" + postId + "”."); } - return post; + return post.get(); } catch (FSParseException fspe1) { throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1); } @@ -209,11 +212,11 @@ public abstract class AbstractSoneCommand extends AbstractCommand { protected PostReply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException { try { String replyId = simpleFieldSet.getString(parameterName); - PostReply reply = core.getReply(replyId, false); - if (reply == null) { + Optional reply = core.getPostReply(replyId); + if (!reply.isPresent()) { throw new FcpException("Could not load reply from “" + replyId + "”."); } - return reply; + return reply.get(); } catch (FSParseException fspe1) { throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1); } @@ -233,14 +236,14 @@ public abstract class AbstractSoneCommand extends AbstractCommand { * such as if the Sone is followed by the local Sone * @return The simple field set containing the given Sone */ - protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Sone localSone) { + protected static SimpleFieldSet encodeSone(Sone sone, String prefix, Optional localSone) { SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder(); soneBuilder.put(prefix + "Name", sone.getName()); soneBuilder.put(prefix + "NiceName", SoneAccessor.getNiceName(sone)); soneBuilder.put(prefix + "LastUpdated", sone.getTime()); - if (localSone != null) { - soneBuilder.put(prefix + "Followed", String.valueOf(localSone.hasFriend(sone.getId()))); + if (localSone.isPresent()) { + soneBuilder.put(prefix + "Followed", String.valueOf(localSone.get().hasFriend(sone.getId()))); } Profile profile = sone.getProfile(); soneBuilder.put(prefix + "Field.Count", profile.getFields().size()); @@ -298,15 +301,15 @@ public abstract class AbstractSoneCommand extends AbstractCommand { postBuilder.put(prefix + "ID", post.getId()); postBuilder.put(prefix + "Sone", post.getSone().getId()); - if (post.getRecipient() != null) { - postBuilder.put(prefix + "Recipient", post.getRecipient().getId()); + if (post.getRecipientId().isPresent()) { + postBuilder.put(prefix + "Recipient", post.getRecipientId().get()); } postBuilder.put(prefix + "Time", post.getTime()); postBuilder.put(prefix + "Text", encodeString(post.getText())); postBuilder.put(encodeLikes(core.getLikes(post), prefix + "Likes.")); if (includeReplies) { - List replies = core.getReplies(post); + List replies = core.getReplies(post.getId()); postBuilder.put(encodeReplies(replies, prefix)); } @@ -335,7 +338,7 @@ public abstract class AbstractSoneCommand extends AbstractCommand { String postPrefix = prefix + postIndex++; postBuilder.put(encodePost(post, postPrefix + ".", includeReplies)); if (includeReplies) { - postBuilder.put(encodeReplies(Filters.filteredList(core.getReplies(post), Reply.FUTURE_REPLY_FILTER), postPrefix + ".")); + postBuilder.put(encodeReplies(Collections2.filter(core.getReplies(post.getId()), Reply.FUTURE_REPLY_FILTER), postPrefix + ".")); } } diff --git a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java index a37dc46..f6c5dd9 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - CreatePostCommand.java - Copyright © 2011–2012 David Roden + * Sone - CreatePostCommand.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.fcp; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; @@ -57,7 +59,7 @@ public class CreatePostCommand extends AbstractSoneCommand { if (sone.equals(recipient)) { return new ErrorResponse("Sone and Recipient must not be the same."); } - Post post = getCore().createPost(sone, recipient, text); + Post post = getCore().createPost(sone, Optional.fromNullable(recipient), text); return new Response("PostCreated", new SimpleFieldSetBuilder().put("Post", post.getId()).get()); } diff --git a/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java index 84bedc7..fe4bee8 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java @@ -1,5 +1,5 @@ /* - * Sone - CreateReplyCommand.java - Copyright © 2011–2012 David Roden + * Sone - CreateReplyCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java index 54f01c8..00d5018 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - DeletePostCommand.java - Copyright © 2011–2012 David Roden + * Sone - DeletePostCommand.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 @@ -48,7 +48,7 @@ public class DeletePostCommand extends AbstractSoneCommand { @Override public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { Post post = getPost(parameters, "Post"); - if (!getCore().isLocalSone(post.getSone())) { + if (!post.getSone().isLocal()) { return new ErrorResponse(401, "Not allowed."); } return new Response("PostDeleted", new SimpleFieldSetBuilder().get()); diff --git a/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java index daed26d..e9c0926 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteReplyCommand.java - Copyright © 2011–2012 David Roden + * Sone - DeleteReplyCommand.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 @@ -48,7 +48,7 @@ public class DeleteReplyCommand extends AbstractSoneCommand { @Override public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { PostReply reply = getReply(parameters, "Reply"); - if (!getCore().isLocalSone(reply.getSone())) { + if (!reply.getSone().isLocal()) { return new ErrorResponse(401, "Not allowed."); } return new Response("ReplyDeleted", new SimpleFieldSetBuilder().get()); diff --git a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java index b3222be..02d84cc 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java +++ b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java @@ -1,5 +1,5 @@ /* - * Sone - FcpInterface.java - Copyright © 2011–2012 David Roden + * Sone - FcpInterface.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.fcp; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -27,9 +29,10 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.freenet.fcp.Command.AccessType; import net.pterodactylus.sone.freenet.fcp.Command.ErrorResponse; import net.pterodactylus.sone.freenet.fcp.Command.Response; -import net.pterodactylus.sone.freenet.fcp.FcpException; import net.pterodactylus.util.logging.Logging; -import net.pterodactylus.util.validation.Validation; + +import com.google.inject.Inject; + import freenet.pluginmanager.FredPluginFCP; import freenet.pluginmanager.PluginNotFoundException; import freenet.pluginmanager.PluginReplySender; @@ -80,6 +83,7 @@ public class FcpInterface { * @param core * The core */ + @Inject public FcpInterface(Core core) { commands.put("Version", new VersionCommand(core)); commands.put("GetLocalSones", new GetLocalSonesCommand(core)); @@ -88,6 +92,8 @@ public class FcpInterface { commands.put("GetPost", new GetPostCommand(core)); commands.put("GetPosts", new GetPostsCommand(core)); commands.put("GetPostFeed", new GetPostFeedCommand(core)); + commands.put("LockSone", new LockSoneCommand(core)); + commands.put("UnlockSone", new UnlockSoneCommand(core)); commands.put("LikePost", new LikePostCommand(core)); commands.put("LikeReply", new LikeReplyCommand(core)); commands.put("CreatePost", new CreatePostCommand(core)); @@ -119,8 +125,7 @@ public class FcpInterface { * The action level for which full FCP access is required */ public void setFullAccessRequired(FullAccessRequired fullAccessRequired) { - Validation.begin().isNotNull("FullAccessRequired", fullAccessRequired).check(); - this.fullAccessRequired = fullAccessRequired; + this.fullAccessRequired = checkNotNull(fullAccessRequired, "fullAccessRequired must not be null"); } // @@ -172,9 +177,9 @@ public class FcpInterface { try { Response response = command.execute(parameters, data, AccessType.values()[accessType]); sendReply(pluginReplySender, identifier, response); - } catch (FcpException fe1) { + } catch (Exception e1) { logger.log(Level.WARNING, "Could not process FCP command “%s”.", command); - sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + fe1.getMessage())); + sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + e1.getMessage())); } } catch (PluginNotFoundException pnfe1) { logger.log(Level.WARNING, "Could not find destination plugin: " + pluginReplySender); diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java index 4619004..b8684b0 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetLocalSonesCommand.java - Copyright © 2011–2012 David Roden + * Sone - GetLocalSonesCommand.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 @@ -18,7 +18,6 @@ package net.pterodactylus.sone.fcp; import net.pterodactylus.sone.core.Core; -import net.pterodactylus.sone.freenet.fcp.FcpException; import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; @@ -44,7 +43,7 @@ public class GetLocalSonesCommand extends AbstractSoneCommand { * {@inheritDoc} */ @Override - public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { + public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) { return new Response("ListLocalSones", encodeSones(getCore().getLocalSones(), "LocalSones.")); } diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java index e15d7d6..c503f5b 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostCommand.java - Copyright © 2011–2012 David Roden + * Sone - GetPostCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java index 781bed2..b22d356 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostFeedCommand.java - Copyright © 2011–2012 David Roden + * Sone - GetPostFeedCommand.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 @@ -18,16 +18,19 @@ package net.pterodactylus.sone.fcp; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Set; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.fcp.FcpException; -import net.pterodactylus.util.collection.filter.Filters; + +import com.google.common.base.Optional; +import com.google.common.collect.Collections2; + import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; @@ -58,16 +61,17 @@ public class GetPostFeedCommand extends AbstractSoneCommand { int startPost = getInt(parameters, "StartPost", 0); int maxPosts = getInt(parameters, "MaxPosts", -1); - Set allPosts = new HashSet(); + Collection allPosts = new HashSet(); allPosts.addAll(sone.getPosts()); for (String friendSoneId : sone.getFriends()) { - if (!getCore().hasSone(friendSoneId)) { + Optional friendSone = getCore().getSone(friendSoneId); + if (!friendSone.isPresent()) { continue; } - allPosts.addAll(getCore().getSone(friendSoneId, false).getPosts()); + allPosts.addAll(friendSone.get().getPosts()); } - allPosts.addAll(getCore().getDirectedPosts(sone)); - allPosts = Filters.filteredSet(allPosts, Post.FUTURE_POSTS_FILTER); + allPosts.addAll(getCore().getDirectedPosts(sone.getId())); + allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER); List sortedPosts = new ArrayList(allPosts); Collections.sort(sortedPosts, Post.TIME_COMPARATOR); diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java index 36d2912..1947718 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostsCommand.java - Copyright © 2011–2012 David Roden + * Sone - GetPostsCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java index c71ea43..e3ec1b1 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetSoneCommand.java - Copyright © 2011–2012 David Roden + * Sone - GetSoneCommand.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 @@ -21,6 +21,9 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.fcp.FcpException; + +import com.google.common.base.Optional; + import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; @@ -48,7 +51,7 @@ public class GetSoneCommand extends AbstractSoneCommand { @Override public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { Sone sone = getSone(parameters, "Sone", false); - Sone localSone = getSone(parameters, "LocalSone", false, false); + Optional localSone = getSone(parameters, "LocalSone", false, false); return new Response("Sone", encodeSone(sone, "", localSone)); } diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java index 5c9c117..9ff8587 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetSonesCommand.java - Copyright © 2011–2012 David Roden + * Sone - GetSonesCommand.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 @@ -23,7 +23,6 @@ import java.util.List; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.freenet.fcp.FcpException; import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; @@ -48,7 +47,7 @@ public class GetSonesCommand extends AbstractSoneCommand { * {@inheritDoc} */ @Override - public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { + public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) { int startSone = getInt(parameters, "StartSone", 0); int maxSones = getInt(parameters, "MaxSones", -1); List sones = new ArrayList(getCore().getSones()); diff --git a/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java index d13271a..20d251c 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - LikePostCommand.java - Copyright © 2011–2012 David Roden + * Sone - LikePostCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java index cc989ab..a462f08 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java @@ -1,5 +1,5 @@ /* - * Sone - LikeReplyCommand.java - Copyright © 2011–2012 David Roden + * Sone - LikeReplyCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java new file mode 100644 index 0000000..5cad8ca --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java @@ -0,0 +1,60 @@ +/* + * Sone - LockSoneCommand.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.fcp; + +import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder; +import net.pterodactylus.sone.freenet.fcp.FcpException; + +import freenet.support.SimpleFieldSet; +import freenet.support.api.Bucket; + +import com.google.common.base.Optional; + +/** + * Implements the “LockSone” FCP command. If a valid local Sone was given as + * parameter “Sone,” this command will always lock the Sone and reply with + * “SoneLocked.” + * + * @author David ‘Bombe’ Roden + */ +public class LockSoneCommand extends AbstractSoneCommand { + + /** + * Creates a new “LockSone” FCP command. + * + * @param core + * The core to operate on + */ + public LockSoneCommand(Core core) { + super(core, true); + } + + // + // COMMAND METHODS + // + + @Override + public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { + Optional sone = getSone(parameters, "Sone", true, true); + getCore().lockSone(sone.get()); + return new Response("SoneLocked", new SimpleFieldSetBuilder().put("Sone", sone.get().getId()).get()); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java new file mode 100644 index 0000000..7ecfada --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java @@ -0,0 +1,60 @@ +/* + * Sone - LockSoneCommand.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.fcp; + +import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder; +import net.pterodactylus.sone.freenet.fcp.FcpException; + +import freenet.support.SimpleFieldSet; +import freenet.support.api.Bucket; + +import com.google.common.base.Optional; + +/** + * Implements the “UnlockSone” FCP command. If a valid local Sone was given as + * parameter “Sone,” this command will always unlock the Sone and reply with + * “SoneUnlocked.” + * + * @author David ‘Bombe’ Roden + */ +public class UnlockSoneCommand extends AbstractSoneCommand { + + /** + * Creates a new “LockSone” FCP command. + * + * @param core + * The core to operate on + */ + public UnlockSoneCommand(Core core) { + super(core, true); + } + + // + // COMMAND METHODS + // + + @Override + public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException { + Optional sone = getSone(parameters, "Sone", true, true); + getCore().unlockSone(sone.get()); + return new Response("SoneUnlocked", new SimpleFieldSetBuilder().put("Sone", sone.get().getId()).get()); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java b/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java index bc2adb2..c482f24 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java @@ -1,5 +1,5 @@ /* - * Sone - VersionCommand.java - Copyright © 2011–2012 David Roden + * Sone - VersionCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java b/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java index 7fffd5c..36ac8cd 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java +++ b/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java @@ -1,5 +1,5 @@ /* - * Sone - L10nFilter.java - Copyright © 2010–2012 David Roden + * Sone - L10nFilter.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java b/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java index 94a2690..1e95389 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java +++ b/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java @@ -1,5 +1,5 @@ /* - * Sone - PluginStoreConfigurationBackend.java - Copyright © 2010–2012 David Roden + * Sone - PluginStoreConfigurationBackend.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java b/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java index 5507b0d..a256edb 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java +++ b/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - SimpleFieldSetBuilder.java - Copyright © 2011–2012 David Roden + * Sone - SimpleFieldSetBuilder.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 @@ -17,7 +17,8 @@ package net.pterodactylus.sone.freenet; -import net.pterodactylus.util.validation.Validation; +import static com.google.common.base.Preconditions.checkNotNull; + import freenet.support.SimpleFieldSet; /** @@ -46,8 +47,7 @@ public class SimpleFieldSetBuilder { * The simple field set to build */ public SimpleFieldSetBuilder(SimpleFieldSet simpleFieldSet) { - Validation.begin().isNotNull("Simple Field Set", simpleFieldSet).check(); - this.simpleFieldSet = simpleFieldSet; + this.simpleFieldSet = checkNotNull(simpleFieldSet, "simpleFieldSet must not be null"); } /** diff --git a/src/main/java/net/pterodactylus/sone/freenet/StringBucket.java b/src/main/java/net/pterodactylus/sone/freenet/StringBucket.java index 8f6b27f..c96a3cd 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/StringBucket.java +++ b/src/main/java/net/pterodactylus/sone/freenet/StringBucket.java @@ -1,5 +1,5 @@ /* - * Sone - StringBucket.java - Copyright © 2010–2012 David Roden + * Sone - StringBucket.java - Copyright © 2010–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 @@ -18,7 +18,6 @@ package net.pterodactylus.sone.freenet; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; @@ -84,7 +83,7 @@ public class StringBucket implements Bucket { * {@inheritDoc} */ @Override - public InputStream getInputStream() throws IOException { + public InputStream getInputStream() { return new ByteArrayInputStream(string.getBytes(encoding)); } @@ -100,7 +99,7 @@ public class StringBucket implements Bucket { * {@inheritDoc} */ @Override - public OutputStream getOutputStream() throws IOException { + public OutputStream getOutputStream() { return null; } diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java index b73fb1d..fa5a372 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractCommand.java - Copyright © 2011–2012 David Roden + * Sone - AbstractCommand.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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java index e3b55a9..32df6d8 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java @@ -1,5 +1,5 @@ /* - * Sone - Command.java - Copyright © 2011–2012 David Roden + * Sone - Command.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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java index 22bb269..e40438c 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java @@ -1,5 +1,5 @@ /* - * Sone - FcpException.java - Copyright © 2011–2012 David Roden + * Sone - FcpException.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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/ConnectorListener.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/ConnectorListener.java deleted file mode 100644 index 76a5497..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/ConnectorListener.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Sone - ConnectorListener.java - Copyright © 2010–2012 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.freenet.plugin; - -import java.util.EventListener; - -import freenet.support.SimpleFieldSet; -import freenet.support.api.Bucket; - -/** - * Interface for objects that want to be notified if a {@link PluginConnector} - * receives a reply from a plugin. As a connection listener is always - * {@link PluginConnector#addConnectorListener(String, String, ConnectorListener) - * added} for a specific plugin, it will always be notified for replies from the - * correct plugin (unless you register the same listener for multiple - * plugins—which you subsequently should not do). - * - * @author David ‘Bombe’ Roden - */ -public interface ConnectorListener extends EventListener { - - /** - * A reply was received from the plugin this connection listener was added - * for. - * - * @param pluginConnector - * The plugin connector that received the reply - * @param fields - * The fields of the reply - * @param data - * The data of the reply (may be null) - */ - public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data); - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/ConnectorListenerManager.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/ConnectorListenerManager.java deleted file mode 100644 index 742a514..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/ConnectorListenerManager.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Sone - ConnectorListenerManager.java - Copyright © 2010–2012 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.freenet.plugin; - -import net.pterodactylus.util.event.AbstractListenerManager; -import freenet.support.SimpleFieldSet; -import freenet.support.api.Bucket; - -/** - * Manages {@link ConnectorListener}s and fire events. - * - * @author David ‘Bombe’ Roden - */ -public class ConnectorListenerManager extends AbstractListenerManager { - - /** - * Creates a new manager for {@link ConnectorListener}s. - * - * @param pluginConnector - * The plugin connector that is the source for all events - */ - public ConnectorListenerManager(PluginConnector pluginConnector) { - super(pluginConnector); - } - - // - // ACTIONS - // - - /** - * Notifies all registered listeners that a reply from the plugin was - * received. - * - * @param fields - * The fields of the reply - * @param data - * The data of the reply (may be null) - */ - public void fireReceivedReply(SimpleFieldSet fields, Bucket data) { - for (ConnectorListener connectorListener : getListeners()) { - connectorListener.receivedReply(getSource(), fields, data); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java index 9145cf5..47330e8 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java +++ b/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java @@ -1,5 +1,5 @@ /* - * Sone - PluginConnector.java - Copyright © 2010–2012 David Roden + * Sone - PluginConnector.java - Copyright © 2010–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 @@ -17,11 +17,11 @@ package net.pterodactylus.sone.freenet.plugin; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent; + +import com.google.common.eventbus.EventBus; +import com.google.inject.Inject; -import net.pterodactylus.util.collection.Pair; import freenet.pluginmanager.FredPluginTalker; import freenet.pluginmanager.PluginNotFoundException; import freenet.pluginmanager.PluginRespirator; @@ -37,55 +37,27 @@ import freenet.support.api.Bucket; */ public class PluginConnector implements FredPluginTalker { + /** The event bus. */ + private final EventBus eventBus; + /** The plugin respirator. */ private final PluginRespirator pluginRespirator; - /** Connector listener managers for all plugin connections. */ - private final Map, ConnectorListenerManager> connectorListenerManagers = Collections.synchronizedMap(new HashMap, ConnectorListenerManager>()); - /** * Creates a new plugin connector. * + * @param eventBus + * The event bus * @param pluginRespirator * The plugin respirator */ - public PluginConnector(PluginRespirator pluginRespirator) { + @Inject + public PluginConnector(EventBus eventBus, PluginRespirator pluginRespirator) { + this.eventBus = eventBus; this.pluginRespirator = pluginRespirator; } // - // LISTENER MANAGEMENT - // - - /** - * Adds a connection listener for the given plugin connection. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @param connectorListener - * The listener to add - */ - public void addConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) { - getConnectorListenerManager(pluginName, identifier).addListener(connectorListener); - } - - /** - * Removes a connection listener for the given plugin connection. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @param connectorListener - * The listener to remove - */ - public void removeConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) { - getConnectorListenerManager(pluginName, identifier).removeListener(connectorListener); - } - - // // ACTIONS // @@ -128,43 +100,6 @@ public class PluginConnector implements FredPluginTalker { // /** - * Returns the connection listener manager for the given plugin connection, - * creating a new one if none does exist yet. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @return The connection listener manager - */ - private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier) { - return getConnectorListenerManager(pluginName, identifier, true); - } - - /** - * Returns the connection listener manager for the given plugin connection, - * optionally creating a new one if none does exist yet. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @param create - * {@code true} to create a new manager if there is none, - * {@code false} to return {@code null} in that case - * @return The connection listener manager, or {@code null} if none existed - * and {@code create} is {@code false} - */ - private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier, boolean create) { - ConnectorListenerManager connectorListenerManager = connectorListenerManagers.get(new Pair(pluginName, identifier)); - if (create && (connectorListenerManager == null)) { - connectorListenerManager = new ConnectorListenerManager(this); - connectorListenerManagers.put(new Pair(pluginName, identifier), connectorListenerManager); - } - return connectorListenerManager; - } - - /** * Returns the plugin talker for the given plugin connection. * * @param pluginName @@ -192,12 +127,7 @@ public class PluginConnector implements FredPluginTalker { */ @Override public void onReply(String pluginName, String identifier, SimpleFieldSet params, Bucket data) { - ConnectorListenerManager connectorListenerManager = getConnectorListenerManager(pluginName, identifier, false); - if (connectorListenerManager == null) { - /* we don’t care about events for this plugin. */ - return; - } - connectorListenerManager.fireReceivedReply(params, data); + eventBus.post(new ReceivedReplyEvent(this, pluginName, identifier, params, data)); } } diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java index 08810b3..fd3dec7 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java +++ b/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java @@ -1,5 +1,5 @@ /* - * Sone - PluginException.java - Copyright © 2010–2012 David Roden + * Sone - PluginException.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/event/ReceivedReplyEvent.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/event/ReceivedReplyEvent.java new file mode 100644 index 0000000..89a8df3 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/plugin/event/ReceivedReplyEvent.java @@ -0,0 +1,117 @@ +/* + * Sone - ReceivedReplyEvent.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.freenet.plugin.event; + +import net.pterodactylus.sone.freenet.plugin.PluginConnector; +import freenet.support.SimpleFieldSet; +import freenet.support.api.Bucket; + +/** + * Event that signals that a plugin reply was received. + * + * @author David ‘Bombe’ Roden + */ +public class ReceivedReplyEvent { + + /** The connector that received the reply. */ + private final PluginConnector pluginConnector; + + /** The name of the plugin that sent the reply. */ + private final String pluginName; + + /** The identifier of the initial request. */ + private final String identifier; + + /** The fields containing the reply. */ + private final SimpleFieldSet fieldSet; + + /** The optional reply data. */ + private final Bucket data; + + /** + * Creates a new “reply received” event. + * + * @param pluginConnector + * The connector that received the event + * @param pluginName + * The name of the plugin that sent the reply + * @param identifier + * The identifier of the initial request + * @param fieldSet + * The fields containing the reply + * @param data + * The optional data of the reply + */ + public ReceivedReplyEvent(PluginConnector pluginConnector, String pluginName, String identifier, SimpleFieldSet fieldSet, Bucket data) { + this.pluginConnector = pluginConnector; + this.pluginName = pluginName; + this.identifier = identifier; + this.fieldSet = fieldSet; + this.data = data; + } + + // + // ACCESSORS + // + + /** + * Returns the plugin connector that received the reply. + * + * @return The plugin connector that received the reply + */ + public PluginConnector pluginConnector() { + return pluginConnector; + } + + /** + * Returns the name of the plugin that sent the reply. + * + * @return The name of the plugin that sent the reply + */ + public String pluginName() { + return pluginName; + } + + /** + * Returns the identifier of the initial request. + * + * @return The identifier of the initial request + */ + public String identifier() { + return identifier; + } + + /** + * Returns the fields containing the reply. + * + * @return The fields containing the reply + */ + public SimpleFieldSet fieldSet() { + return fieldSet; + } + + /** + * Returns the optional data of the reply. + * + * @return The optional data of the reply (may be {@code null}) + */ + public Bucket data() { + return data; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java index 2ef33c3..0cfb055 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java @@ -1,5 +1,5 @@ /* - * Sone - DefaultIdentity.java - Copyright © 2010–2012 David Roden + * Sone - DefaultIdentity.java - Copyright © 2010–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 @@ -208,7 +208,7 @@ public class DefaultIdentity implements Identity { */ @Override public int hashCode() { - return id.hashCode(); + return getId().hashCode(); } /** @@ -216,11 +216,11 @@ public class DefaultIdentity implements Identity { */ @Override public boolean equals(Object object) { - if (!(object instanceof DefaultIdentity)) { + if (!(object instanceof Identity)) { return false; } - DefaultIdentity identity = (DefaultIdentity) object; - return identity.id.equals(id); + Identity identity = (Identity) object; + return identity.getId().equals(getId()); } /** diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java index 451dd5f..4f87b97 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java @@ -1,5 +1,5 @@ /* - * Sone - DefaultOwnIdentity.java - Copyright © 2010–2012 David Roden + * Sone - DefaultOwnIdentity.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java index 2849da9..bc594f8 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java @@ -1,5 +1,5 @@ /* - * Sone - Identity.java - Copyright © 2010–2012 David Roden + * Sone - Identity.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityListener.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityListener.java deleted file mode 100644 index 51e1437..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityListener.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Sone - IdentityListener.java - Copyright © 2010–2012 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.freenet.wot; - -import java.util.EventListener; - -/** - * Listener interface for {@link IdentityManager} events. - * - * @author David ‘Bombe’ Roden - */ -public interface IdentityListener extends EventListener { - - /** - * Notifies a listener that an {@link OwnIdentity} that was not known on the - * previous check is available. - * - * @param ownIdentity - * The new own identity - */ - public void ownIdentityAdded(OwnIdentity ownIdentity); - - /** - * Notifies a listener that an {@link OwnIdentity} that was available during - * the last check has gone away. - * - * @param ownIdentity - * The disappeared own identity - */ - public void ownIdentityRemoved(OwnIdentity ownIdentity); - - /** - * Notifies a listener that a new identity was discovered. - * - * @param ownIdentity - * The own identity at the root of the trust tree - * @param identity - * The new identity - */ - public void identityAdded(OwnIdentity ownIdentity, Identity identity); - - /** - * Notifies a listener that some properties of the identity have changed. - * - * @param ownIdentity - * The own identity at the root of the trust tree - * @param identity - * The updated identity - */ - public void identityUpdated(OwnIdentity ownIdentity, Identity identity); - - /** - * Notifies a listener that an identity has gone away. - * - * @param ownIdentity - * The own identity at the root of the trust tree - * @param identity - * The disappeared identity - */ - public void identityRemoved(OwnIdentity ownIdentity, Identity identity); - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityListenerManager.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityListenerManager.java deleted file mode 100644 index 50808a2..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityListenerManager.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Sone - IdentityListenerManager.java - Copyright © 2010–2012 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.freenet.wot; - -import net.pterodactylus.util.event.AbstractListenerManager; - -/** - * Manager for {@link IdentityListener}s. - * - * @author David ‘Bombe’ Roden - */ -public class IdentityListenerManager extends AbstractListenerManager { - - /** - * Creates a new identity listener manager. - */ - public IdentityListenerManager() { - super(null); - } - - // - // ACTIONS - // - - /** - * Notifies all listeners that an {@link OwnIdentity} that was not known on - * the previous check is available. - * - * @see IdentityListener#ownIdentityAdded(OwnIdentity) - * @param ownIdentity - * The new own identity - */ - public void fireOwnIdentityAdded(OwnIdentity ownIdentity) { - for (IdentityListener identityListener : getListeners()) { - identityListener.ownIdentityAdded(ownIdentity); - } - } - - /** - * Notifies all listeners that an {@link OwnIdentity} that was available - * during the last check has gone away. - * - * @see IdentityListener#ownIdentityRemoved(OwnIdentity) - * @param ownIdentity - * The disappeared own identity - */ - public void fireOwnIdentityRemoved(OwnIdentity ownIdentity) { - for (IdentityListener identityListener : getListeners()) { - identityListener.ownIdentityRemoved(ownIdentity); - } - } - - /** - * Notifies all listeners that a new identity was discovered. - * - * @see IdentityListener#identityAdded(OwnIdentity, Identity) - * @param ownIdentity - * The own identity at the root of the trust tree - * @param identity - * The new identity - */ - public void fireIdentityAdded(OwnIdentity ownIdentity, Identity identity) { - for (IdentityListener identityListener : getListeners()) { - identityListener.identityAdded(ownIdentity, identity); - } - } - - /** - * Notifies all listeners that some properties of the identity have changed. - * - * @see IdentityListener#identityUpdated(OwnIdentity, Identity) - * @param ownIdentity - * The own identity at the root of the trust tree - * @param identity - * The updated identity - */ - public void fireIdentityUpdated(OwnIdentity ownIdentity, Identity identity) { - for (IdentityListener identityListener : getListeners()) { - identityListener.identityUpdated(ownIdentity, identity); - } - } - - /** - * Notifies all listeners that an identity has gone away. - * - * @see IdentityListener#identityRemoved(OwnIdentity, Identity) - * @param ownIdentity - * The own identity at the root of the trust tree - * @param identity - * The disappeared identity - */ - public void fireIdentityRemoved(OwnIdentity ownIdentity, Identity identity) { - for (IdentityListener identityListener : getListeners()) { - identityListener.identityRemoved(ownIdentity, identity); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java index 568a637..de36ca1 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java @@ -1,5 +1,5 @@ /* - * Sone - IdentityManager.java - Copyright © 2010–2012 David Roden + * Sone - IdentityManager.java - Copyright © 2010–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 @@ -27,16 +27,25 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.pterodactylus.sone.freenet.plugin.PluginException; +import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent; +import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent; +import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent; +import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent; +import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.service.AbstractService; +import com.google.common.eventbus.EventBus; +import com.google.inject.Inject; +import com.google.inject.name.Named; + /** * The identity manager takes care of loading and storing identities, their * contexts, and properties. It does so in a way that does not expose errors via * exceptions but it only logs them and tries to return sensible defaults. *

* It is also responsible for polling identities from the Web of Trust plugin - * and notifying registered {@link IdentityListener}s when {@link Identity}s and + * and sending events to the {@link EventBus} when {@link Identity}s and * {@link OwnIdentity}s are discovered or disappearing. * * @author David ‘Bombe’ Roden @@ -52,8 +61,8 @@ public class IdentityManager extends AbstractService { /** The logger. */ private static final Logger logger = Logging.getLogger(IdentityManager.class); - /** The event manager. */ - private final IdentityListenerManager identityListenerManager = new IdentityListenerManager(); + /** The event bus. */ + private final EventBus eventBus; /** The Web of Trust connector. */ private final WebOfTrustConnector webOfTrustConnector; @@ -71,43 +80,23 @@ public class IdentityManager extends AbstractService { /** * Creates a new identity manager. * + * @param eventBus + * The event bus * @param webOfTrustConnector * The Web of Trust connector * @param context * The context to focus on (may be {@code null} to ignore * contexts) */ - public IdentityManager(WebOfTrustConnector webOfTrustConnector, String context) { + @Inject + public IdentityManager(EventBus eventBus, WebOfTrustConnector webOfTrustConnector, @Named("WebOfTrustContext") String context) { super("Sone Identity Manager", false); + this.eventBus = eventBus; this.webOfTrustConnector = webOfTrustConnector; this.context = context; } // - // LISTENER MANAGEMENT - // - - /** - * Adds a listener for identity events. - * - * @param identityListener - * The listener to add - */ - public void addIdentityListener(IdentityListener identityListener) { - identityListenerManager.addListener(identityListener); - } - - /** - * Removes a listener for identity events. - * - * @param identityListener - * The listener to remove - */ - public void removeIdentityListener(IdentityListener identityListener) { - identityListenerManager.removeListener(identityListener); - } - - // // ACCESSORS // @@ -190,7 +179,10 @@ public class IdentityManager extends AbstractService { Map identities = new HashMap(); currentIdentities.put(ownIdentity, identities); - /* if the context doesn’t match, skip getting trusted identities. */ + /* + * if the context doesn’t match, skip getting trusted + * identities. + */ if ((context != null) && !ownIdentity.hasContext(context)) { continue; } @@ -218,7 +210,7 @@ public class IdentityManager extends AbstractService { /* find new identities. */ for (Identity currentIdentity : currentIdentities.get(ownIdentity).values()) { if (!oldIdentities.containsKey(ownIdentity) || !oldIdentities.get(ownIdentity).containsKey(currentIdentity.getId())) { - identityListenerManager.fireIdentityAdded(ownIdentity, currentIdentity); + eventBus.post(new IdentityAddedEvent(ownIdentity, currentIdentity)); } } @@ -226,7 +218,7 @@ public class IdentityManager extends AbstractService { if (oldIdentities.containsKey(ownIdentity)) { for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) { if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) { - identityListenerManager.fireIdentityRemoved(ownIdentity, oldIdentity); + eventBus.post(new IdentityRemovedEvent(ownIdentity, oldIdentity)); } } @@ -239,12 +231,12 @@ public class IdentityManager extends AbstractService { Set oldContexts = oldIdentity.getContexts(); Set newContexts = newIdentity.getContexts(); if (oldContexts.size() != newContexts.size()) { - identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); + eventBus.post(new IdentityUpdatedEvent(ownIdentity, newIdentity)); continue; } for (String oldContext : oldContexts) { if (!newContexts.contains(oldContext)) { - identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); + eventBus.post(new IdentityUpdatedEvent(ownIdentity, newIdentity)); break; } } @@ -259,12 +251,12 @@ public class IdentityManager extends AbstractService { Map oldProperties = oldIdentity.getProperties(); Map newProperties = newIdentity.getProperties(); if (oldProperties.size() != newProperties.size()) { - identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); + eventBus.post(new IdentityUpdatedEvent(ownIdentity, newIdentity)); continue; } for (Entry oldProperty : oldProperties.entrySet()) { if (!newProperties.containsKey(oldProperty.getKey()) || !newProperties.get(oldProperty.getKey()).equals(oldProperty.getValue())) { - identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); + eventBus.post(new IdentityUpdatedEvent(ownIdentity, newIdentity)); break; } } @@ -299,7 +291,7 @@ public class IdentityManager extends AbstractService { for (OwnIdentity oldOwnIdentity : currentOwnIdentities.values()) { OwnIdentity newOwnIdentity = newOwnIdentities.get(oldOwnIdentity.getId()); if ((newOwnIdentity == null) || ((context != null) && oldOwnIdentity.hasContext(context) && !newOwnIdentity.hasContext(context))) { - identityListenerManager.fireOwnIdentityRemoved(new DefaultOwnIdentity(oldOwnIdentity)); + eventBus.post(new OwnIdentityRemovedEvent(new DefaultOwnIdentity(oldOwnIdentity))); } } @@ -307,7 +299,7 @@ public class IdentityManager extends AbstractService { for (OwnIdentity currentOwnIdentity : newOwnIdentities.values()) { OwnIdentity oldOwnIdentity = currentOwnIdentities.get(currentOwnIdentity.getId()); if (((oldOwnIdentity == null) && ((context == null) || currentOwnIdentity.hasContext(context))) || ((oldOwnIdentity != null) && (context != null) && (!oldOwnIdentity.hasContext(context) && currentOwnIdentity.hasContext(context)))) { - identityListenerManager.fireOwnIdentityAdded(new DefaultOwnIdentity(currentOwnIdentity)); + eventBus.post(new OwnIdentityAddedEvent(new DefaultOwnIdentity(currentOwnIdentity))); } } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java index 4272669..6fc7044 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java @@ -1,5 +1,5 @@ /* - * Sone - OwnIdentity.java - Copyright © 2010–2012 David Roden + * Sone - OwnIdentity.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java index 9b355b5..6fa37d0 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java @@ -1,5 +1,5 @@ /* - * Sone - Trust.java - Copyright © 2010–2012 David Roden + * Sone - Trust.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java index c4e8d89..07628e3 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java @@ -1,5 +1,5 @@ /* - * Sone - WebOfTrustConnector.java - Copyright © 2010–2012 David Roden + * Sone - WebOfTrustConnector.java - Copyright © 2010–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 @@ -25,11 +25,16 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; -import net.pterodactylus.sone.freenet.plugin.ConnectorListener; import net.pterodactylus.sone.freenet.plugin.PluginConnector; import net.pterodactylus.sone.freenet.plugin.PluginException; +import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; + +import com.google.common.collect.MapMaker; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; + import freenet.support.SimpleFieldSet; import freenet.support.api.Bucket; @@ -52,6 +57,9 @@ public class WebOfTrustConnector { /** The plugin connector. */ private final PluginConnector pluginConnector; + /** Map for replies. */ + private final Map replies = new MapMaker().makeMap(); + /** * Creates a new Web of Trust connector that uses the given plugin * connector. @@ -59,6 +67,7 @@ public class WebOfTrustConnector { * @param pluginConnector * The plugin connector */ + @Inject public WebOfTrustConnector(PluginConnector pluginConnector) { this.pluginConnector = pluginConnector; } @@ -380,24 +389,12 @@ public class WebOfTrustConnector { * if the request could not be sent */ private Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException { - final String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement(); - final Reply reply = new Reply(); + String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement(); + Reply reply = new Reply(); + PluginIdentifier pluginIdentifier = new PluginIdentifier(WOT_PLUGIN_NAME, identifier); + replies.put(pluginIdentifier, reply); + logger.log(Level.FINE, String.format("Sending FCP Request: %s", fields.get("Message"))); - ConnectorListener connectorListener = new ConnectorListener() { - - @Override - @SuppressWarnings("synthetic-access") - public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) { - String messageName = fields.get("Message"); - logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", messageName)); - synchronized (reply) { - reply.setFields(fields); - reply.setData(data); - reply.notify(); - } - } - }; - pluginConnector.addConnectorListener(WOT_PLUGIN_NAME, identifier, connectorListener); synchronized (reply) { try { pluginConnector.sendRequest(WOT_PLUGIN_NAME, identifier, fields, data); @@ -409,7 +406,7 @@ public class WebOfTrustConnector { } } } finally { - pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, identifier, connectorListener); + replies.remove(pluginIdentifier); } } logger.log(Level.FINEST, String.format("Received FCP Response for %s: %s", fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null)); @@ -420,6 +417,27 @@ public class WebOfTrustConnector { } /** + * Notifies the connector that a plugin reply was received. + * + * @param receivedReplyEvent + * The event + */ + @Subscribe + public void receivedReply(ReceivedReplyEvent receivedReplyEvent) { + PluginIdentifier pluginIdentifier = new PluginIdentifier(receivedReplyEvent.pluginName(), receivedReplyEvent.identifier()); + Reply reply = replies.remove(pluginIdentifier); + if (reply == null) { + return; + } + logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", receivedReplyEvent.fieldSet().get("Message"))); + synchronized (reply) { + reply.setFields(receivedReplyEvent.fieldSet()); + reply.setData(receivedReplyEvent.data()); + reply.notify(); + } + } + + /** * Container for the data of the reply from a plugin. * * @author David ‘Bombe’ Roden @@ -554,4 +572,57 @@ public class WebOfTrustConnector { } + /** + * Container for identifying plugins. Plugins are identified by their plugin + * name and their unique identifier. + * + * @author David Roden + */ + private static class PluginIdentifier { + + /** The plugin name. */ + private final String pluginName; + + /** The plugin identifier. */ + private final String identifier; + + /** + * Creates a new plugin identifier. + * + * @param pluginName + * The name of the plugin + * @param identifier + * The identifier of the plugin + */ + public PluginIdentifier(String pluginName, String identifier) { + this.pluginName = pluginName; + this.identifier = identifier; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return pluginName.hashCode() ^ identifier.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof PluginIdentifier)) { + return false; + } + PluginIdentifier pluginIdentifier = (PluginIdentifier) object; + return pluginName.equals(pluginIdentifier.pluginName) && identifier.equals(pluginIdentifier.identifier); + } + + } + } diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java index 35d65af..2b154fc 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java @@ -1,5 +1,5 @@ /* - * Sone - WebOfTrustException.java - Copyright © 2010–2012 David Roden + * Sone - WebOfTrustException.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.java new file mode 100644 index 0000000..69f17ef --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.java @@ -0,0 +1,42 @@ +/* + * Sone - IdentityAddedEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Event that signals that an {@link Identity} was added. + * + * @author David ‘Bombe’ Roden + */ +public class IdentityAddedEvent extends IdentityEvent { + + /** + * Creates a new “identity added” event. + * + * @param ownIdentity + * The own identity that added the identity + * @param identity + * The identity that was added + */ + public IdentityAddedEvent(OwnIdentity ownIdentity, Identity identity) { + super(ownIdentity, identity); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityEvent.java new file mode 100644 index 0000000..2727226 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityEvent.java @@ -0,0 +1,71 @@ +/* + * Sone - IdentityEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Base class for {@link Identity} events. + * + * @author David ‘Bombe’ Roden + */ +public abstract class IdentityEvent { + + /** The own identity this event relates to. */ + private final OwnIdentity ownIdentity; + + /** The identity this event is about. */ + private final Identity identity; + + /** + * Creates a new identity-based event. + * + * @param ownIdentity + * The own identity that relates to the identity + * @param identity + * The identity this event is about + */ + protected IdentityEvent(OwnIdentity ownIdentity, Identity identity) { + this.ownIdentity = ownIdentity; + this.identity = identity; + } + + // + // ACCESSORS + // + + /** + * Returns the own identity this event relates to. + * + * @return The own identity this event relates to + */ + public OwnIdentity ownIdentity() { + return ownIdentity; + } + + /** + * Returns the identity this event is about. + * + * @return The identity this event is about + */ + public Identity identity() { + return identity; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.java new file mode 100644 index 0000000..d5671cc --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.java @@ -0,0 +1,42 @@ +/* + * Sone - IdentityRemovedEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Event that signals that an {@link Identity} was removed. + * + * @author David ‘Bombe’ Roden + */ +public class IdentityRemovedEvent extends IdentityEvent { + + /** + * Creates a new “identity removed” event. + * + * @param ownIdentity + * The own identity that removed the identity + * @param identity + * The identity that was removed + */ + public IdentityRemovedEvent(OwnIdentity ownIdentity, Identity identity) { + super(ownIdentity, identity); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.java new file mode 100644 index 0000000..96ed694 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.java @@ -0,0 +1,42 @@ +/* + * Sone - IdentityUpdatedEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Event that signals that an {@link Identity} was updated. + * + * @author David ‘Bombe’ Roden + */ +public class IdentityUpdatedEvent extends IdentityEvent { + + /** + * Creates a new “identity updated” event. + * + * @param ownIdentity + * The own identity that tracks the identity + * @param identity + * The identity that was updated + */ + public IdentityUpdatedEvent(OwnIdentity ownIdentity, Identity identity) { + super(ownIdentity, identity); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.java new file mode 100644 index 0000000..aacf2e8 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - OwnIdentityAddedEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Event that signals that an {@link OwnIdentity} was added. + * + * @author David ‘Bombe’ Roden + */ +public class OwnIdentityAddedEvent extends OwnIdentityEvent { + + /** + * Creates new “own identity added” event. + * + * @param ownIdentity + * The own identity that was added + */ + public OwnIdentityAddedEvent(OwnIdentity ownIdentity) { + super(ownIdentity); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityEvent.java new file mode 100644 index 0000000..97179e8 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityEvent.java @@ -0,0 +1,55 @@ +/* + * Sone - OwnIdentityEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Base class for {@link OwnIdentity} events. + * + * @author David ‘Bombe’ Roden + */ +public abstract class OwnIdentityEvent { + + /** The own identity this event is about. */ + private final OwnIdentity ownIdentity; + + /** + * Creates a new own identity-based event. + * + * @param ownIdentity + * The own identity this event is about + */ + protected OwnIdentityEvent(OwnIdentity ownIdentity) { + this.ownIdentity = ownIdentity; + } + + // + // ACCESSORS + // + + /** + * Returns the own identity this event is about. + * + * @return The own identity this event is about + */ + public OwnIdentity ownIdentity() { + return ownIdentity; + } + +} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.java new file mode 100644 index 0000000..437bd6d --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.java @@ -0,0 +1,39 @@ +/* + * Sone - OwnIdentityRemovedEvent.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.freenet.wot.event; + +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Event that signals that an {@link OwnIdentity} was removed. + * + * @author David ‘Bombe’ Roden + */ +public class OwnIdentityRemovedEvent extends OwnIdentityEvent { + + /** + * Creates a new “own identity removed” event. + * + * @param ownIdentity + * The own identity that was removed + */ + public OwnIdentityRemovedEvent(OwnIdentity ownIdentity) { + super(ownIdentity); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index 5b28b85..9626c57 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -1,5 +1,5 @@ /* - * Sone - SonePlugin.java - Copyright © 2010–2012 David Roden + * Sone - SonePlugin.java - Copyright © 2010–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 @@ -25,6 +25,12 @@ import java.util.logging.Logger; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.FreenetInterface; import net.pterodactylus.sone.core.WebOfTrustUpdater; +import net.pterodactylus.sone.database.Database; +import net.pterodactylus.sone.database.PostBuilderFactory; +import net.pterodactylus.sone.database.PostProvider; +import net.pterodactylus.sone.database.PostReplyBuilderFactory; +import net.pterodactylus.sone.database.SoneProvider; +import net.pterodactylus.sone.database.memory.MemoryDatabase; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend; import net.pterodactylus.sone.freenet.plugin.PluginConnector; @@ -37,9 +43,23 @@ import net.pterodactylus.util.config.MapConfigurationBackend; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.logging.LoggingListener; import net.pterodactylus.util.version.Version; + +import com.google.common.eventbus.EventBus; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; +import com.google.inject.matcher.Matchers; +import com.google.inject.name.Names; +import com.google.inject.spi.InjectionListener; +import com.google.inject.spi.TypeEncounter; +import com.google.inject.spi.TypeListener; + import freenet.client.async.DatabaseDisabledException; import freenet.l10n.BaseL10n.LANGUAGE; import freenet.l10n.PluginL10n; +import freenet.node.Node; import freenet.pluginmanager.FredPlugin; import freenet.pluginmanager.FredPluginBaseL10n; import freenet.pluginmanager.FredPluginFCP; @@ -84,7 +104,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr } /** The version. */ - public static final Version VERSION = new Version(0, 8, 4); + public static final Version VERSION = new Version(0, 8, 5); /** The logger. */ private static final Logger logger = Logging.getLogger(SonePlugin.class); @@ -107,9 +127,6 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /** The web of trust connector. */ private WebOfTrustConnector webOfTrustConnector; - /** The identity manager. */ - private IdentityManager identityManager; - // // ACCESSORS // @@ -178,33 +195,72 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr } } - boolean startupFailed = true; - try { - /* create freenet interface. */ - FreenetInterface freenetInterface = new FreenetInterface(pluginRespirator.getNode()); + final Configuration startConfiguration = oldConfiguration; + final EventBus eventBus = new EventBus(); + + /* Freenet injector configuration. */ + AbstractModule freenetModule = new AbstractModule() { + + @Override + @SuppressWarnings("synthetic-access") + protected void configure() { + bind(PluginRespirator.class).toInstance(SonePlugin.this.pluginRespirator); + bind(Node.class).toInstance(SonePlugin.this.pluginRespirator.getNode()); + } + }; + /* Sone injector configuration. */ + AbstractModule soneModule = new AbstractModule() { - /* create web of trust connector. */ - PluginConnector pluginConnector = new PluginConnector(pluginRespirator); - webOfTrustConnector = new WebOfTrustConnector(pluginConnector); - identityManager = new IdentityManager(webOfTrustConnector, "Sone"); + @Override + protected void configure() { + bind(Core.class).in(Singleton.class); + bind(MemoryDatabase.class).in(Singleton.class); + bind(EventBus.class).toInstance(eventBus); + bind(Configuration.class).toInstance(startConfiguration); + bind(FreenetInterface.class).in(Singleton.class); + bind(PluginConnector.class).in(Singleton.class); + bind(WebOfTrustConnector.class).in(Singleton.class); + bind(WebOfTrustUpdater.class).in(Singleton.class); + bind(IdentityManager.class).in(Singleton.class); + bind(String.class).annotatedWith(Names.named("WebOfTrustContext")).toInstance("Sone"); + bind(SonePlugin.class).toInstance(SonePlugin.this); + bind(FcpInterface.class).in(Singleton.class); + bind(Database.class).to(MemoryDatabase.class); + bind(PostBuilderFactory.class).to(MemoryDatabase.class); + bind(PostReplyBuilderFactory.class).to(MemoryDatabase.class); + bind(SoneProvider.class).to(Core.class).in(Singleton.class); + bind(PostProvider.class).to(MemoryDatabase.class); + bindListener(Matchers.any(), new TypeListener() { + + @Override + public void hear(TypeLiteral typeLiteral, TypeEncounter typeEncounter) { + typeEncounter.register(new InjectionListener() { + + @Override + public void afterInjection(I injectee) { + eventBus.register(injectee); + } + }); + } + }); + } - /* create trust updater. */ - WebOfTrustUpdater trustUpdater = new WebOfTrustUpdater(webOfTrustConnector); - trustUpdater.init(); + }; + Injector injector = Guice.createInjector(freenetModule, soneModule); + core = injector.getInstance(Core.class); - /* create core. */ - core = new Core(oldConfiguration, freenetInterface, identityManager, trustUpdater); + /* create web of trust connector. */ + webOfTrustConnector = injector.getInstance(WebOfTrustConnector.class); - /* create the web interface. */ - webInterface = new WebInterface(this); - core.addCoreListener(webInterface); + /* create FCP interface. */ + fcpInterface = injector.getInstance(FcpInterface.class); + core.setFcpInterface(fcpInterface); - /* create FCP interface. */ - fcpInterface = new FcpInterface(core); - core.setFcpInterface(fcpInterface); + /* create the web interface. */ + webInterface = injector.getInstance(WebInterface.class); - /* create the identity manager. */ - identityManager.addIdentityListener(core); + boolean startupFailed = true; + try { /* start core! */ core.start(); @@ -215,7 +271,6 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr webInterface.start(); webInterface.setFirstStart(firstStart); webInterface.setNewConfig(newConfig); - identityManager.start(); startupFailed = false; } finally { if (startupFailed) { @@ -241,9 +296,6 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /* stop the core. */ core.stop(); - /* stop the identity manager. */ - identityManager.stop(); - /* stop the web of trust connector. */ webOfTrustConnector.stop(); } catch (Throwable t1) { diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotification.java b/src/main/java/net/pterodactylus/sone/notify/ListNotification.java index f1343ad..9d1203b 100644 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotification.java +++ b/src/main/java/net/pterodactylus/sone/notify/ListNotification.java @@ -1,5 +1,5 @@ /* - * Sone - ListNotification.java - Copyright © 2010–2012 David Roden + * Sone - ListNotification.java - Copyright © 2010–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 @@ -141,7 +141,9 @@ public class ListNotification extends TemplateNotification { * The element to remove */ public void remove(T element) { - elements.remove(element); + while (elements.remove(element)) { + /* do nothing, just remove all instances of the element. */ + } if (elements.isEmpty()) { dismiss(); } diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java index 6efee27..f431bb7 100644 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java +++ b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java @@ -1,5 +1,5 @@ /* - * Sone - ListNotificationFilters.java - Copyright © 2010–2012 David Roden + * Sone - ListNotificationFilters.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.notify; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -28,7 +30,8 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.util.notify.Notification; -import net.pterodactylus.util.validation.Validation; + +import com.google.common.base.Optional; /** * Filter for {@link ListNotification}s. @@ -218,7 +221,7 @@ public class ListNotificationFilters { * otherwise */ public static boolean isPostVisible(Sone sone, Post post) { - Validation.begin().isNotNull("Post", post).check(); + checkNotNull(post, "post must not be null"); Sone postSone = post.getSone(); if (postSone == null) { return false; @@ -240,9 +243,8 @@ public class ListNotificationFilters { * received trust values. to prevent this we simply assume that * posts are visible if there is no trust. */ - return true; } - if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.equals(post.getRecipient())) { + if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.getId().equals(post.getRecipientId().orNull())) { return false; } } @@ -280,12 +282,12 @@ public class ListNotificationFilters { * otherwise */ public static boolean isReplyVisible(Sone sone, PostReply reply) { - Validation.begin().isNotNull("Reply", reply).check(); - Post post = reply.getPost(); - if (post == null) { + checkNotNull(reply, "reply must not be null"); + Optional post = reply.getPost(); + if (!post.isPresent()) { return false; } - if (!isPostVisible(sone, post)) { + if (!isPostVisible(sone, post.get())) { return false; } if (reply.getTime() > System.currentTimeMillis()) { diff --git a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java index e22e00b..96891e5 100644 --- a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - AlbumAccessor.java - Copyright © 2011–2012 David Roden + * Sone - AlbumAccessor.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 diff --git a/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java b/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java index afa22ec..12b4bc2 100644 --- a/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - CollectionAccessor.java - Copyright © 2010–2012 David Roden + * Sone - CollectionAccessor.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java b/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java index 0282156..a3f8db6 100644 --- a/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java @@ -1,5 +1,5 @@ /* - * Sone - CssClassNameFilter.java - Copyright © 2010–2012 David Roden + * Sone - CssClassNameFilter.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java b/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java index dab01cc..d070fe9 100644 --- a/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java +++ b/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java @@ -1,5 +1,5 @@ /* - * Sone - GetPagePlugin.java - Copyright © 2010–2012 David Roden + * Sone - GetPagePlugin.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java b/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java index 8fadc6c..0d888bc 100644 --- a/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - HttpRequestAccessor.java - Copyright © 2011–2012 David Roden + * Sone - HttpRequestAccessor.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 diff --git a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java index 4fbe74c..c08a0a2 100644 --- a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - IdentityAccessor.java - Copyright © 2010–2012 David Roden + * Sone - IdentityAccessor.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java b/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java index ec7f360..3ac684c 100644 --- a/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - ImageAccessor.java - Copyright © 2011–2012 David Roden + * Sone - ImageAccessor.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 diff --git a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java index 06a11d7..7e3723c 100644 --- a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java @@ -1,5 +1,5 @@ /* - * Sone - ImageLinkFilter.java - Copyright © 2011–2012 David Roden + * Sone - ImageLinkFilter.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 @@ -24,13 +24,14 @@ import java.util.Map; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Image; import net.pterodactylus.util.number.Numbers; -import net.pterodactylus.util.object.Default; import net.pterodactylus.util.template.Filter; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.template.TemplateContextFactory; import net.pterodactylus.util.template.TemplateParser; +import com.google.common.base.Optional; + /** * Template filter that turns an {@link Image} into an HTML <img> tag, * using some parameters to influence parameters of the image. @@ -105,8 +106,8 @@ public class ImageLinkFilter implements Filter { linkTemplateContext.set("width", (int) (imageWidth * scale + 0.5)); linkTemplateContext.set("height", (int) (imageHeight * scale + 0.5)); } - linkTemplateContext.set("alt", Default.forNull(title, image.getDescription())); - linkTemplateContext.set("title", Default.forNull(title, image.getTitle())); + linkTemplateContext.set("alt", Optional.fromNullable(title).or(image.getDescription())); + linkTemplateContext.set("title", Optional.fromNullable(title).or(image.getTitle())); StringWriter stringWriter = new StringWriter(); linkTemplate.render(linkTemplateContext, stringWriter); diff --git a/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java b/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java index 2dc963f..9b8cd7e 100644 --- a/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java @@ -1,5 +1,5 @@ /* - * Sone - JavascriptFilter.java - Copyright © 2011–2012 David Roden + * Sone - JavascriptFilter.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 @@ -19,10 +19,11 @@ package net.pterodactylus.sone.template; import java.util.Map; -import net.pterodactylus.util.number.Hex; import net.pterodactylus.util.template.Filter; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.io.BaseEncoding; + /** * Escapes double quotes, backslashes, carriage returns and line feeds, and * additionally encloses a given string with double quotes to make it possible @@ -56,7 +57,7 @@ public class JavascriptFilter implements Filter { javascriptString.append('\\'); javascriptString.append(c); } else if (c < 32) { - javascriptString.append("\\x").append(Hex.toHex((byte) c)); + javascriptString.append("\\x").append(BaseEncoding.base16().lowerCase().encode(new byte[] { (byte) c })); } else { javascriptString.append(c); } diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java index 75eff8b..8833a2d 100644 --- a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java @@ -1,5 +1,5 @@ /* - * Sone - ParserFilter.java - Copyright © 2011–2012 David Roden + * Sone - ParserFilter.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 @@ -20,7 +20,9 @@ package net.pterodactylus.sone.template; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; +import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -92,7 +94,7 @@ public class ParserFilter implements Filter { int cutOffLength = Numbers.safeParseInteger(parameters.get("cut-off-length"), Numbers.safeParseInteger(templateContext.get(String.valueOf(parameters.get("cut-off-length"))), length)); Object sone = parameters.get("sone"); if (sone instanceof String) { - sone = core.getSone((String) sone, false); + sone = core.getSone((String) sone); } FreenetRequest request = (FreenetRequest) templateContext.get("request"); SoneTextParserContext context = new SoneTextParserContext(request, (Sone) sone); @@ -213,7 +215,12 @@ public class ParserFilter implements Filter { * The part to render */ private void render(Writer writer, LinkPart linkPart) { - renderLink(writer, "/?_CHECKED_HTTP_=" + linkPart.getLink(), linkPart.getText(), linkPart.getTitle(), "internet"); + try { + renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(), linkPart.getTitle(), "internet"); + } catch (UnsupportedEncodingException uee1) { + /* not possible for UTF-8. */ + throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1); + } } /** diff --git a/src/main/java/net/pterodactylus/sone/template/PostAccessor.java b/src/main/java/net/pterodactylus/sone/template/PostAccessor.java index d05b074..d593b51 100644 --- a/src/main/java/net/pterodactylus/sone/template/PostAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/PostAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - PostAccessor.java - Copyright © 2010–2012 David Roden + * Sone - PostAccessor.java - Copyright © 2010–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 @@ -21,10 +21,11 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.template.ReflectionAccessor; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.collect.Collections2; + /** * Accessor for {@link Post} objects that adds additional properties: *

@@ -56,7 +57,7 @@ public class PostAccessor extends ReflectionAccessor { public Object get(TemplateContext templateContext, Object object, String member) { Post post = (Post) object; if ("replies".equals(member)) { - return Filters.filteredList(core.getReplies(post), Reply.FUTURE_REPLY_FILTER); + return Collections2.filter(core.getReplies(post.getId()), Reply.FUTURE_REPLY_FILTER); } else if (member.equals("likes")) { return core.getLikes(post); } else if (member.equals("liked")) { diff --git a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java index f004967..30c0a52 100644 --- a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - ProfileAccessor.java - Copyright © 2011–2012 David Roden + * Sone - ProfileAccessor.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 @@ -70,7 +70,7 @@ public class ProfileAccessor extends ReflectionAccessor { return null; } Sone remoteSone = profile.getSone(); - if (core.isLocalSone(remoteSone)) { + if (remoteSone.isLocal()) { /* always show your own avatars. */ return avatarId; } diff --git a/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java b/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java index 4faa045..b497a94 100644 --- a/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - ReplyAccessor.java - Copyright © 2010–2012 David Roden + * Sone - ReplyAccessor.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java index ff4b64e..bcf73d8 100644 --- a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java @@ -1,5 +1,5 @@ /* - * Sone - ReplyGroupFilter.java - Copyright © 2010–2012 David Roden + * Sone - ReplyGroupFilter.java - Copyright © 2010–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 @@ -29,6 +29,8 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.util.template.Filter; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; + /** * {@link Filter} implementation that groups replies by the post the are in * reply to, returning a map with the post as key and the list of replies as @@ -48,17 +50,21 @@ public class ReplyGroupFilter implements Filter { Map> postSones = new HashMap>(); Map> postReplies = new HashMap>(); for (PostReply reply : allReplies) { - Post post = reply.getPost(); - Set sones = postSones.get(post); + /* + * All replies from a new-reply notification have posts, + * ListNotificationFilters takes care of that. + */ + Optional post = reply.getPost(); + Set sones = postSones.get(post.get()); if (sones == null) { sones = new HashSet(); - postSones.put(post, sones); + postSones.put(post.get(), sones); } sones.add(reply.getSone()); - Set replies = postReplies.get(post); + Set replies = postReplies.get(post.get()); if (replies == null) { replies = new HashSet(); - postReplies.put(post, replies); + postReplies.put(post.get(), replies); } replies.add(reply); } diff --git a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java index 3a89021..0d4f556 100644 --- a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java @@ -1,5 +1,5 @@ /* - * Sone - RequestChangeFilter.java - Copyright © 2010–2012 David Roden + * Sone - RequestChangeFilter.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java index eca8654..c54fc0d 100644 --- a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - SoneAccessor.java - Copyright © 2010–2012 David Roden + * Sone - SoneAccessor.java - Copyright © 2010–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 @@ -76,8 +76,6 @@ public class SoneAccessor extends ReflectionAccessor { Sone sone = (Sone) object; if (member.equals("niceName")) { return getNiceName(sone); - } else if (member.equals("local")) { - return core.isLocalSone(sone); } else if (member.equals("friend")) { Sone currentSone = (Sone) templateContext.get("currentSone"); return (currentSone != null) && currentSone.hasFriend(sone.getId()); diff --git a/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java b/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java index 005eb1f..913e058 100644 --- a/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java @@ -1,5 +1,5 @@ /* - * Sone - SubstringFilter.java - Copyright © 2010–2012 David Roden + * Sone - SubstringFilter.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java b/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java index 5097cb2..7ba7a10 100644 --- a/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - TrustAccessor.java - Copyright © 2010–2012 David Roden + * Sone - TrustAccessor.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java b/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java index e3ddd59..fb5551d 100644 --- a/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java @@ -1,5 +1,5 @@ /* - * Sone - UniqueElementFilter.java - Copyright © 2011–2012 David Roden + * Sone - UniqueElementFilter.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 diff --git a/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java b/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java index c87a97b..077e1fd 100644 --- a/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java @@ -1,5 +1,5 @@ /* - * Sone - UnknownDateFilter.java - Copyright © 2011–2012 David Roden + * Sone - UnknownDateFilter.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 diff --git a/src/main/java/net/pterodactylus/sone/text/FreenetLinkPart.java b/src/main/java/net/pterodactylus/sone/text/FreenetLinkPart.java index cfd2f63..c667f38 100644 --- a/src/main/java/net/pterodactylus/sone/text/FreenetLinkPart.java +++ b/src/main/java/net/pterodactylus/sone/text/FreenetLinkPart.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetLinkPart.java - Copyright © 2011–2012 David Roden + * Sone - FreenetLinkPart.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 diff --git a/src/main/java/net/pterodactylus/sone/text/LinkPart.java b/src/main/java/net/pterodactylus/sone/text/LinkPart.java index 202b9db..f531bab 100644 --- a/src/main/java/net/pterodactylus/sone/text/LinkPart.java +++ b/src/main/java/net/pterodactylus/sone/text/LinkPart.java @@ -1,5 +1,5 @@ /* - * Sone - LinkPart.java - Copyright © 2011–2012 David Roden + * Sone - LinkPart.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 diff --git a/src/main/java/net/pterodactylus/sone/text/Parser.java b/src/main/java/net/pterodactylus/sone/text/Parser.java index 3a80db8..2a7700a 100644 --- a/src/main/java/net/pterodactylus/sone/text/Parser.java +++ b/src/main/java/net/pterodactylus/sone/text/Parser.java @@ -1,5 +1,5 @@ /* - * Sone - Parser.java - Copyright © 2010–2012 David Roden + * Sone - Parser.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/text/ParserContext.java b/src/main/java/net/pterodactylus/sone/text/ParserContext.java index d044d1b..4734906 100644 --- a/src/main/java/net/pterodactylus/sone/text/ParserContext.java +++ b/src/main/java/net/pterodactylus/sone/text/ParserContext.java @@ -1,5 +1,5 @@ /* - * Sone - ParserContext.java - Copyright © 2010–2012 David Roden + * Sone - ParserContext.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/text/Part.java b/src/main/java/net/pterodactylus/sone/text/Part.java index 79c59dc..4c1a188 100644 --- a/src/main/java/net/pterodactylus/sone/text/Part.java +++ b/src/main/java/net/pterodactylus/sone/text/Part.java @@ -1,5 +1,5 @@ /* - * Sone - Part.java - Copyright © 2010–2012 David Roden + * Sone - Part.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/text/PartContainer.java b/src/main/java/net/pterodactylus/sone/text/PartContainer.java index a8a7e85..7d8acd6 100644 --- a/src/main/java/net/pterodactylus/sone/text/PartContainer.java +++ b/src/main/java/net/pterodactylus/sone/text/PartContainer.java @@ -1,5 +1,5 @@ /* - * Sone - PartContainer.java - Copyright © 2010–2012 David Roden + * Sone - PartContainer.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java b/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java index 2c29ee2..aa6441b 100644 --- a/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PlainTextPart.java @@ -1,5 +1,5 @@ /* - * Sone - PlainTextPart.java - Copyright © 2011–2012 David Roden + * Sone - PlainTextPart.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 diff --git a/src/main/java/net/pterodactylus/sone/text/PostPart.java b/src/main/java/net/pterodactylus/sone/text/PostPart.java index 6241b7a..f7177c9 100644 --- a/src/main/java/net/pterodactylus/sone/text/PostPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PostPart.java @@ -1,5 +1,5 @@ /* - * Sone - PostLinkPart.java - Copyright © 2011–2012 David Roden + * Sone - PostPart.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 diff --git a/src/main/java/net/pterodactylus/sone/text/SonePart.java b/src/main/java/net/pterodactylus/sone/text/SonePart.java index 37f098b..a7b418b 100644 --- a/src/main/java/net/pterodactylus/sone/text/SonePart.java +++ b/src/main/java/net/pterodactylus/sone/text/SonePart.java @@ -1,5 +1,5 @@ /* - * Sone - SoneLinkPart.java - Copyright © 2011–2012 David Roden + * Sone - SonePart.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 diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index baa3dc9..6d81c0d 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetLinkParser.java - Copyright © 2010–2012 David Roden + * Sone - SoneTextParser.java - Copyright © 2010–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 @@ -26,12 +26,15 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.pterodactylus.sone.core.PostProvider; -import net.pterodactylus.sone.core.SoneProvider; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.PostProvider; +import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; + +import com.google.common.base.Optional; + import freenet.keys.FreenetURI; /** @@ -55,28 +58,50 @@ public class SoneTextParser implements Parser { private enum LinkType { /** Link is a KSK. */ - KSK, + KSK("KSK@"), /** Link is a CHK. */ - CHK, + CHK("CHK@"), /** Link is an SSK. */ - SSK, + SSK("SSK@"), /** Link is a USK. */ - USK, + USK("USK@"), /** Link is HTTP. */ - HTTP, + HTTP("http://"), /** Link is HTTPS. */ - HTTPS, + HTTPS("https://"), /** Link is a Sone. */ - SONE, + SONE("sone://"), /** Link is a post. */ - POST, + POST("post://"); + + /** The scheme identifying this link type. */ + private final String scheme; + + /** + * Creates a new link type identified by the given scheme. + * + * @param scheme + * The scheme of the link type + */ + private LinkType(String scheme) { + this.scheme = scheme; + } + + /** + * Returns the scheme of this link type. + * + * @return The scheme of this link type + */ + public String getScheme() { + return scheme; + } } @@ -200,18 +225,32 @@ public class SoneTextParser implements Parser { } lineComplete = false; + Matcher matcher = whitespacePattern.matcher(line); + int nextSpace = matcher.find(0) ? matcher.start() : line.length(); + String link = line.substring(0, nextSpace); + String name = link; + logger.log(Level.FINER, String.format("Found link: %s", link)); + logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); + + /* if there is no text after the scheme, it’s not a link! */ + if (link.equals(linkType.getScheme())) { + parts.add(new PlainTextPart(linkType.getScheme())); + line = line.substring(linkType.getScheme().length()); + continue; + } + if (linkType == LinkType.SONE) { if (line.length() >= (7 + 43)) { String soneId = line.substring(7, 50); - Sone sone = soneProvider.getSone(soneId, false); - if (sone == null) { + Optional sone = soneProvider.getSone(soneId); + if (!sone.isPresent()) { /* * don’t use create=true above, we don’t want * the empty shell. */ - sone = new Sone(soneId); + sone = Optional.fromNullable(new Sone(soneId, false)); } - parts.add(new SonePart(sone)); + parts.add(new SonePart(sone.get())); line = line.substring(50); } else { parts.add(new PlainTextPart(line)); @@ -222,9 +261,9 @@ public class SoneTextParser implements Parser { if (linkType == LinkType.POST) { if (line.length() >= (7 + 36)) { String postId = line.substring(7, 43); - Post post = postProvider.getPost(postId, false); - if ((post != null) && (post.getSone() != null)) { - parts.add(new PostPart(post)); + Optional post = postProvider.getPost(postId); + if (post.isPresent()) { + parts.add(new PostPart(post.get())); } else { parts.add(new PlainTextPart(line.substring(0, 43))); } @@ -235,12 +274,6 @@ public class SoneTextParser implements Parser { } continue; } - Matcher matcher = whitespacePattern.matcher(line); - int nextSpace = matcher.find(0) ? matcher.start() : line.length(); - String link = line.substring(0, nextSpace); - String name = link; - logger.log(Level.FINER, String.format("Found link: %s", link)); - logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) { FreenetURI uri; diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java index e1a6eb3..ba00916 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java @@ -1,5 +1,5 @@ /* - * Sone - SoneTextParserContext.java - Copyright © 2011–2012 David Roden + * Sone - SoneTextParserContext.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 diff --git a/src/main/java/net/pterodactylus/sone/text/TextFilter.java b/src/main/java/net/pterodactylus/sone/text/TextFilter.java index dfd9627..f07a000 100644 --- a/src/main/java/net/pterodactylus/sone/text/TextFilter.java +++ b/src/main/java/net/pterodactylus/sone/text/TextFilter.java @@ -1,5 +1,5 @@ /* - * Sone - TextFilter.java - Copyright © 2011–2012 David Roden + * Sone - TextFilter.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 diff --git a/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java b/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java new file mode 100644 index 0000000..db6a841 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java @@ -0,0 +1,62 @@ +/* + * Sone - IntegerRangePredicate.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.utils; + +import com.google.common.base.Predicate; + +/** + * {@link Predicate} that verifies that an {@link Integer} value is not + * {@code null} and is between a lower and an upper bound. Both bounds are + * inclusive. + * + * @author David ‘Bombe’ Roden + */ +public class IntegerRangePredicate implements Predicate { + + /** The lower bound. */ + private final int lowerBound; + + /** The upper bound. */ + private final int upperBound; + + /** + * Creates a new integer range predicate. + * + * @param lowerBound + * The lower bound + * @param upperBound + * The upper bound + */ + public IntegerRangePredicate(int lowerBound, int upperBound) { + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + // + // PREDICATE METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public boolean apply(Integer value) { + return (value != null) && (value >= lowerBound) && (value <= upperBound); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/web/AboutPage.java b/src/main/java/net/pterodactylus/sone/web/AboutPage.java index 2e9780b..6a6bc63 100644 --- a/src/main/java/net/pterodactylus/sone/web/AboutPage.java +++ b/src/main/java/net/pterodactylus/sone/web/AboutPage.java @@ -1,5 +1,5 @@ /* - * Sone - AboutPage.java - Copyright © 2010–2012 David Roden + * Sone - AboutPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java b/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java index 0c9bbae..042a683 100644 --- a/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java +++ b/src/main/java/net/pterodactylus/sone/web/BookmarkPage.java @@ -1,5 +1,5 @@ /* - * Sone - BookmarkPage.java - Copyright © 2011–2012 David Roden + * Sone - BookmarkPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java b/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java index 95dfd65..10b6dc3 100644 --- a/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java +++ b/src/main/java/net/pterodactylus/sone/web/BookmarksPage.java @@ -1,5 +1,5 @@ /* - * Sone - BookmarksPage.java - Copyright © 2011–2012 David Roden + * Sone - BookmarksPage.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 @@ -18,6 +18,7 @@ package net.pterodactylus.sone.web; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -25,12 +26,13 @@ import java.util.Set; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.collection.Pagination; -import net.pterodactylus.util.collection.filter.Filter; -import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; + /** * Page that lets the user browse all his bookmarked posts. * @@ -61,10 +63,10 @@ public class BookmarksPage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); Set allPosts = webInterface.getCore().getBookmarkedPosts(); - Set loadedPosts = Filters.filteredSet(allPosts, new Filter() { + Collection loadedPosts = Collections2.filter(allPosts, new Predicate() { @Override - public boolean filterObject(Post post) { + public boolean apply(Post post) { return post.getSone() != null; } }); diff --git a/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java index 1e83a0e..95ca0da 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java @@ -1,5 +1,5 @@ /* - * Sone - CreateAlbumPage.java - Copyright © 2011–2012 David Roden + * Sone - CreateAlbumPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java index 6b374dc9..83913b4 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java @@ -1,5 +1,5 @@ /* - * Sone - CreatePostPage.java - Copyright © 2010–2012 David Roden + * Sone - CreatePostPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.text.TextFilter; @@ -65,7 +67,7 @@ public class CreatePostPage extends SoneTemplatePage { if (sender == null) { sender = currentSone; } - Sone recipient = webInterface.getCore().getSone(recipientId, false); + Optional recipient = webInterface.getCore().getSone(recipientId); text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); webInterface.getCore().createPost(sender, recipient, System.currentTimeMillis(), text); throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java index 82e4c51..c8979c1 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java @@ -1,5 +1,5 @@ /* - * Sone - CreateReplyPage.java - Copyright © 2010–2012 David Roden + * Sone - CreateReplyPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.text.TextFilter; @@ -58,7 +60,10 @@ public class CreateReplyPage extends SoneTemplatePage { String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim(); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); if (request.getMethod() == Method.POST) { - Post post = webInterface.getCore().getPost(postId); + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { + throw new RedirectException("noPermission.html"); + } if (text.length() > 0) { String senderId = request.getHttpRequest().getPartAsStringFailsafe("sender", 43); Sone sender = webInterface.getCore().getLocalSone(senderId, false); @@ -66,7 +71,7 @@ public class CreateReplyPage extends SoneTemplatePage { sender = getCurrentSone(request.getToadletContext()); } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - webInterface.getCore().createReply(sender, post, text); + webInterface.getCore().createReply(sender, post.get(), text); throw new RedirectException(returnPage); } templateContext.set("errorTextEmpty", true); diff --git a/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java b/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java index a4c6379..aea76c4 100644 --- a/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/CreateSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - CreateSonePage.java - Copyright © 2010–2012 David Roden + * Sone - CreateSonePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java index 2f2b766..3dfb8b4 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteAlbumPage.java - Copyright © 2011–2012 David Roden + * Sone - DeleteAlbumPage.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 @@ -54,7 +54,7 @@ public class DeleteAlbumPage extends SoneTemplatePage { if (album == null) { throw new RedirectException("invalid.html"); } - if (!webInterface.getCore().isLocalSone(album.getSone())) { + if (!album.getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getHttpRequest().isPartSet("abortDelete")) { diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java b/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java index 3bbaf3d..77f3ab8 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteImagePage.java - Copyright © 2011–2012 David Roden + * Sone - DeleteImagePage.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 @@ -57,7 +57,7 @@ public class DeleteImagePage extends SoneTemplatePage { if (image == null) { throw new RedirectException("invalid.html"); } - if (!webInterface.getCore().isLocalSone(image.getSone())) { + if (!image.getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getMethod() == Method.POST) { diff --git a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java index 6689061..6ef86f0 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeletePostPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeletePostPage.java - Copyright © 2010–2012 David Roden + * Sone - DeletePostPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -55,19 +57,22 @@ public class DeletePostPage extends SoneTemplatePage { if (request.getMethod() == Method.GET) { String postId = request.getHttpRequest().getParam("post"); String returnPage = request.getHttpRequest().getParam("returnPage"); - Post post = webInterface.getCore().getPost(postId); - templateContext.set("post", post); + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { + throw new RedirectException("noPermission.html"); + } + templateContext.set("post", post.get()); templateContext.set("returnPage", returnPage); return; } else if (request.getMethod() == Method.POST) { String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); - Post post = webInterface.getCore().getPost(postId); - if (!webInterface.getCore().isLocalSone(post.getSone())) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent() || !post.get().getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getHttpRequest().isPartSet("confirmDelete")) { - webInterface.getCore().deletePost(post); + webInterface.getCore().deletePost(post.get()); throw new RedirectException(returnPage); } else if (request.getHttpRequest().isPartSet("abortDelete")) { throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java index 5754b2f..852cc91 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteProfileFieldPage.java - Copyright © 2011–2012 David Roden + * Sone - DeleteProfileFieldPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java index 900acd9..f7ca132 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteReplyPage.java - Copyright © 2010–2012 David Roden + * Sone - DeleteReplyPage.java - Copyright © 2010–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 @@ -23,6 +23,8 @@ import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.web.Method; +import com.google.common.base.Optional; + /** * This page lets the user delete a reply. * @@ -53,14 +55,14 @@ public class DeleteReplyPage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36); - PostReply reply = webInterface.getCore().getReply(replyId); + Optional reply = webInterface.getCore().getPostReply(replyId); String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); if (request.getMethod() == Method.POST) { - if (!webInterface.getCore().isLocalSone(reply.getSone())) { + if (!reply.get().getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if (request.getHttpRequest().isPartSet("confirmDelete")) { - webInterface.getCore().deleteReply(reply); + webInterface.getCore().deleteReply(reply.get()); throw new RedirectException(returnPage); } else if (request.getHttpRequest().isPartSet("abortDelete")) { throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java b/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java index a5bdd2f..39d3fb3 100644 --- a/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteSonePage.java - Copyright © 2010–2012 David Roden + * Sone - DeleteSonePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java b/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java index 763271c..40efa5d 100644 --- a/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java @@ -1,5 +1,5 @@ /* - * Sone - DismissNotificationPage.java - Copyright © 2010–2012 David Roden + * Sone - DismissNotificationPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/DistrustPage.java b/src/main/java/net/pterodactylus/sone/web/DistrustPage.java index e4fd52d..c8879a0 100644 --- a/src/main/java/net/pterodactylus/sone/web/DistrustPage.java +++ b/src/main/java/net/pterodactylus/sone/web/DistrustPage.java @@ -1,5 +1,5 @@ /* - * Sone - DistrustPage.java - Copyright © 2011–2012 David Roden + * Sone - DistrustPage.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -59,9 +61,9 @@ public class DistrustPage extends SoneTemplatePage { String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44); Sone currentSone = getCurrentSone(request.getToadletContext()); - Sone sone = webInterface.getCore().getSone(identity, false); - if (sone != null) { - webInterface.getCore().distrustSone(currentSone, sone); + Optional sone = webInterface.getCore().getSone(identity); + if (sone.isPresent()) { + webInterface.getCore().distrustSone(currentSone, sone.get()); } throw new RedirectException(returnPage); } diff --git a/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java b/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java index d028f8d..8ef03e3 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java @@ -1,5 +1,5 @@ /* - * Sone - EditAlbumPage.java - Copyright © 2011–2012 David Roden + * Sone - EditAlbumPage.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 @@ -57,7 +57,7 @@ public class EditAlbumPage extends SoneTemplatePage { if (album == null) { throw new RedirectException("invalid.html"); } - if (!webInterface.getCore().isLocalSone(album.getSone())) { + if (!album.getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveLeft", 4))) { diff --git a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java index b0297a8..75872eb 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditImagePage.java @@ -1,5 +1,5 @@ /* - * FreenetSone - WebInterface.java - Copyright © 2010–2012 David Roden + * Sone - EditImagePage.java - Copyright © 2010–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 @@ -60,12 +60,12 @@ public class EditImagePage extends SoneTemplatePage { if (image == null) { throw new RedirectException("invalid.html"); } - if (!webInterface.getCore().isLocalSone(image.getSone())) { + if (!image.getSone().isLocal()) { throw new RedirectException("noPermission.html"); } if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveLeft", 4))) { image.getAlbum().moveImageUp(image); - } else if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveRight", 4))) { + } else if ("true".equals(request.getHttpRequest().getPartAsStringFailsafe("moveRight", 4))) { image.getAlbum().moveImageDown(image); } else { String title = request.getHttpRequest().getPartAsStringFailsafe("title", 100).trim(); diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java b/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java index 9958a2d..fe9d78f 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java @@ -1,5 +1,5 @@ /* - * Sone - EditProfileFieldPage.java - Copyright © 2011–2012 David Roden + * Sone - EditProfileFieldPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java index a1b759b..c79a1e9 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java @@ -1,5 +1,5 @@ /* - * Sone - EditProfilePage.java - Copyright © 2010–2012 David Roden + * Sone - EditProfilePage.java - Copyright © 2010–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 @@ -77,7 +77,7 @@ public class EditProfilePage extends SoneTemplatePage { birthDay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim()); birthMonth = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim()); birthYear = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim()); - avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatar-id", 36); + avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatarId", 36); profile.setFirstName(firstName.length() > 0 ? firstName : null); profile.setMiddleName(middleName.length() > 0 ? middleName : null); profile.setLastName(lastName.length() > 0 ? lastName : null); @@ -139,7 +139,7 @@ public class EditProfilePage extends SoneTemplatePage { templateContext.set("birthDay", birthDay); templateContext.set("birthMonth", birthMonth); templateContext.set("birthYear", birthYear); - templateContext.set("avatar-id", avatarId); + templateContext.set("avatarId", avatarId); templateContext.set("fields", fields); } diff --git a/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java b/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java index 661ddd3..11a680c 100644 --- a/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/FollowSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - FollowSonePage.java - Copyright © 2010–2012 David Roden + * Sone - FollowSonePage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; @@ -55,9 +57,10 @@ public class FollowSonePage extends SoneTemplatePage { Sone currentSone = getCurrentSone(request.getToadletContext()); String soneIds = request.getHttpRequest().getPartAsStringFailsafe("sone", 1200); for (String soneId : soneIds.split("[ ,]+")) { - if (webInterface.getCore().hasSone(soneId)) { + Optional sone = webInterface.getCore().getSone(soneId); + if (sone.isPresent()) { webInterface.getCore().followSone(currentSone, soneId); - webInterface.getCore().markSoneKnown(webInterface.getCore().getSone(soneId)); + webInterface.getCore().markSoneKnown(sone.get()); } } throw new RedirectException(returnPage); diff --git a/src/main/java/net/pterodactylus/sone/web/GetImagePage.java b/src/main/java/net/pterodactylus/sone/web/GetImagePage.java index a16c7db..442ce7d 100644 --- a/src/main/java/net/pterodactylus/sone/web/GetImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/GetImagePage.java @@ -1,5 +1,5 @@ /* - * Sone - GetImagePage.java - Copyright © 2011–2012 David Roden + * Sone - GetImagePage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java index da48ec5..37268ec 100644 --- a/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java @@ -1,5 +1,5 @@ /* - * Sone - ImageBrowserPage.java - Copyright © 2011–2012 David Roden + * Sone - ImageBrowserPage.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 @@ -22,6 +22,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.google.common.base.Optional; +import com.google.common.collect.FluentIterable; + import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Sone; @@ -77,9 +80,9 @@ public class ImageBrowserPage extends SoneTemplatePage { } String soneId = request.getHttpRequest().getParam("sone", null); if (soneId != null) { - Sone sone = webInterface.getCore().getSone(soneId, false); + Optional sone = webInterface.getCore().getSone(soneId); templateContext.set("soneRequested", true); - templateContext.set("sone", sone); + templateContext.set("sone", sone.orNull()); return; } String mode = request.getHttpRequest().getParam("mode", null); @@ -87,7 +90,7 @@ public class ImageBrowserPage extends SoneTemplatePage { templateContext.set("galleryRequested", true); List albums = new ArrayList(); for (Sone sone : webInterface.getCore().getSones()) { - albums.addAll(sone.getAllAlbums()); + albums.addAll(FluentIterable.from(sone.getAlbums()).transformAndConcat(Album.FLATTENER).toList()); } Collections.sort(albums, Album.TITLE_COMPARATOR); Pagination albumPagination = new Pagination(albums, 12).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0)); diff --git a/src/main/java/net/pterodactylus/sone/web/IndexPage.java b/src/main/java/net/pterodactylus/sone/web/IndexPage.java index 8a5d1b5..e1a52b9 100644 --- a/src/main/java/net/pterodactylus/sone/web/IndexPage.java +++ b/src/main/java/net/pterodactylus/sone/web/IndexPage.java @@ -1,5 +1,5 @@ /* - * Sone - IndexPage.java - Copyright © 2010–2012 David Roden + * Sone - IndexPage.java - Copyright © 2010–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 @@ -18,6 +18,7 @@ package net.pterodactylus.sone.web; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -26,12 +27,14 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.notify.ListNotificationFilters; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.collection.Pagination; -import net.pterodactylus.util.collection.filter.Filter; -import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; + /** * The index page shows the main page of Sone. This page will contain the posts * of all friends of the current user. @@ -61,31 +64,33 @@ public class IndexPage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); final Sone currentSone = getCurrentSone(request.getToadletContext()); - List allPosts = new ArrayList(); + Collection allPosts = new ArrayList(); allPosts.addAll(currentSone.getPosts()); for (String friendSoneId : currentSone.getFriends()) { - if (!webInterface.getCore().hasSone(friendSoneId)) { + Optional friendSone = webInterface.getCore().getSone(friendSoneId); + if (!friendSone.isPresent()) { continue; } - allPosts.addAll(webInterface.getCore().getSone(friendSoneId).getPosts()); + allPosts.addAll(friendSone.get().getPosts()); } for (Sone sone : webInterface.getCore().getSones()) { for (Post post : sone.getPosts()) { - if (currentSone.equals(post.getRecipient()) && !allPosts.contains(post)) { + if (currentSone.equals(post.getRecipient().orNull()) && !allPosts.contains(post)) { allPosts.add(post); } } } - allPosts = Filters.filteredList(allPosts, new Filter() { + allPosts = Collections2.filter(allPosts, new Predicate() { @Override - public boolean filterObject(Post post) { + public boolean apply(Post post) { return ListNotificationFilters.isPostVisible(currentSone, post); } }); - allPosts = Filters.filteredList(allPosts, Post.FUTURE_POSTS_FILTER); - Collections.sort(allPosts, Post.TIME_COMPARATOR); - Pagination pagination = new Pagination(allPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0)); + allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER); + List sortedPosts = new ArrayList(allPosts); + Collections.sort(sortedPosts, Post.TIME_COMPARATOR); + Pagination pagination = new Pagination(sortedPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0)); templateContext.set("pagination", pagination); templateContext.set("posts", pagination.getItems()); } diff --git a/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java b/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java index b40515a..dd1b1fc 100644 --- a/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java +++ b/src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java @@ -1,5 +1,5 @@ /* - * Sone - KnownSonesPage.java - Copyright © 2010–2012 David Roden + * Sone - KnownSonesPage.java - Copyright © 2010–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 @@ -18,19 +18,22 @@ package net.pterodactylus.sone.web; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.collection.Pagination; -import net.pterodactylus.util.collection.ReverseComparator; -import net.pterodactylus.util.collection.filter.Filter; -import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.Ordering; + /** * This page shows all known Sones. * @@ -67,70 +70,77 @@ public class KnownSonesPage extends SoneTemplatePage { templateContext.set("order", (sortOrder != null) ? sortOrder : "asc"); templateContext.set("filter", filter); final Sone currentSone = getCurrentSone(request.getToadletContext(), false); - List knownSones = Filters.filteredList(new ArrayList(webInterface.getCore().getSones()), Sone.EMPTY_SONE_FILTER); + Collection knownSones = Collections2.filter(webInterface.getCore().getSones(), Sone.EMPTY_SONE_FILTER); if ((currentSone != null) && "followed".equals(filter)) { - knownSones = Filters.filteredList(knownSones, new Filter() { + knownSones = Collections2.filter(knownSones, new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return currentSone.hasFriend(sone.getId()); } }); } else if ((currentSone != null) && "not-followed".equals(filter)) { - knownSones = Filters.filteredList(knownSones, new Filter() { + knownSones = Collections2.filter(knownSones, new Predicate() { @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return !currentSone.hasFriend(sone.getId()); } }); } else if ("new".equals(filter)) { - knownSones = Filters.filteredList(knownSones, new Filter() { + knownSones = Collections2.filter(knownSones, new Predicate() { + /** * {@inheritDoc} */ @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return !sone.isKnown(); } }); } else if ("not-new".equals(filter)) { - knownSones = Filters.filteredList(knownSones, new Filter() { + knownSones = Collections2.filter(knownSones, new Predicate() { + /** * {@inheritDoc} */ @Override - public boolean filterObject(Sone sone) { + public boolean apply(Sone sone) { return sone.isKnown(); } }); + } else if ("own".equals(filter)) { + knownSones = Collections2.filter(knownSones, Sone.LOCAL_SONE_FILTER); + } else if ("not-own".equals(filter)) { + knownSones = Collections2.filter(knownSones, Predicates.not(Sone.LOCAL_SONE_FILTER)); } + List sortedSones = new ArrayList(knownSones); if ("activity".equals(sortField)) { if ("asc".equals(sortOrder)) { - Collections.sort(knownSones, new ReverseComparator(Sone.LAST_ACTIVITY_COMPARATOR)); + Collections.sort(sortedSones, Ordering.from(Sone.LAST_ACTIVITY_COMPARATOR).reverse()); } else { - Collections.sort(knownSones, Sone.LAST_ACTIVITY_COMPARATOR); + Collections.sort(sortedSones, Sone.LAST_ACTIVITY_COMPARATOR); } } else if ("posts".equals(sortField)) { if ("asc".equals(sortOrder)) { - Collections.sort(knownSones, new ReverseComparator(Sone.POST_COUNT_COMPARATOR)); + Collections.sort(sortedSones, Ordering.from(Sone.POST_COUNT_COMPARATOR).reverse()); } else { - Collections.sort(knownSones, Sone.POST_COUNT_COMPARATOR); + Collections.sort(sortedSones, Sone.POST_COUNT_COMPARATOR); } } else if ("images".equals(sortField)) { if ("asc".equals(sortOrder)) { - Collections.sort(knownSones, new ReverseComparator(Sone.IMAGE_COUNT_COMPARATOR)); + Collections.sort(sortedSones, Ordering.from(Sone.IMAGE_COUNT_COMPARATOR).reverse()); } else { - Collections.sort(knownSones, Sone.IMAGE_COUNT_COMPARATOR); + Collections.sort(sortedSones, Sone.IMAGE_COUNT_COMPARATOR); } } else { if ("desc".equals(sortOrder)) { - Collections.sort(knownSones, new ReverseComparator(Sone.NICE_NAME_COMPARATOR)); + Collections.sort(sortedSones, Ordering.from(Sone.NICE_NAME_COMPARATOR).reverse()); } else { - Collections.sort(knownSones, Sone.NICE_NAME_COMPARATOR); + Collections.sort(sortedSones, Sone.NICE_NAME_COMPARATOR); } } - Pagination sonePagination = new Pagination(knownSones, 25).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0)); + Pagination sonePagination = new Pagination(sortedSones, 25).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0)); templateContext.set("pagination", sonePagination); templateContext.set("knownSones", sonePagination.getItems()); } diff --git a/src/main/java/net/pterodactylus/sone/web/LikePage.java b/src/main/java/net/pterodactylus/sone/web/LikePage.java index 960f32b..7f08e65 100644 --- a/src/main/java/net/pterodactylus/sone/web/LikePage.java +++ b/src/main/java/net/pterodactylus/sone/web/LikePage.java @@ -1,5 +1,5 @@ /* - * Sone - LikePage.java - Copyright © 2010–2012 David Roden + * Sone - LikePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/LockSonePage.java b/src/main/java/net/pterodactylus/sone/web/LockSonePage.java index 662e833..604b176 100644 --- a/src/main/java/net/pterodactylus/sone/web/LockSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/LockSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - LockSonePage.java - Copyright © 2010–2012 David Roden + * Sone - LockSonePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/LoginPage.java b/src/main/java/net/pterodactylus/sone/web/LoginPage.java index a2fb9bc..a837bd1 100644 --- a/src/main/java/net/pterodactylus/sone/web/LoginPage.java +++ b/src/main/java/net/pterodactylus/sone/web/LoginPage.java @@ -1,5 +1,5 @@ /* - * Sone - LoginPage.java - Copyright © 2010–2012 David Roden + * Sone - LoginPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/LogoutPage.java b/src/main/java/net/pterodactylus/sone/web/LogoutPage.java index 9b712c6..bc4c56d 100644 --- a/src/main/java/net/pterodactylus/sone/web/LogoutPage.java +++ b/src/main/java/net/pterodactylus/sone/web/LogoutPage.java @@ -1,5 +1,5 @@ /* - * Sone - LogoutPage.java - Copyright © 2010–2012 David Roden + * Sone - LogoutPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java b/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java index 4f8e071..66ac635 100644 --- a/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java +++ b/src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java @@ -1,5 +1,5 @@ /* - * Sone - MarkAsKnownPage.java - Copyright © 2011–2012 David Roden + * Sone - MarkAsKnownPage.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 @@ -27,6 +27,8 @@ import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; + /** * Page that lets the user mark a number of {@link Sone}s, {@link Post}s, or * {@link Reply Replie}s as known. @@ -65,23 +67,23 @@ public class MarkAsKnownPage extends SoneTemplatePage { for (StringTokenizer idTokenizer = new StringTokenizer(ids); idTokenizer.hasMoreTokens();) { String id = idTokenizer.nextToken(); if (type.equals("post")) { - Post post = webInterface.getCore().getPost(id, false); - if (post == null) { + Optional post = webInterface.getCore().getPost(id); + if (!post.isPresent()) { continue; } - webInterface.getCore().markPostKnown(post); + webInterface.getCore().markPostKnown(post.get()); } else if (type.equals("reply")) { - PostReply reply = webInterface.getCore().getReply(id, false); - if (reply == null) { + Optional reply = webInterface.getCore().getPostReply(id); + if (!reply.isPresent()) { continue; } - webInterface.getCore().markReplyKnown(reply); + webInterface.getCore().markReplyKnown(reply.get()); } else if (type.equals("sone")) { - Sone sone = webInterface.getCore().getSone(id, false); - if (sone == null) { + Optional sone = webInterface.getCore().getSone(id); + if (!sone.isPresent()) { continue; } - webInterface.getCore().markSoneKnown(sone); + webInterface.getCore().markSoneKnown(sone.get()); } } String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); diff --git a/src/main/java/net/pterodactylus/sone/web/NewPage.java b/src/main/java/net/pterodactylus/sone/web/NewPage.java index c7281e3..71200d8 100644 --- a/src/main/java/net/pterodactylus/sone/web/NewPage.java +++ b/src/main/java/net/pterodactylus/sone/web/NewPage.java @@ -1,5 +1,5 @@ /* - * Sone - NewPage.java - Copyright © 2012 David Roden + * Sone - NewPage.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 @@ -19,9 +19,12 @@ package net.pterodactylus.sone.web; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; +import com.google.common.collect.Collections2; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.notify.ListNotificationFilters; @@ -57,16 +60,16 @@ public class NewPage extends SoneTemplatePage { // /** - * {@inherit} + * {@inheritDoc} */ @Override protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); /* collect new elements from notifications. */ - Set posts = webInterface.getNewPosts(); - for (PostReply reply : webInterface.getNewReplies()) { - posts.add(reply.getPost()); + Set posts = new HashSet(webInterface.getNewPosts()); + for (PostReply reply : Collections2.filter(webInterface.getNewReplies(), PostReply.HAS_POST_FILTER)) { + posts.add(reply.getPost().get()); } /* filter and sort them. */ diff --git a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java index c770da3..094a9cf 100644 --- a/src/main/java/net/pterodactylus/sone/web/OptionsPage.java +++ b/src/main/java/net/pterodactylus/sone/web/OptionsPage.java @@ -1,5 +1,5 @@ /* - * Sone - OptionsPage.java - Copyright © 2010–2012 David Roden + * Sone - OptionsPage.java - Copyright © 2010–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 @@ -20,7 +20,7 @@ package net.pterodactylus.sone.web; import java.util.ArrayList; import java.util.List; -import net.pterodactylus.sone.core.Core.Preferences; +import net.pterodactylus.sone.core.Preferences; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; diff --git a/src/main/java/net/pterodactylus/sone/web/RescuePage.java b/src/main/java/net/pterodactylus/sone/web/RescuePage.java index 083c68b..6888277 100644 --- a/src/main/java/net/pterodactylus/sone/web/RescuePage.java +++ b/src/main/java/net/pterodactylus/sone/web/RescuePage.java @@ -1,5 +1,5 @@ /* - * Sone - RescuePage.java - Copyright © 2011–2012 David Roden + * Sone - RescuePage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/SearchPage.java b/src/main/java/net/pterodactylus/sone/web/SearchPage.java index 0f12abf..6c9e15c 100644 --- a/src/main/java/net/pterodactylus/sone/web/SearchPage.java +++ b/src/main/java/net/pterodactylus/sone/web/SearchPage.java @@ -1,5 +1,5 @@ /* - * Sone - SearchPage.java - Copyright © 2010–2012 David Roden + * Sone - SearchPage.java - Copyright © 2010–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 @@ -24,6 +24,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -34,18 +35,7 @@ import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; -import net.pterodactylus.util.cache.Cache; -import net.pterodactylus.util.cache.CacheException; -import net.pterodactylus.util.cache.CacheItem; -import net.pterodactylus.util.cache.DefaultCacheItem; -import net.pterodactylus.util.cache.MemoryCache; -import net.pterodactylus.util.cache.ValueRetriever; import net.pterodactylus.util.collection.Pagination; -import net.pterodactylus.util.collection.TimedMap; -import net.pterodactylus.util.collection.filter.Filter; -import net.pterodactylus.util.collection.filter.Filters; -import net.pterodactylus.util.collection.mapper.Mapper; -import net.pterodactylus.util.collection.mapper.Mappers; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.template.Template; @@ -53,6 +43,16 @@ import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.text.StringEscaper; import net.pterodactylus.util.text.TextException; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Collections2; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Ordering; + /** * This page lets the user search for posts and replies that contain certain * words. @@ -65,19 +65,18 @@ public class SearchPage extends SoneTemplatePage { private static final Logger logger = Logging.getLogger(SearchPage.class); /** Short-term cache. */ - private final Cache, Set>> hitCache = new MemoryCache, Set>>(new ValueRetriever, Set>>() { + private final LoadingCache, Set>> hitCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(new CacheLoader, Set>>() { @Override @SuppressWarnings("synthetic-access") - public CacheItem>> retrieve(List phrases) throws CacheException { + public Set> load(List phrases) { Set posts = new HashSet(); for (Sone sone : webInterface.getCore().getSones()) { posts.addAll(sone.getPosts()); } - return new DefaultCacheItem>>(getHits(Filters.filteredSet(posts, Post.FUTURE_POSTS_FILTER), phrases, new PostStringGenerator())); + return getHits(Collections2.filter(posts, Post.FUTURE_POSTS_FILTER), phrases, new PostStringGenerator()); } - - }, new TimedMap, CacheItem>>>(300000)); + }); /** * Creates a new search page. @@ -99,6 +98,7 @@ public class SearchPage extends SoneTemplatePage { * {@inheritDoc} */ @Override + @SuppressWarnings("synthetic-access") protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); String query = request.getHttpRequest().getParam("query").trim(); @@ -131,31 +131,22 @@ public class SearchPage extends SoneTemplatePage { redirectIfNotNull(getImageId(phrase), "imageBrowser.html?image="); } - Set sones = webInterface.getCore().getSones(); - Set> soneHits = getHits(sones, phrases, SoneStringGenerator.COMPLETE_GENERATOR); + Collection sones = webInterface.getCore().getSones(); + Collection> soneHits = getHits(sones, phrases, SoneStringGenerator.COMPLETE_GENERATOR); - Set> postHits; - try { - postHits = hitCache.get(phrases); - } catch (CacheException ce1) { - /* should never happen. */ - logger.log(Level.SEVERE, "Could not get search results from cache!", ce1); - postHits = Collections.emptySet(); - } + Collection> postHits = hitCache.getUnchecked(phrases); /* now filter. */ - soneHits = Filters.filteredSet(soneHits, Hit.POSITIVE_FILTER); - postHits = Filters.filteredSet(postHits, Hit.POSITIVE_FILTER); + soneHits = Collections2.filter(soneHits, Hit.POSITIVE_FILTER); + postHits = Collections2.filter(postHits, Hit.POSITIVE_FILTER); /* now sort. */ - List> sortedSoneHits = new ArrayList>(soneHits); - Collections.sort(sortedSoneHits, Hit.DESCENDING_COMPARATOR); - List> sortedPostHits = new ArrayList>(postHits); - Collections.sort(sortedPostHits, Hit.DESCENDING_COMPARATOR); + List> sortedSoneHits = Ordering.from(Hit.DESCENDING_COMPARATOR).sortedCopy(soneHits); + List> sortedPostHits = Ordering.from(Hit.DESCENDING_COMPARATOR).sortedCopy(postHits); /* extract Sones and posts. */ - List resultSones = Mappers.mappedList(sortedSoneHits, new HitMapper()); - List resultPosts = Mappers.mappedList(sortedPostHits, new HitMapper()); + List resultSones = FluentIterable.from(sortedSoneHits).transform(new HitMapper()).toList(); + List resultPosts = FluentIterable.from(sortedPostHits).transform(new HitMapper()).toList(); /* pagination. */ Pagination sonePagination = new Pagination(resultSones, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("sonePage"), 0)); @@ -319,7 +310,7 @@ public class SearchPage extends SoneTemplatePage { */ private String getSoneId(String phrase) { String soneId = phrase.startsWith("sone://") ? phrase.substring(7) : phrase; - return (webInterface.getCore().getSone(soneId, false) != null) ? soneId : null; + return (webInterface.getCore().getSone(soneId).isPresent()) ? soneId : null; } /** @@ -332,7 +323,7 @@ public class SearchPage extends SoneTemplatePage { */ private String getPostId(String phrase) { String postId = phrase.startsWith("post://") ? phrase.substring(7) : phrase; - return (webInterface.getCore().getPost(postId, false) != null) ? postId : null; + return (webInterface.getCore().getPost(postId).isPresent()) ? postId : null; } /** @@ -346,7 +337,11 @@ public class SearchPage extends SoneTemplatePage { */ private String getReplyPostId(String phrase) { String replyId = phrase.startsWith("reply://") ? phrase.substring(8) : phrase; - return (webInterface.getCore().getReply(replyId, false) != null) ? webInterface.getCore().getReply(replyId, false).getPost().getId() : null; + Optional postReply = webInterface.getCore().getPostReply(replyId); + if (!postReply.isPresent()) { + return null; + } + return postReply.get().getPostId(); } /** @@ -469,10 +464,10 @@ public class SearchPage extends SoneTemplatePage { public String generateString(Post post) { StringBuilder postString = new StringBuilder(); postString.append(post.getText()); - if (post.getRecipient() != null) { - postString.append(' ').append(SoneStringGenerator.NAME_GENERATOR.generateString(post.getRecipient())); + if (post.getRecipient().isPresent()) { + postString.append(' ').append(SoneStringGenerator.NAME_GENERATOR.generateString(post.getRecipient().get())); } - for (PostReply reply : Filters.filteredList(webInterface.getCore().getReplies(post), Reply.FUTURE_REPLY_FILTER)) { + for (PostReply reply : Collections2.filter(webInterface.getCore().getReplies(post.getId()), Reply.FUTURE_REPLY_FILTER)) { postString.append(' ').append(SoneStringGenerator.NAME_GENERATOR.generateString(reply.getSone())); postString.append(' ').append(reply.getText()); } @@ -582,10 +577,10 @@ public class SearchPage extends SoneTemplatePage { private static class Hit { /** Filter for {@link Hit}s with a score of more than 0. */ - public static final Filter> POSITIVE_FILTER = new Filter>() { + public static final Predicate> POSITIVE_FILTER = new Predicate>() { @Override - public boolean filterObject(Hit hit) { + public boolean apply(Hit hit) { return hit.getScore() > 0; } @@ -647,13 +642,13 @@ public class SearchPage extends SoneTemplatePage { * The type of the object to extract * @author David ‘Bombe’ Roden */ - public static class HitMapper implements Mapper, T> { + private static class HitMapper implements Function, T> { /** * {@inheritDoc} */ @Override - public T map(Hit input) { + public T apply(Hit input) { return input.getObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java b/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java index 4ef434f..7dfdfe7 100644 --- a/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java +++ b/src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java @@ -1,5 +1,5 @@ /* - * Sone - SoneTemplatePage.java - Copyright © 2010–2012 David Roden + * Sone - SoneTemplatePage.java - Copyright © 2010–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 @@ -30,12 +30,13 @@ import net.pterodactylus.sone.main.SonePlugin; import net.pterodactylus.sone.notify.ListNotificationFilters; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.sone.web.page.FreenetTemplatePage; -import net.pterodactylus.util.collection.ListBuilder; -import net.pterodactylus.util.collection.MapBuilder; import net.pterodactylus.util.notify.Notification; -import net.pterodactylus.util.object.HashCode; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + import freenet.clients.http.SessionManager.Session; import freenet.clients.http.ToadletContext; import freenet.support.api.HTTPRequest; @@ -217,7 +218,7 @@ public class SoneTemplatePage extends FreenetTemplatePage { */ @Override protected List> getAdditionalLinkNodes(FreenetRequest request) { - return new ListBuilder>().add(new MapBuilder().put("rel", "search").put("type", "application/opensearchdescription+xml").put("title", "Sone").put("href", "http://" + request.getHttpRequest().getHeader("host") + "/Sone/OpenSearch.xml").get()).get(); + return ImmutableList.> builder().add(ImmutableMap. builder().put("rel", "search").put("type", "application/opensearchdescription+xml").put("title", "Sone").put("href", "http://" + request.getHttpRequest().getHeader("host") + "/Sone/OpenSearch.xml").build()).build(); } /** @@ -265,7 +266,7 @@ public class SoneTemplatePage extends FreenetTemplatePage { List notifications = ListNotificationFilters.filterNotifications(webInterface.getNotifications().getNotifications(), currentSone); Collections.sort(notifications, Notification.CREATED_TIME_SORTER); templateContext.set("notifications", notifications); - templateContext.set("notificationHash", HashCode.hashCode(notifications)); + templateContext.set("notificationHash", notifications.hashCode()); } /** diff --git a/src/main/java/net/pterodactylus/sone/web/TrustPage.java b/src/main/java/net/pterodactylus/sone/web/TrustPage.java index 3723fac..39fd1de 100644 --- a/src/main/java/net/pterodactylus/sone/web/TrustPage.java +++ b/src/main/java/net/pterodactylus/sone/web/TrustPage.java @@ -1,5 +1,5 @@ /* - * Sone - TrustPage.java - Copyright © 2011–2012 David Roden + * Sone - TrustPage.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -59,9 +61,9 @@ public class TrustPage extends SoneTemplatePage { String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44); Sone currentSone = getCurrentSone(request.getToadletContext()); - Sone sone = webInterface.getCore().getSone(identity, false); - if (sone != null) { - webInterface.getCore().trustSone(currentSone, sone); + Optional sone = webInterface.getCore().getSone(identity); + if (sone.isPresent()) { + webInterface.getCore().trustSone(currentSone, sone.get()); } throw new RedirectException(returnPage); } diff --git a/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java b/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java index 5e8a2a8..b568be1 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java @@ -1,5 +1,5 @@ /* - * Sone - UnbookmarkPage.java - Copyright © 2011–2012 David Roden + * Sone - UnbookmarkPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java b/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java index 97ba4cd..4813239 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - UnfollowSonePage.java - Copyright © 2010–2012 David Roden + * Sone - UnfollowSonePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/UnlikePage.java b/src/main/java/net/pterodactylus/sone/web/UnlikePage.java index d7e9acb..9254a42 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnlikePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnlikePage.java @@ -1,5 +1,5 @@ /* - * Sone - UnlikePage.java - Copyright © 2010–2012 David Roden + * Sone - UnlikePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java b/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java index 3d2370c..ed92246 100644 --- a/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - UnlockSonePage.java - Copyright © 2010–2012 David Roden + * Sone - UnlockSonePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/UntrustPage.java b/src/main/java/net/pterodactylus/sone/web/UntrustPage.java index 29e17e7..d68d5ca 100644 --- a/src/main/java/net/pterodactylus/sone/web/UntrustPage.java +++ b/src/main/java/net/pterodactylus/sone/web/UntrustPage.java @@ -1,5 +1,5 @@ /* - * Sone - UntrustPage.java - Copyright © 2011–2012 David Roden + * Sone - UntrustPage.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -59,9 +61,9 @@ public class UntrustPage extends SoneTemplatePage { String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256); String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44); Sone currentSone = getCurrentSone(request.getToadletContext()); - Sone sone = webInterface.getCore().getSone(identity, false); - if (sone != null) { - webInterface.getCore().untrustSone(currentSone, sone); + Optional sone = webInterface.getCore().getSone(identity); + if (sone.isPresent()) { + webInterface.getCore().untrustSone(currentSone, sone.get()); } 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 559a8ff..2800f29 100644 --- a/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java +++ b/src/main/java/net/pterodactylus/sone/web/UploadImagePage.java @@ -1,5 +1,5 @@ /* - * Sone - UploadImagePage.java - Copyright © 2011–2012 David Roden + * Sone - UploadImagePage.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 @@ -36,11 +36,13 @@ import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.io.Closer; -import net.pterodactylus.util.io.StreamCopier; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.web.Method; + +import com.google.common.io.ByteStreams; + import freenet.support.api.Bucket; import freenet.support.api.HTTPUploadedFile; @@ -99,7 +101,7 @@ public class UploadImagePage extends SoneTemplatePage { imageInputStream = fileBucket.getInputStream(); /* TODO - check length */ imageDataOutputStream = new ByteArrayOutputStream((int) fileBucket.size()); - StreamCopier.copy(imageInputStream, imageDataOutputStream); + ByteStreams.copy(imageInputStream, imageDataOutputStream); } catch (IOException ioe1) { logger.log(Level.WARNING, "Could not read uploaded image!", ioe1); return; diff --git a/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java b/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java index 98b2603..8adf46d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ViewPostPage.java @@ -1,5 +1,5 @@ /* - * Sone - ViewPostPage.java - Copyright © 2010–2012 David Roden + * Sone - ViewPostPage.java - Copyright © 2010–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 @@ -19,6 +19,8 @@ package net.pterodactylus.sone.web; import java.net.URI; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.template.SoneAccessor; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -54,11 +56,11 @@ public class ViewPostPage extends SoneTemplatePage { @Override protected String getPageTitle(FreenetRequest request) { String postId = request.getHttpRequest().getParam("post"); - Post post = webInterface.getCore().getPost(postId, false); + Optional post = webInterface.getCore().getPost(postId); String title = ""; - if ((post != null) && (post.getSone() != null)) { - title = post.getText().substring(0, Math.min(20, post.getText().length())) + "…"; - title += " - " + SoneAccessor.getNiceName(post.getSone()) + " - "; + if (post.isPresent()) { + title = post.get().getText().substring(0, Math.min(20, post.get().getText().length())) + "…"; + title += " - " + SoneAccessor.getNiceName(post.get().getSone()) + " - "; } title += webInterface.getL10n().getString("Page.ViewPost.Title"); return title; @@ -72,8 +74,8 @@ public class ViewPostPage extends SoneTemplatePage { super.processTemplate(request, templateContext); String postId = request.getHttpRequest().getParam("post"); boolean raw = request.getHttpRequest().getParam("raw").equals("true"); - Post post = webInterface.getCore().getPost(postId); - templateContext.set("post", post); + Optional post = webInterface.getCore().getPost(postId); + templateContext.set("post", post.orNull()); templateContext.set("raw", raw); } diff --git a/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java b/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java index 2df911c..a94b4af 100644 --- a/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java +++ b/src/main/java/net/pterodactylus/sone/web/ViewSonePage.java @@ -1,5 +1,5 @@ /* - * Sone - ViewSonePage.java - Copyright © 2010–2012 David Roden + * Sone - ViewSonePage.java - Copyright © 2010–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 @@ -36,6 +36,8 @@ import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.base.Optional; + /** * Lets the user browser another Sone. * @@ -65,9 +67,9 @@ public class ViewSonePage extends SoneTemplatePage { @Override protected String getPageTitle(FreenetRequest request) { String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getSone(soneId, false); - if ((sone != null) && (sone.getTime() > 0)) { - String soneName = SoneAccessor.getNiceName(sone); + Optional sone = webInterface.getCore().getSone(soneId); + if (sone.isPresent()) { + String soneName = SoneAccessor.getNiceName(sone.get()); return soneName + " - " + webInterface.getL10n().getString("Page.ViewSone.Title"); } return webInterface.getL10n().getString("Page.ViewSone.Page.TitleWithoutSone"); @@ -80,26 +82,26 @@ public class ViewSonePage extends SoneTemplatePage { protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException { super.processTemplate(request, templateContext); String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getSone(soneId, false); - templateContext.set("sone", sone); + Optional sone = webInterface.getCore().getSone(soneId); + templateContext.set("sone", sone.orNull()); templateContext.set("soneId", soneId); - if (sone == null) { + if (!sone.isPresent()) { return; } - List sonePosts = sone.getPosts(); - sonePosts.addAll(webInterface.getCore().getDirectedPosts(sone)); + List sonePosts = sone.get().getPosts(); + sonePosts.addAll(webInterface.getCore().getDirectedPosts(sone.get().getId())); Collections.sort(sonePosts, Post.TIME_COMPARATOR); Pagination postPagination = new Pagination(sonePosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("postPage"), 0)); templateContext.set("postPagination", postPagination); templateContext.set("posts", postPagination.getItems()); - Set replies = sone.getReplies(); + Set replies = sone.get().getReplies(); final Map> repliedPosts = new HashMap>(); for (PostReply reply : replies) { - Post post = reply.getPost(); - if (repliedPosts.containsKey(post) || sone.equals(post.getSone()) || (sone.equals(post.getRecipient()))) { + Optional post = reply.getPost(); + if (!post.isPresent() || repliedPosts.containsKey(post.get()) || sone.get().equals(post.get().getSone()) || (sone.get().getId().equals(post.get().getRecipientId().orNull()))) { continue; } - repliedPosts.put(post, webInterface.getCore().getReplies(post)); + repliedPosts.put(post.get(), webInterface.getCore().getReplies(post.get().getId())); } List posts = new ArrayList(repliedPosts.keySet()); Collections.sort(posts, new Comparator() { diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 00f8cf9..593193c 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -1,5 +1,5 @@ /* - * Sone - WebInterface.java - Copyright © 2010–2012 David Roden + * Sone - WebInterface.java - Copyright © 2010–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 @@ -32,11 +32,33 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import net.pterodactylus.sone.core.Core; -import net.pterodactylus.sone.core.CoreListener; +import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent; +import net.pterodactylus.sone.core.event.ImageInsertFailedEvent; +import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; +import net.pterodactylus.sone.core.event.ImageInsertStartedEvent; +import net.pterodactylus.sone.core.event.MarkPostKnownEvent; +import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent; +import net.pterodactylus.sone.core.event.MarkSoneKnownEvent; +import net.pterodactylus.sone.core.event.NewPostFoundEvent; +import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent; +import net.pterodactylus.sone.core.event.NewSoneFoundEvent; +import net.pterodactylus.sone.core.event.PostRemovedEvent; +import net.pterodactylus.sone.core.event.PostReplyRemovedEvent; +import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent; +import net.pterodactylus.sone.core.event.SoneInsertedEvent; +import net.pterodactylus.sone.core.event.SoneInsertingEvent; +import net.pterodactylus.sone.core.event.SoneLockedEvent; +import net.pterodactylus.sone.core.event.SoneRemovedEvent; +import net.pterodactylus.sone.core.event.SoneUnlockedEvent; +import net.pterodactylus.sone.core.event.UpdateFoundEvent; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; @@ -103,8 +125,6 @@ import net.pterodactylus.sone.web.ajax.UntrustAjaxPage; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.sone.web.page.PageToadlet; import net.pterodactylus.sone.web.page.PageToadletFactory; -import net.pterodactylus.util.collection.SetBuilder; -import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.notify.Notification; import net.pterodactylus.util.notify.NotificationManager; @@ -126,11 +146,15 @@ import net.pterodactylus.util.template.TemplateContextFactory; import net.pterodactylus.util.template.TemplateParser; import net.pterodactylus.util.template.TemplateProvider; import net.pterodactylus.util.template.XmlFilter; -import net.pterodactylus.util.thread.Ticker; -import net.pterodactylus.util.version.Version; import net.pterodactylus.util.web.RedirectPage; import net.pterodactylus.util.web.StaticPage; import net.pterodactylus.util.web.TemplatePage; + +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; + import freenet.clients.http.SessionManager; import freenet.clients.http.SessionManager.Session; import freenet.clients.http.ToadletContainer; @@ -144,7 +168,7 @@ import freenet.support.api.HTTPRequest; * * @author David ‘Bombe’ Roden */ -public class WebInterface implements CoreListener { +public class WebInterface { /** The logger. */ private static final Logger logger = Logging.getLogger(WebInterface.class); @@ -192,7 +216,7 @@ public class WebInterface implements CoreListener { private final Map soneInsertNotifications = new HashMap(); /** Sone locked notification ticker objects. */ - private final Map lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap()); + private final Map> lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap>()); /** The “Sone locked” notification. */ private final ListNotification lockedSonesNotification; @@ -209,12 +233,16 @@ public class WebInterface implements CoreListener { /** The “image insert failed” notification. */ private final ListNotification imageInsertFailedNotification; + /** Scheduled executor for time-based notifications. */ + private final ScheduledExecutorService ticker = Executors.newScheduledThreadPool(1); + /** * Creates a new web interface. * * @param sonePlugin * The Sone plugin */ + @Inject public WebInterface(SonePlugin sonePlugin) { this.sonePlugin = sonePlugin; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); @@ -372,7 +400,7 @@ public class WebInterface implements CoreListener { * currently logged in */ public Sone getCurrentSone(ToadletContext toadletContext, boolean create) { - Set localSones = getCore().getLocalSones(); + Collection localSones = getCore().getLocalSones(); if (localSones.size() == 1) { return localSones.iterator().next(); } @@ -458,7 +486,7 @@ public class WebInterface implements CoreListener { * @return The new posts */ public Set getNewPosts() { - return new SetBuilder().addAll(newPostNotification.getElements()).addAll(localPostNotification.getElements()).get(); + return ImmutableSet. builder().addAll(newPostNotification.getElements()).addAll(localPostNotification.getElements()).build(); } /** @@ -468,7 +496,7 @@ public class WebInterface implements CoreListener { * @return The new replies */ public Set getNewReplies() { - return new SetBuilder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).get(); + return ImmutableSet. builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build(); } /** @@ -532,17 +560,17 @@ public class WebInterface implements CoreListener { final TemplateNotification startupNotification = new TemplateNotification("startup-notification", startupNotificationTemplate); notificationManager.addNotification(startupNotification); - Ticker.getInstance().registerEvent(System.currentTimeMillis() + (120 * 1000), new Runnable() { + ticker.schedule(new Runnable() { @Override public void run() { startupNotification.dismiss(); } - }, "Sone Startup Notification Remover"); + }, 2, TimeUnit.MINUTES); Template wotMissingNotificationTemplate = TemplateParser.parse(createReader("/templates/notify/wotMissingNotification.html")); final TemplateNotification wotMissingNotification = new TemplateNotification("wot-missing-notification", wotMissingNotificationTemplate); - Ticker.getInstance().registerEvent(System.currentTimeMillis() + (15 * 1000), new Runnable() { + ticker.scheduleAtFixedRate(new Runnable() { @Override @SuppressWarnings("synthetic-access") @@ -552,10 +580,9 @@ public class WebInterface implements CoreListener { } else { notificationManager.addNotification(wotMissingNotification); } - Ticker.getInstance().registerEvent(System.currentTimeMillis() + (15 * 1000), this, "Sone WoT Connector Checker"); } - }, "Sone WoT Connector Checker"); + }, 15, 15, TimeUnit.SECONDS); } /** @@ -563,7 +590,7 @@ public class WebInterface implements CoreListener { */ public void stop() { unregisterToadlets(); - Ticker.getInstance().stop(); + ticker.shutdownNow(); } // @@ -725,15 +752,15 @@ public class WebInterface implements CoreListener { } /** - * Returns all {@link Core#isLocalSone(Sone) local Sone}s that are - * referenced by {@link SonePart}s in the given text (after parsing it using + * Returns all {@link Sone#isLocal() local Sone}s that are referenced by + * {@link SonePart}s in the given text (after parsing it using * {@link SoneTextParser}). * * @param text * The text to parse * @return All mentioned local Sones */ - private Set getMentionedSones(String text) { + private Collection getMentionedSones(String text) { /* we need no context to find mentioned Sones. */ Set mentionedSones = new HashSet(); try { @@ -745,7 +772,7 @@ public class WebInterface implements CoreListener { } catch (IOException ioe1) { logger.log(Level.WARNING, String.format("Could not parse post text: %s", text), ioe1); } - return Filters.filteredSet(mentionedSones, Sone.LOCAL_SONE_FILTER); + return Collections2.filter(mentionedSones, Sone.LOCAL_SONE_FILTER); } /** @@ -770,26 +797,33 @@ public class WebInterface implements CoreListener { } // - // CORELISTENER METHODS + // EVENT HANDLERS // /** - * {@inheritDoc} + * Notifies the web interface that a new {@link Sone} was found. + * + * @param newSoneFoundEvent + * The event */ - @Override - public void newSoneFound(Sone sone) { - newSoneNotification.add(sone); + @Subscribe + public void newSoneFound(NewSoneFoundEvent newSoneFoundEvent) { + newSoneNotification.add(newSoneFoundEvent.sone()); if (!hasFirstStartNotification()) { notificationManager.addNotification(newSoneNotification); } } /** - * {@inheritDoc} + * Notifies the web interface that a new {@link Post} was found. + * + * @param newPostFoundEvent + * The event */ - @Override - public void newPostFound(Post post) { - boolean isLocal = getCore().isLocalSone(post.getSone()); + @Subscribe + public void newPostFound(NewPostFoundEvent newPostFoundEvent) { + Post post = newPostFoundEvent.post(); + boolean isLocal = post.getSone().isLocal(); if (isLocal) { localPostNotification.add(post); } else { @@ -807,11 +841,15 @@ public class WebInterface implements CoreListener { } /** - * {@inheritDoc} + * Notifies the web interface that a new {@link PostReply} was found. + * + * @param newPostReplyFoundEvent + * The event */ - @Override - public void newReplyFound(PostReply reply) { - boolean isLocal = getCore().isLocalSone(reply.getSone()); + @Subscribe + public void newReplyFound(NewPostReplyFoundEvent newPostReplyFoundEvent) { + PostReply reply = newPostReplyFoundEvent.postReply(); + boolean isLocal = reply.getSone().isLocal(); if (isLocal) { localReplyNotification.add(reply); } else { @@ -819,8 +857,8 @@ public class WebInterface implements CoreListener { } if (!hasFirstStartNotification()) { notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification); - if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal && (reply.getPost().getSone() != null) && (reply.getTime() <= System.currentTimeMillis())) { - mentionNotification.add(reply.getPost()); + if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal && reply.getPost().isPresent() && (reply.getTime() <= System.currentTimeMillis())) { + mentionNotification.add(reply.getPost().get()); notificationManager.addNotification(mentionNotification); } } else { @@ -829,179 +867,228 @@ public class WebInterface implements CoreListener { } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Sone} was marked as known. + * + * @param markSoneKnownEvent + * The event */ - @Override - public void markSoneKnown(Sone sone) { - newSoneNotification.remove(sone); + @Subscribe + public void markSoneKnown(MarkSoneKnownEvent markSoneKnownEvent) { + newSoneNotification.remove(markSoneKnownEvent.sone()); } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Post} was marked as known. + * + * @param markPostKnownEvent + * The event */ - @Override - public void markPostKnown(Post post) { - newPostNotification.remove(post); - localPostNotification.remove(post); - mentionNotification.remove(post); + @Subscribe + public void markPostKnown(MarkPostKnownEvent markPostKnownEvent) { + newPostNotification.remove(markPostKnownEvent.post()); + localPostNotification.remove(markPostKnownEvent.post()); + mentionNotification.remove(markPostKnownEvent.post()); } /** - * {@inheritDoc} + * Notifies the web interface that a {@link PostReply} was marked as known. + * + * @param markPostReplyKnownEvent + * The event */ - @Override - public void markReplyKnown(PostReply reply) { - newReplyNotification.remove(reply); - localReplyNotification.remove(reply); - mentionNotification.remove(reply.getPost()); + @Subscribe + public void markReplyKnown(MarkPostReplyKnownEvent markPostReplyKnownEvent) { + newReplyNotification.remove(markPostReplyKnownEvent.postReply()); + localReplyNotification.remove(markPostReplyKnownEvent.postReply()); + mentionNotification.remove(markPostReplyKnownEvent.postReply().getPost().get()); } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Sone} was removed. + * + * @param soneRemovedEvent + * The event */ - @Override - public void soneRemoved(Sone sone) { - newSoneNotification.remove(sone); + @Subscribe + public void soneRemoved(SoneRemovedEvent soneRemovedEvent) { + newSoneNotification.remove(soneRemovedEvent.sone()); } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Post} was removed. + * + * @param postRemovedEvent + * The event */ - @Override - public void postRemoved(Post post) { - newPostNotification.remove(post); - localPostNotification.remove(post); - mentionNotification.remove(post); + @Subscribe + public void postRemoved(PostRemovedEvent postRemovedEvent) { + newPostNotification.remove(postRemovedEvent.post()); + localPostNotification.remove(postRemovedEvent.post()); + mentionNotification.remove(postRemovedEvent.post()); } /** - * {@inheritDoc} + * Notifies the web interface that a {@link PostReply} was removed. + * + * @param postReplyRemovedEvent + * The event */ - @Override - public void replyRemoved(PostReply reply) { + @Subscribe + public void replyRemoved(PostReplyRemovedEvent postReplyRemovedEvent) { + PostReply reply = postReplyRemovedEvent.postReply(); newReplyNotification.remove(reply); localReplyNotification.remove(reply); - if (!getMentionedSones(reply.getText()).isEmpty()) { + if (!getMentionedSones(reply.getText()).isEmpty() && reply.getPost().isPresent()) { boolean isMentioned = false; - for (PostReply existingReply : getCore().getReplies(reply.getPost())) { + for (PostReply existingReply : getCore().getReplies(reply.getPostId())) { isMentioned |= !reply.isKnown() && !getMentionedSones(existingReply.getText()).isEmpty(); } if (!isMentioned) { - mentionNotification.remove(reply.getPost()); + mentionNotification.remove(reply.getPost().get()); } } } /** - * {@inheritDoc} + * Notifies the web interface that a Sone was locked. + * + * @param soneLockedEvent + * The event */ - @Override - public void soneLocked(final Sone sone) { - Object tickerObject = Ticker.getInstance().registerEvent(System.currentTimeMillis() + (5 * 60) * 1000, new Runnable() { + @Subscribe + public void soneLocked(SoneLockedEvent soneLockedEvent) { + final Sone sone = soneLockedEvent.sone(); + ScheduledFuture tickerObject = ticker.schedule(new Runnable() { @Override @SuppressWarnings("synthetic-access") public void run() { lockedSonesNotification.add(sone); - lockedSonesTickerObjects.remove(sone); notificationManager.addNotification(lockedSonesNotification); } - }, "Sone Locked Notification"); + }, 5, TimeUnit.MINUTES); lockedSonesTickerObjects.put(sone, tickerObject); } /** - * {@inheritDoc} + * Notifies the web interface that a Sone was unlocked. + * + * @param soneUnlockedEvent + * The event */ - @Override - public void soneUnlocked(Sone sone) { - lockedSonesNotification.remove(sone); - Ticker.getInstance().deregisterEvent(lockedSonesTickerObjects.remove(sone)); + @Subscribe + public void soneUnlocked(SoneUnlockedEvent soneUnlockedEvent) { + lockedSonesNotification.remove(soneUnlockedEvent.sone()); + lockedSonesTickerObjects.remove(soneUnlockedEvent.sone()).cancel(false); } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Sone} is being inserted. + * + * @param soneInsertingEvent + * The event */ - @Override - public void soneInserting(Sone sone) { - TemplateNotification soneInsertNotification = getSoneInsertNotification(sone); + @Subscribe + public void soneInserting(SoneInsertingEvent soneInsertingEvent) { + TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertingEvent.sone()); soneInsertNotification.set("soneStatus", "inserting"); - if (sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) { + if (soneInsertingEvent.sone().getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) { notificationManager.addNotification(soneInsertNotification); } } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Sone} was inserted. + * + * @param soneInsertedEvent + * The event */ - @Override - public void soneInserted(Sone sone, long insertDuration) { - TemplateNotification soneInsertNotification = getSoneInsertNotification(sone); + @Subscribe + public void soneInserted(SoneInsertedEvent soneInsertedEvent) { + TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertedEvent.sone()); soneInsertNotification.set("soneStatus", "inserted"); - soneInsertNotification.set("insertDuration", insertDuration / 1000); - if (sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) { + soneInsertNotification.set("insertDuration", soneInsertedEvent.insertDuration() / 1000); + if (soneInsertedEvent.sone().getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) { notificationManager.addNotification(soneInsertNotification); } } /** - * {@inheritDoc} + * Notifies the web interface that a {@link Sone} insert was aborted. + * + * @param soneInsertAbortedEvent + * The event */ - @Override - public void soneInsertAborted(Sone sone, Throwable cause) { - TemplateNotification soneInsertNotification = getSoneInsertNotification(sone); + @Subscribe + public void soneInsertAborted(SoneInsertAbortedEvent soneInsertAbortedEvent) { + TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertAbortedEvent.sone()); soneInsertNotification.set("soneStatus", "insert-aborted"); - soneInsertNotification.set("insert-error", cause); - if (sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) { + soneInsertNotification.set("insert-error", soneInsertAbortedEvent.cause()); + if (soneInsertAbortedEvent.sone().getOptions().getBooleanOption("EnableSoneInsertNotifications").get()) { notificationManager.addNotification(soneInsertNotification); } } /** - * {@inheritDoc} + * Notifies the web interface that a new Sone version was found. + * + * @param updateFoundEvent + * The event */ - @Override - public void updateFound(Version version, long releaseTime, long latestEdition) { - newVersionNotification.getTemplateContext().set("latestVersion", version); - newVersionNotification.getTemplateContext().set("latestEdition", latestEdition); - newVersionNotification.getTemplateContext().set("releaseTime", releaseTime); + @Subscribe + public void updateFound(UpdateFoundEvent updateFoundEvent) { + newVersionNotification.getTemplateContext().set("latestVersion", updateFoundEvent.version()); + newVersionNotification.getTemplateContext().set("latestEdition", updateFoundEvent.latestEdition()); + newVersionNotification.getTemplateContext().set("releaseTime", updateFoundEvent.releaseTime()); notificationManager.addNotification(newVersionNotification); } /** - * {@inheritDoc} + * Notifies the web interface that an image insert was started + * + * @param imageInsertStartedEvent + * The event */ - @Override - public void imageInsertStarted(Image image) { - insertingImagesNotification.add(image); + @Subscribe + public void imageInsertStarted(ImageInsertStartedEvent imageInsertStartedEvent) { + insertingImagesNotification.add(imageInsertStartedEvent.image()); notificationManager.addNotification(insertingImagesNotification); } /** - * {@inheritDoc} + * Notifies the web interface that an {@link Image} insert was aborted. + * + * @param imageInsertAbortedEvent + * The event */ - @Override - public void imageInsertAborted(Image image) { - insertingImagesNotification.remove(image); + @Subscribe + public void imageInsertAborted(ImageInsertAbortedEvent imageInsertAbortedEvent) { + insertingImagesNotification.remove(imageInsertAbortedEvent.image()); } /** - * {@inheritDoc} + * Notifies the web interface that an {@link Image} insert is finished. + * + * @param imageInsertFinishedEvent + * The event */ - @Override - public void imageInsertFinished(Image image) { - insertingImagesNotification.remove(image); - insertedImagesNotification.add(image); + @Subscribe + public void imageInsertFinished(ImageInsertFinishedEvent imageInsertFinishedEvent) { + insertingImagesNotification.remove(imageInsertFinishedEvent.image()); + insertedImagesNotification.add(imageInsertFinishedEvent.image()); notificationManager.addNotification(insertedImagesNotification); } /** - * {@inheritDoc} + * Notifies the web interface that an {@link Image} insert has failed. + * + * @param imageInsertFailedEvent + * The event */ - @Override - public void imageInsertFailed(Image image, Throwable cause) { - insertingImagesNotification.remove(image); - imageInsertFailedNotification.add(image); + @Subscribe + public void imageInsertFailed(ImageInsertFailedEvent imageInsertFailedEvent) { + insertingImagesNotification.remove(imageInsertFailedEvent.image()); + imageInsertFailedNotification.add(imageInsertFailedEvent.image()); notificationManager.addNotification(imageInsertFailedNotification); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java index a684b0f..1005381 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - BookmarkAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - BookmarkAjaxPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java index 53a5a51..4c278c6 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - CreatePostAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - CreatePostAjaxPage.java - Copyright © 2010–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 @@ -24,6 +24,8 @@ import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; +import com.google.common.base.Optional; + /** * AJAX handler that creates a new post. * @@ -51,7 +53,7 @@ public class CreatePostAjaxPage extends JsonPage { return createErrorJsonObject("auth-required"); } String recipientId = request.getHttpRequest().getParam("recipient"); - Sone recipient = webInterface.getCore().getSone(recipientId, false); + Optional recipient = webInterface.getCore().getSone(recipientId); String senderId = request.getHttpRequest().getParam("sender"); Sone sender = webInterface.getCore().getLocalSone(senderId, false); if (sender == null) { @@ -63,7 +65,7 @@ public class CreatePostAjaxPage extends JsonPage { } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); Post newPost = webInterface.getCore().createPost(sender, recipient, text); - return createSuccessJsonObject().put("postId", newPost.getId()).put("sone", sender.getId()).put("recipient", (newPost.getRecipient() != null) ? newPost.getRecipient().getId() : null); + return createSuccessJsonObject().put("postId", newPost.getId()).put("sone", sender.getId()).put("recipient", newPost.getRecipientId().orNull()); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java index 20857f3..8a35185 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/CreateReplyAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - CreateReplyAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - CreateReplyAjaxPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Sone; @@ -58,12 +60,12 @@ public class CreateReplyAjaxPage extends JsonPage { if (sender == null) { sender = getCurrentSone(request.getToadletContext()); } - Post post = webInterface.getCore().getPost(postId); - if ((post == null) || (post.getSone() == null)) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { return createErrorJsonObject("invalid-post-id"); } text = TextFilter.filter(request.getHttpRequest().getHeader("host"), text); - PostReply reply = webInterface.getCore().createReply(sender, post, text); + PostReply reply = webInterface.getCore().createReply(sender, post.get(), text); return createSuccessJsonObject().put("reply", reply.getId()).put("sone", sender.getId()); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java index ea2310c..71b0016 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeletePostAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeletePostAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - DeletePostAjaxPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -49,14 +51,14 @@ public class DeletePostAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String postId = request.getHttpRequest().getParam("post"); - Post post = webInterface.getCore().getPost(postId, false); - if ((post == null) || (post.getSone() == null)) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { return createErrorJsonObject("invalid-post-id"); } - if (!webInterface.getCore().isLocalSone(post.getSone())) { + if (!post.get().getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } - webInterface.getCore().deletePost(post); + webInterface.getCore().deletePost(post.get()); return createSuccessJsonObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java index 20fe841..7874f83 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteProfileFieldAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteProfileFieldAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - DeleteProfileFieldAjaxPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java index 0442117..2aa217d 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DeleteReplyAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteReplyAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - DeleteReplyAjaxPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -49,14 +51,14 @@ public class DeleteReplyAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String replyId = request.getHttpRequest().getParam("reply"); - PostReply reply = webInterface.getCore().getReply(replyId); - if (reply == null) { + Optional reply = webInterface.getCore().getPostReply(replyId); + if (!reply.isPresent()) { return createErrorJsonObject("invalid-reply-id"); } - if (!webInterface.getCore().isLocalSone(reply.getSone())) { + if (!reply.get().getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } - webInterface.getCore().deleteReply(reply); + webInterface.getCore().deleteReply(reply.get()); return createSuccessJsonObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java index bd5d83c..595696f 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DismissNotificationAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - DismissNotificationAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - DismissNotificationAjaxPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java index 6e72a5a..843b088 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - DistrustAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - DistrustAjaxPage.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -51,11 +53,11 @@ public class DistrustAjaxPage extends JsonPage { return createErrorJsonObject("auth-required"); } String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getSone(soneId, false); - if (sone == null) { + Optional sone = webInterface.getCore().getSone(soneId); + if (!sone.isPresent()) { return createErrorJsonObject("invalid-sone-id"); } - webInterface.getCore().distrustSone(currentSone, sone); + webInterface.getCore().distrustSone(currentSone, sone.get()); return createSuccessJsonObject().put("trustValue", webInterface.getCore().getPreferences().getNegativeTrust()); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java index 92e1e48..8d436ea 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - EditAlbumAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - EditAlbumAjaxPage.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 @@ -54,7 +54,7 @@ public class EditAlbumAjaxPage extends JsonPage { if (album == null) { return createErrorJsonObject("invalid-album-id"); } - if (!webInterface.getCore().isLocalSone(album.getSone())) { + if (!album.getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) { 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 42f4285..378b03b 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - EditImageAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - EditImageAjaxPage.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 @@ -22,10 +22,11 @@ import net.pterodactylus.sone.template.ParserFilter; import net.pterodactylus.sone.text.TextFilter; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; -import net.pterodactylus.util.collection.MapBuilder; import net.pterodactylus.util.json.JsonObject; import net.pterodactylus.util.template.TemplateContext; +import com.google.common.collect.ImmutableMap; + /** * Page that stores a user’s image modifications. * @@ -63,7 +64,7 @@ public class EditImageAjaxPage extends JsonPage { if (image == null) { return createErrorJsonObject("invalid-image-id"); } - if (!webInterface.getCore().isLocalSone(image.getSone())) { + if (!image.getSone().isLocal()) { return createErrorJsonObject("not-authorized"); } if ("true".equals(request.getHttpRequest().getParam("moveLeft"))) { @@ -80,7 +81,7 @@ public class EditImageAjaxPage extends JsonPage { String description = request.getHttpRequest().getParam("description").trim(); image.setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)); 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(), new MapBuilder().put("sone", image.getSone()).get())); + 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())); } } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java index 16572cf..8a47ffe 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/EditProfileFieldAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - EditProfileFieldAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - EditProfileFieldAjaxPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java index ce0fd06..071f8e9 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/FollowSoneAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - FollowSoneAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - FollowSoneAjaxPage.java - Copyright © 2010–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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; @@ -45,7 +47,8 @@ public class FollowSoneAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String soneId = request.getHttpRequest().getParam("sone"); - if (!webInterface.getCore().hasSone(soneId)) { + Optional sone = webInterface.getCore().getSone(soneId); + if (!sone.isPresent()) { return createErrorJsonObject("invalid-sone-id"); } Sone currentSone = getCurrentSone(request.getToadletContext()); @@ -53,7 +56,7 @@ public class FollowSoneAjaxPage extends JsonPage { return createErrorJsonObject("auth-required"); } webInterface.getCore().followSone(currentSone, soneId); - webInterface.getCore().markSoneKnown(webInterface.getCore().getSone(soneId)); + webInterface.getCore().markSoneKnown(sone.get()); return createSuccessJsonObject(); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java index 293b4b0..1a5286f 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetLikesAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetLikesAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - GetLikesAjaxPage.java - Copyright © 2010–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 @@ -31,6 +31,8 @@ import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonArray; import net.pterodactylus.util.json.JsonObject; +import com.google.common.base.Optional; + /** * AJAX page that retrieves the number of “likes” a {@link Post} has. * @@ -63,12 +65,18 @@ public class GetLikesAjaxPage extends JsonPage { return createErrorJsonObject("invalid-" + type + "-id"); } if ("post".equals(type)) { - Post post = webInterface.getCore().getPost(id); - Set sones = webInterface.getCore().getLikes(post); + Optional post = webInterface.getCore().getPost(id); + if (!post.isPresent()) { + return createErrorJsonObject("invalid-post-id"); + } + Set sones = webInterface.getCore().getLikes(post.get()); return createSuccessJsonObject().put("likes", sones.size()).put("sones", getSones(sones)); } else if ("reply".equals(type)) { - PostReply reply = webInterface.getCore().getReply(id); - Set sones = webInterface.getCore().getLikes(reply); + Optional reply = webInterface.getCore().getPostReply(id); + if (!reply.isPresent()) { + return createErrorJsonObject("invalid-reply-id"); + } + Set sones = webInterface.getCore().getLikes(reply.get()); return createSuccessJsonObject().put("likes", sones.size()).put("sones", getSones(sones)); } return createErrorJsonObject("invalid-type"); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java index 98a204b..6573dda 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetNotificationsAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - GetNotificationsAjaxPage.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 @@ -32,7 +32,6 @@ import net.pterodactylus.util.json.JsonArray; import net.pterodactylus.util.json.JsonObject; import net.pterodactylus.util.notify.Notification; import net.pterodactylus.util.notify.TemplateNotification; -import net.pterodactylus.util.object.HashCode; import net.pterodactylus.util.template.TemplateContext; /** @@ -81,12 +80,11 @@ public class GetNotificationsAjaxPage extends JsonPage { Collection notifications = webInterface.getNotifications().getNotifications(); List filteredNotifications = ListNotificationFilters.filterNotifications(notifications, currentSone); Collections.sort(filteredNotifications, Notification.CREATED_TIME_SORTER); - int notificationHash = HashCode.hashCode(filteredNotifications); JsonArray jsonNotifications = new JsonArray(); for (Notification notification : filteredNotifications) { jsonNotifications.add(createJsonNotification(request, notification)); } - return createSuccessJsonObject().put("notificationHash", notificationHash).put("notifications", jsonNotifications).put("options", createJsonOptions(currentSone)); + return createSuccessJsonObject().put("notificationHash", filteredNotifications.hashCode()).put("notifications", jsonNotifications).put("options", createJsonOptions(currentSone)); } // diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java index 3d4633c..8eeedc9 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - GetPostAjaxPage.java - Copyright © 2010–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 @@ -19,6 +19,8 @@ package net.pterodactylus.sone.web.ajax; import java.io.StringWriter; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -59,11 +61,11 @@ public class GetPostAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String postId = request.getHttpRequest().getParam("post"); - Post post = webInterface.getCore().getPost(postId, false); - if (post == null) { + Optional post = webInterface.getCore().getPost(postId); + if (!post.isPresent()) { return createErrorJsonObject("invalid-post-id"); } - return createSuccessJsonObject().put("post", createJsonPost(request, post, getCurrentSone(request.getToadletContext()))); + return createSuccessJsonObject().put("post", createJsonPost(request, post.get(), getCurrentSone(request.getToadletContext()))); } /** @@ -94,7 +96,7 @@ public class GetPostAjaxPage extends JsonPage { JsonObject jsonPost = new JsonObject(); jsonPost.put("id", post.getId()); jsonPost.put("sone", post.getSone().getId()); - jsonPost.put("recipient", (post.getRecipient() == null) ? null : post.getRecipient().getId()); + jsonPost.put("recipient", post.getRecipientId().orNull()); jsonPost.put("time", post.getTime()); StringWriter stringWriter = new StringWriter(); TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java index 38bf090..b617608 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetReplyAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - GetReplyAjaxPage.java - Copyright © 2010–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 @@ -19,6 +19,8 @@ package net.pterodactylus.sone.web.ajax; import java.io.StringWriter; +import com.google.common.base.Optional; + import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -62,11 +64,11 @@ public class GetReplyAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String replyId = request.getHttpRequest().getParam("reply"); - PostReply reply = webInterface.getCore().getReply(replyId); - if ((reply == null) || (reply.getSone() == null)) { + Optional reply = webInterface.getCore().getPostReply(replyId); + if (!reply.isPresent()) { return createErrorJsonObject("invalid-reply-id"); } - return createSuccessJsonObject().put("reply", createJsonReply(request, reply, getCurrentSone(request.getToadletContext()))); + return createSuccessJsonObject().put("reply", createJsonReply(request, reply.get(), getCurrentSone(request.getToadletContext()))); } /** @@ -95,7 +97,7 @@ public class GetReplyAjaxPage extends JsonPage { private JsonObject createJsonReply(FreenetRequest request, PostReply reply, Sone currentSone) { JsonObject jsonReply = new JsonObject(); jsonReply.put("id", reply.getId()); - jsonReply.put("postId", reply.getPost().getId()); + jsonReply.put("postId", reply.getPostId()); jsonReply.put("soneId", reply.getSone().getId()); jsonReply.put("time", reply.getTime()); StringWriter stringWriter = new StringWriter(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java index 4c61f89..48b25d6 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetStatusAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - GetStatusAjaxPage.java - Copyright © 2010–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 @@ -19,6 +19,7 @@ package net.pterodactylus.sone.web.ajax; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; @@ -32,12 +33,12 @@ import net.pterodactylus.sone.notify.ListNotificationFilters; import net.pterodactylus.sone.template.SoneAccessor; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; -import net.pterodactylus.util.collection.filter.Filter; -import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.json.JsonArray; import net.pterodactylus.util.json.JsonObject; import net.pterodactylus.util.notify.Notification; -import net.pterodactylus.util.object.HashCode; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; /** * The “get status” AJAX handler returns all information that is necessary to @@ -73,7 +74,7 @@ public class GetStatusAjaxPage extends JsonPage { String[] soneIds = loadSoneIds.split(","); for (String soneId : soneIds) { /* just add it, we skip null further down. */ - sones.add(webInterface.getCore().getSone(soneId, false)); + sones.add(webInterface.getCore().getSone(soneId).orNull()); } } JsonArray jsonSones = new JsonArray(); @@ -87,14 +88,13 @@ public class GetStatusAjaxPage extends JsonPage { /* load notifications. */ List notifications = ListNotificationFilters.filterNotifications(webInterface.getNotifications().getNotifications(), currentSone); Collections.sort(notifications, Notification.CREATED_TIME_SORTER); - int notificationHash = HashCode.hashCode(notifications); /* load new posts. */ - Set newPosts = webInterface.getNewPosts(); + Collection newPosts = webInterface.getNewPosts(); if (currentSone != null) { - newPosts = Filters.filteredSet(newPosts, new Filter() { + newPosts = Collections2.filter(newPosts, new Predicate() { @Override - public boolean filterObject(Post post) { + public boolean apply(Post post) { return ListNotificationFilters.isPostVisible(currentSone, post); } @@ -105,40 +105,34 @@ public class GetStatusAjaxPage extends JsonPage { JsonObject jsonPost = new JsonObject(); jsonPost.put("id", post.getId()); jsonPost.put("sone", post.getSone().getId()); - jsonPost.put("recipient", (post.getRecipient() != null) ? post.getRecipient().getId() : null); + jsonPost.put("recipient", post.getRecipientId().orNull()); jsonPost.put("time", post.getTime()); jsonPosts.add(jsonPost); } /* load new replies. */ - Set newReplies = webInterface.getNewReplies(); + Collection newReplies = webInterface.getNewReplies(); if (currentSone != null) { - newReplies = Filters.filteredSet(newReplies, new Filter() { + newReplies = Collections2.filter(newReplies, new Predicate() { @Override - public boolean filterObject(PostReply reply) { + public boolean apply(PostReply reply) { return ListNotificationFilters.isReplyVisible(currentSone, reply); } }); } /* remove replies to unknown posts. */ - newReplies = Filters.filteredSet(newReplies, new Filter() { - - @Override - public boolean filterObject(PostReply reply) { - return (reply.getPost() != null) && (reply.getPost().getSone() != null); - } - }); + newReplies = Collections2.filter(newReplies, PostReply.HAS_POST_FILTER); JsonArray jsonReplies = new JsonArray(); for (PostReply reply : newReplies) { JsonObject jsonReply = new JsonObject(); jsonReply.put("id", reply.getId()); jsonReply.put("sone", reply.getSone().getId()); - jsonReply.put("post", reply.getPost().getId()); - jsonReply.put("postSone", reply.getPost().getSone().getId()); + jsonReply.put("post", reply.getPostId()); + jsonReply.put("postSone", reply.getPost().get().getSone().getId()); jsonReplies.add(jsonReply); } - return createSuccessJsonObject().put("loggedIn", currentSone != null).put("options", createJsonOptions(currentSone)).put("sones", jsonSones).put("notificationHash", notificationHash).put("newPosts", jsonPosts).put("newReplies", jsonReplies); + return createSuccessJsonObject().put("loggedIn", currentSone != null).put("options", createJsonOptions(currentSone)).put("sones", jsonSones).put("notificationHash", notifications.hashCode()).put("newPosts", jsonPosts).put("newReplies", jsonReplies); } /** diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java index f6bc05d..b0d9dac 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetTimesAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetTimesAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - GetTimesAjaxPage.java - Copyright © 2010–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 @@ -20,13 +20,15 @@ package net.pterodactylus.sone.web.ajax; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.concurrent.TimeUnit; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; -import net.pterodactylus.util.number.Digits; + +import com.google.common.base.Optional; /** * Ajax page that returns a formatted, relative timestamp for replies or posts. @@ -58,16 +60,16 @@ public class GetTimesAjaxPage extends JsonPage { if (allIds.length() > 0) { String[] ids = allIds.split(","); for (String id : ids) { - Post post = webInterface.getCore().getPost(id, false); - if (post == null) { + Optional post = webInterface.getCore().getPost(id); + if (!post.isPresent()) { continue; } JsonObject postTime = new JsonObject(); - Time time = getTime(post.getTime()); + Time time = getTime(post.get().getTime()); postTime.put("timeText", time.getText()); - postTime.put("refreshTime", time.getRefresh() / Time.SECOND); + postTime.put("refreshTime", TimeUnit.MILLISECONDS.toSeconds(time.getRefresh())); synchronized (dateFormat) { - postTime.put("tooltip", dateFormat.format(new Date(post.getTime()))); + postTime.put("tooltip", dateFormat.format(new Date(post.get().getTime()))); } postTimes.put(id, postTime); } @@ -77,16 +79,16 @@ public class GetTimesAjaxPage extends JsonPage { if (allIds.length() > 0) { String[] ids = allIds.split(","); for (String id : ids) { - PostReply reply = webInterface.getCore().getReply(id, false); - if (reply == null) { + Optional reply = webInterface.getCore().getPostReply(id); + if (!reply.isPresent()) { continue; } JsonObject replyTime = new JsonObject(); - Time time = getTime(reply.getTime()); + Time time = getTime(reply.get().getTime()); replyTime.put("timeText", time.getText()); - replyTime.put("refreshTime", time.getRefresh() / Time.SECOND); + replyTime.put("refreshTime", TimeUnit.MILLISECONDS.toSeconds(time.getRefresh())); synchronized (dateFormat) { - replyTime.put("tooltip", dateFormat.format(new Date(reply.getTime()))); + replyTime.put("tooltip", dateFormat.format(new Date(reply.get().getTime()))); } replyTimes.put(id, replyTime); } @@ -140,59 +142,59 @@ public class GetTimesAjaxPage extends JsonPage { */ public static Time getTime(WebInterface webInterface, long time) { if (time == 0) { - return new Time(webInterface.getL10n().getString("View.Sone.Text.UnknownDate"), 12 * Time.HOUR); + return new Time(webInterface.getL10n().getString("View.Sone.Text.UnknownDate"), TimeUnit.HOURS.toMillis(12)); } long age = System.currentTimeMillis() - time; String text; long refresh; if (age < 0) { text = webInterface.getL10n().getDefaultString("View.Time.InTheFuture"); - refresh = 5 * Time.MINUTE; - } else if (age < 20 * Time.SECOND) { + refresh = TimeUnit.MINUTES.toMillis(5); + } else if (age < TimeUnit.SECONDS.toMillis(20)) { text = webInterface.getL10n().getDefaultString("View.Time.AFewSecondsAgo"); - refresh = 10 * Time.SECOND; - } else if (age < 45 * Time.SECOND) { + refresh = TimeUnit.SECONDS.toMillis(10); + } else if (age < TimeUnit.SECONDS.toMillis(45)) { text = webInterface.getL10n().getString("View.Time.HalfAMinuteAgo"); - refresh = 20 * Time.SECOND; - } else if (age < 90 * Time.SECOND) { + refresh = TimeUnit.SECONDS.toMillis(20); + } else if (age < TimeUnit.SECONDS.toMillis(90)) { text = webInterface.getL10n().getString("View.Time.AMinuteAgo"); - refresh = Time.MINUTE; - } else if (age < 30 * Time.MINUTE) { - text = webInterface.getL10n().getString("View.Time.XMinutesAgo", "min", String.valueOf((int) (Digits.round(age, Time.MINUTE) / Time.MINUTE))); - refresh = 1 * Time.MINUTE; - } else if (age < 45 * Time.MINUTE) { + refresh = TimeUnit.MINUTES.toMillis(1); + } else if (age < TimeUnit.MINUTES.toMillis(30)) { + text = webInterface.getL10n().getString("View.Time.XMinutesAgo", "min", String.valueOf(TimeUnit.MILLISECONDS.toMinutes(age + TimeUnit.SECONDS.toMillis(30)))); + refresh = TimeUnit.MINUTES.toMillis(1); + } else if (age < TimeUnit.MINUTES.toMillis(45)) { text = webInterface.getL10n().getString("View.Time.HalfAnHourAgo"); - refresh = 10 * Time.MINUTE; - } else if (age < 90 * Time.MINUTE) { + refresh = TimeUnit.MINUTES.toMillis(10); + } else if (age < TimeUnit.MINUTES.toMillis(90)) { text = webInterface.getL10n().getString("View.Time.AnHourAgo"); - refresh = Time.HOUR; - } else if (age < 21 * Time.HOUR) { - text = webInterface.getL10n().getString("View.Time.XHoursAgo", "hour", String.valueOf((int) (Digits.round(age, Time.HOUR) / Time.HOUR))); - refresh = Time.HOUR; - } else if (age < 42 * Time.HOUR) { + refresh = TimeUnit.HOURS.toMillis(1); + } else if (age < TimeUnit.HOURS.toMillis(21)) { + text = webInterface.getL10n().getString("View.Time.XHoursAgo", "hour", String.valueOf(TimeUnit.MILLISECONDS.toHours(age + TimeUnit.MINUTES.toMillis(30)))); + refresh = TimeUnit.HOURS.toMillis(1); + } else if (age < TimeUnit.HOURS.toMillis(42)) { text = webInterface.getL10n().getString("View.Time.ADayAgo"); - refresh = Time.DAY; - } else if (age < 6 * Time.DAY) { - text = webInterface.getL10n().getString("View.Time.XDaysAgo", "day", String.valueOf((int) (Digits.round(age, Time.DAY) / Time.DAY))); - refresh = Time.DAY; - } else if (age < 11 * Time.DAY) { + refresh = TimeUnit.DAYS.toMillis(1); + } else if (age < TimeUnit.DAYS.toMillis(6)) { + text = webInterface.getL10n().getString("View.Time.XDaysAgo", "day", String.valueOf(TimeUnit.MILLISECONDS.toDays(age + TimeUnit.HOURS.toMillis(12)))); + refresh = TimeUnit.DAYS.toMillis(1); + } else if (age < TimeUnit.DAYS.toMillis(11)) { text = webInterface.getL10n().getString("View.Time.AWeekAgo"); - refresh = Time.DAY; - } else if (age < 4 * Time.WEEK) { - text = webInterface.getL10n().getString("View.Time.XWeeksAgo", "week", String.valueOf((int) (Digits.round(age, Time.WEEK) / Time.WEEK))); - refresh = Time.DAY; - } else if (age < 6 * Time.WEEK) { + refresh = TimeUnit.DAYS.toMillis(1); + } else if (age < TimeUnit.DAYS.toMillis(28)) { + text = webInterface.getL10n().getString("View.Time.XWeeksAgo", "week", String.valueOf((TimeUnit.MILLISECONDS.toHours(age) + 84) / (7 * 24))); + refresh = TimeUnit.DAYS.toMillis(1); + } else if (age < TimeUnit.DAYS.toMillis(42)) { text = webInterface.getL10n().getString("View.Time.AMonthAgo"); - refresh = Time.DAY; - } else if (age < 11 * Time.MONTH) { - text = webInterface.getL10n().getString("View.Time.XMonthsAgo", "month", String.valueOf((int) (Digits.round(age, Time.MONTH) / Time.MONTH))); - refresh = Time.DAY; - } else if (age < 18 * Time.MONTH) { + refresh = TimeUnit.DAYS.toMillis(1); + } else if (age < TimeUnit.DAYS.toMillis(330)) { + text = webInterface.getL10n().getString("View.Time.XMonthsAgo", "month", String.valueOf((TimeUnit.MILLISECONDS.toDays(age) + 15) / 30)); + refresh = TimeUnit.DAYS.toMillis(1); + } else if (age < TimeUnit.DAYS.toMillis(540)) { text = webInterface.getL10n().getString("View.Time.AYearAgo"); - refresh = Time.WEEK; + refresh = TimeUnit.DAYS.toMillis(7); } else { - text = webInterface.getL10n().getString("View.Time.XYearsAgo", "year", String.valueOf((int) (Digits.round(age, Time.YEAR) / Time.YEAR))); - refresh = Time.WEEK; + text = webInterface.getL10n().getString("View.Time.XYearsAgo", "year", String.valueOf((long) ((TimeUnit.MILLISECONDS.toDays(age) + 182.64) / 365.28))); + refresh = TimeUnit.DAYS.toMillis(7); } return new Time(text, refresh); } @@ -204,27 +206,6 @@ public class GetTimesAjaxPage extends JsonPage { */ public static class Time { - /** Number of milliseconds in a second. */ - private static final long SECOND = 1000; - - /** Number of milliseconds in a minute. */ - private static final long MINUTE = 60 * SECOND; - - /** Number of milliseconds in an hour. */ - private static final long HOUR = 60 * MINUTE; - - /** Number of milliseconds in a day. */ - private static final long DAY = 24 * HOUR; - - /** Number of milliseconds in a week. */ - private static final long WEEK = 7 * DAY; - - /** Number of milliseconds in a 30-day month. */ - private static final long MONTH = 30 * DAY; - - /** Number of milliseconds in a year. */ - private static final long YEAR = 365 * DAY; - /** The formatted time. */ private final String text; diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java index 343359d..0ecfaea 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/GetTranslationPage.java @@ -1,5 +1,5 @@ /* - * Sone - GetTranslationPage.java - Copyright © 2010–2012 David Roden + * Sone - GetTranslationPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java index 22eeec8..994f90c 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java @@ -1,5 +1,5 @@ /* - * Sone - JsonPage.java - Copyright © 2010–2012 David Roden + * Sone - JsonPage.java - Copyright © 2010–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 @@ -17,15 +17,22 @@ package net.pterodactylus.sone.web.ajax; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.net.URI; +import java.util.logging.Level; +import java.util.logging.Logger; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetPage; import net.pterodactylus.sone.web.page.FreenetRequest; +import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.json.JsonObject; import net.pterodactylus.util.json.JsonUtils; +import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.web.Page; import net.pterodactylus.util.web.Response; import freenet.clients.http.SessionManager.Session; @@ -39,6 +46,9 @@ import freenet.clients.http.ToadletContext; */ public abstract class JsonPage implements FreenetPage { + /** The logger. */ + private static final Logger logger = Logging.getLogger(JsonPage.class); + /** The path of the page. */ private final String path; @@ -218,8 +228,13 @@ public abstract class JsonPage implements FreenetPage { return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required"))); } } - JsonObject jsonObject = createJsonObject(request); - return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject)); + try { + JsonObject jsonObject = createJsonObject(request); + return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject)); + } catch (Exception e1) { + logger.log(Level.WARNING, "Error executing JSON page!", e1); + return response.setStatusCode(500).setStatusText(e1.getMessage()).setContentType("text/plain").write(dumpStackTrace(e1)); + } } /** @@ -230,4 +245,36 @@ public abstract class JsonPage implements FreenetPage { return false; } + // + // PRIVATE METHODS + // + + /** + * Returns a byte array containing the stack trace of the given throwable. + * + * @param t + * The throwable whose stack trace to dump into an array + * @return The array with the stack trace, or an empty array if the stack + * trace could not be dumped + */ + private static byte[] dumpStackTrace(Throwable t) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + OutputStreamWriter writer = null; + PrintWriter printWriter = null; + try { + writer = new OutputStreamWriter(byteArrayOutputStream, "uTF-8"); + printWriter = new PrintWriter(writer); + t.printStackTrace(printWriter); + byteArrayOutputStream.flush(); + return byteArrayOutputStream.toByteArray(); + } catch (IOException ioe1) { + /* quite not possible. */ + return new byte[0]; + } finally { + Closer.close(printWriter); + Closer.close(writer); + Closer.close(byteArrayOutputStream); + } + } + } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java index 3f6f8db..6787711 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/LikeAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - LikeAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - LikeAjaxPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java index 131d3e1..af74426 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/LockSoneAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - LockSoneAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - LockSoneAjaxPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java index 370bacd..6610971 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/MarkAsKnownAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - MarkAsKnownAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - MarkAsKnownAjaxPage.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 @@ -26,6 +26,8 @@ import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.util.json.JsonObject; +import com.google.common.base.Optional; + /** * AJAX page that lets the user mark a number of {@link Sone}s, {@link Post}s, * or {@link Reply}s as known. @@ -57,23 +59,23 @@ public class MarkAsKnownAjaxPage extends JsonPage { Core core = webInterface.getCore(); for (String id : ids) { if (type.equals("post")) { - Post post = core.getPost(id, false); - if (post == null) { + Optional post = core.getPost(id); + if (!post.isPresent()) { continue; } - core.markPostKnown(post); + core.markPostKnown(post.get()); } else if (type.equals("reply")) { - PostReply reply = core.getReply(id, false); - if (reply == null) { + Optional reply = core.getPostReply(id); + if (!reply.isPresent()) { continue; } - core.markReplyKnown(reply); + core.markReplyKnown(reply.get()); } else if (type.equals("sone")) { - Sone sone = core.getSone(id, false); - if (sone == null) { + Optional sone = core.getSone(id); + if (!sone.isPresent()) { continue; } - core.markSoneKnown(sone); + core.markSoneKnown(sone.get()); } } return createSuccessJsonObject(); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java index 02029ef..7721aa3 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/MoveProfileFieldAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - MoveProfileFieldAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - MoveProfileFieldAjaxPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java index 5b27d1b..d03e030 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/TrustAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - TrustAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - TrustAjaxPage.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -51,11 +53,11 @@ public class TrustAjaxPage extends JsonPage { return createErrorJsonObject("auth-required"); } String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getSone(soneId, false); - if (sone == null) { + Optional sone = webInterface.getCore().getSone(soneId); + if (!sone.isPresent()) { return createErrorJsonObject("invalid-sone-id"); } - webInterface.getCore().trustSone(currentSone, sone); + webInterface.getCore().trustSone(currentSone, sone.get()); return createSuccessJsonObject().put("trustValue", webInterface.getCore().getPreferences().getPositiveTrust()); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java index 6a1154a..8d209af 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnbookmarkAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - UnbookmarkAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - UnbookmarkAjaxPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java index 6e29cbc..3d984b9 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnfollowSoneAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - UnfollowSoneAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - UnfollowSoneAjaxPage.java - Copyright © 2010–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 @@ -45,7 +45,7 @@ public class UnfollowSoneAjaxPage extends JsonPage { @Override protected JsonObject createJsonObject(FreenetRequest request) { String soneId = request.getHttpRequest().getParam("sone"); - if (!webInterface.getCore().hasSone(soneId)) { + if (!webInterface.getCore().getSone(soneId).isPresent()) { return createErrorJsonObject("invalid-sone-id"); } Sone currentSone = getCurrentSone(request.getToadletContext()); diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java index f961bd5..0633fda 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnlikeAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - UnlikeAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - UnlikeAjaxPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java index 14cb10d..32bb93c 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UnlockSoneAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - UnlockSoneAjaxPage.java - Copyright © 2010–2012 David Roden + * Sone - UnlockSoneAjaxPage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java index 916b6e9..65d45c9 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/UntrustAjaxPage.java @@ -1,5 +1,5 @@ /* - * Sone - UntrustAjaxPage.java - Copyright © 2011–2012 David Roden + * Sone - UntrustAjaxPage.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 @@ -17,6 +17,8 @@ package net.pterodactylus.sone.web.ajax; +import com.google.common.base.Optional; + import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; @@ -51,11 +53,11 @@ public class UntrustAjaxPage extends JsonPage { return createErrorJsonObject("auth-required"); } String soneId = request.getHttpRequest().getParam("sone"); - Sone sone = webInterface.getCore().getSone(soneId, false); - if (sone == null) { + Optional sone = webInterface.getCore().getSone(soneId); + if (!sone.isPresent()) { return createErrorJsonObject("invalid-sone-id"); } - webInterface.getCore().untrustSone(currentSone, sone); + webInterface.getCore().untrustSone(currentSone, sone.get()); return createSuccessJsonObject().put("trustValue", (String) null); } diff --git a/src/main/java/net/pterodactylus/sone/web/page/FreenetPage.java b/src/main/java/net/pterodactylus/sone/web/page/FreenetPage.java index e68444c..a67fd04 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/FreenetPage.java +++ b/src/main/java/net/pterodactylus/sone/web/page/FreenetPage.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetPage.java - Copyright © 2011–2012 David Roden + * Sone - FreenetPage.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 diff --git a/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java b/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java index 783c8eb..858a219 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java +++ b/src/main/java/net/pterodactylus/sone/web/page/FreenetRequest.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetRequest.java - Copyright © 2011–2012 David Roden + * Sone - FreenetRequest.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 diff --git a/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java b/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java index 67884bd..625978b 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java +++ b/src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetTemplatePage.java - Copyright © 2010–2012 David Roden + * Sone - FreenetTemplatePage.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java index 1d2a2db..2c6706f 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java +++ b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java @@ -1,5 +1,5 @@ /* - * Sone - PageToadlet.java - Copyright © 2010–2012 David Roden + * Sone - PageToadlet.java - Copyright © 2010–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 diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java b/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java index ef5de69..a517f0a 100644 --- a/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java +++ b/src/main/java/net/pterodactylus/sone/web/page/PageToadletFactory.java @@ -1,5 +1,5 @@ /* - * Sone - PageToadletFactory.java - Copyright © 2010–2012 David Roden + * Sone - PageToadletFactory.java - Copyright © 2010–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 diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html new file mode 100644 index 0000000..5c6fb9f --- /dev/null +++ b/src/main/javadoc/overview.html @@ -0,0 +1,8 @@ + +

Sone API Documentation

+

This is the API documentation of Sone.

+

I recognize that this has probably little value for the average + user; however, should you ever plan to fiddle around with Sone, extend + it, change some functionality, fix bugs, or do whatever else you have + in mind, this documentation might come in handy.

+ \ No newline at end of file diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index e253ad5..d0ec575 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -117,6 +117,8 @@ Page.KnownSones.Filter.Followed=Nur gefolgte Sones anzeigen Page.KnownSones.Filter.NotFollowed=Gefolgte Sones nicht anzeigen Page.KnownSones.Filter.New=Nur neue Sones anzeigen Page.KnownSones.Filter.NotNew=Neue Sones nicht anzeigen +Page.KnownSones.Filter.Own=Nur eigene Sones anzeigen +Page.KnownSones.Filter.NotOwn=Nur fremde Sones anzeigen Page.KnownSones.Button.Apply=Anwenden Page.KnownSones.Button.FollowAllSones=Allen Sones auf dieser Seite folgen Page.KnownSones.Button.UnfollowAllSones=Alle Sones auf dieser Seite entfolgen diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index 5c4367a..6bab23e 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -117,6 +117,8 @@ Page.KnownSones.Filter.Followed=Show only followed Sones Page.KnownSones.Filter.NotFollowed=Hide followed Sones Page.KnownSones.Filter.New=Show only new Sones Page.KnownSones.Filter.NotNew=Hide new Sones +Page.KnownSones.Filter.Own=Show only local Sones +Page.KnownSones.Filter.NotOwn=Show only remote Sones Page.KnownSones.Button.Apply=Apply Page.KnownSones.Button.FollowAllSones=Follow all Sones on this page Page.KnownSones.Button.UnfollowAllSones=Unfollow all Sones on this page diff --git a/src/main/resources/i18n/sone.fr.properties b/src/main/resources/i18n/sone.fr.properties index ac36e74..397032f 100644 --- a/src/main/resources/i18n/sone.fr.properties +++ b/src/main/resources/i18n/sone.fr.properties @@ -4,9 +4,9 @@ Navigation.Menu.Sone.Item.Login.Name=Connexion Navigation.Menu.Sone.Item.Login.Tooltip=Se connecter à son Sone Navigation.Menu.Sone.Item.Index.Name=Votre Sone Navigation.Menu.Sone.Item.Index.Tooltip=Afficher votre Sone -Navigation.Menu.Sone.Item.New.Name=Nouveaux messages ety réponses +Navigation.Menu.Sone.Item.New.Name=Nouveaux messages et réponses Navigation.Menu.Sone.Item.New.Tooltip=Voir les nouveaux messages et réponses -Navigation.Menu.Sone.Item.CreateSone.Name=Créer Sone +Navigation.Menu.Sone.Item.CreateSone.Name=Créer un Sone Navigation.Menu.Sone.Item.CreateSone.Tooltip=Créer un nouveau Sone Navigation.Menu.Sone.Item.KnownSones.Name=Sones connus Navigation.Menu.Sone.Item.KnownSones.Tooltip=Montre tous les Sones connus @@ -117,6 +117,8 @@ Page.KnownSones.Filter.Followed=Montrer seulement les Sones suivis Page.KnownSones.Filter.NotFollowed=Cacher les Sones suivis Page.KnownSones.Filter.New=Montrer seulement les nouveaux Sones Page.KnownSones.Filter.NotNew=Cacher les nouveaux Sones +Page.KnownSones.Filter.Own=Show only local Sones +Page.KnownSones.Filter.NotOwn=Show only remote Sones Page.KnownSones.Button.Apply=Appliquer Page.KnownSones.Button.FollowAllSones=Suivre tous les Sones de cette page Page.KnownSones.Button.UnfollowAllSones=Ne plus suivre tous les Sones de cette page @@ -376,18 +378,18 @@ View.Time.InTheFuture=dans le futur View.Time.AFewSecondsAgo=au cours des dernières secondes passées View.Time.HalfAMinuteAgo=au cours des 30 dernières secondes View.Time.AMinuteAgo=au cours de la dernière minute -View.Time.XMinutesAgo=${min} il y a quelques minutes +View.Time.XMinutesAgo=il y a environs {min} minutes View.Time.HalfAnHourAgo=au cours de la dernière demi heure View.Time.AnHourAgo=il y a environ une heure -View.Time.XHoursAgo=${heure} au cours des dernières heures +View.Time.XHoursAgo=Il y a environ ${hour} heures View.Time.ADayAgo=il y a environ un jour -View.Time.XDaysAgo=${jour} il y a quelques jours +View.Time.XDaysAgo=il y a plus ou moins ${day} jours View.Time.AWeekAgo=il y a environ une semaine -View.Time.XWeeksAgo=${semaine} au cours des dernières semaines +View.Time.XWeeksAgo=au cours des dernières ${week}semaines View.Time.AMonthAgo=au cours du dernier mois -View.Time.XMonthsAgo=${mois} au cours des derniers mois +View.Time.XMonthsAgo=au cours des derniers ${month} mois View.Time.AYearAgo=au cours de la dernière année -View.Time.XYearsAgo=${année} au cours des dernières années +View.Time.XYearsAgo=au cours des dernières ${year} années WebInterface.DefaultText.StatusUpdate=Exprimez-vous WebInterface.DefaultText.Message=Écrire un message... @@ -450,3 +452,4 @@ Notification.ImageInsertFailed.Text=Les images suivantes ne peuvent être insér Notification.Mention.ShortText=Vous avez été mentionné. Notification.Mention.Text=Vous avez été mentionné dans les messages suivants: Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1
  • - checked="checked"<%/if>/> + checked="checked"<%/if>/> <%= Page.EditProfile.Avatar.Delete|l10n|html>
  • <%foreach currentSone.allImages image>
  • - checked="checked"<%/if>/> + checked="checked"<%/if>/>
    <% image|image-link max-width==48 max-height==48 mode==enlarge title=image.title>
  • <%/foreach> diff --git a/src/main/resources/templates/include/viewPost.html b/src/main/resources/templates/include/viewPost.html index 7120d06..efc3d53 100644 --- a/src/main/resources/templates/include/viewPost.html +++ b/src/main/resources/templates/include/viewPost.html @@ -18,12 +18,12 @@
    class="hidden"<%/if>> - <%ifnull !post.recipient> + <%if post.recipientId.present> → - <%ifnull post.recipient.identity> - + <%if !post.recipient.present> + <%else> - + <%/if> <%/if> <% post.text|html|store key==originalText text==true> diff --git a/src/main/resources/templates/insert/sone.xml b/src/main/resources/templates/insert/sone.xml index e615725..511cbf2 100644 --- a/src/main/resources/templates/insert/sone.xml +++ b/src/main/resources/templates/insert/sone.xml @@ -31,7 +31,7 @@ <%foreach currentSone.posts post> <% post.id|xml> - <%ifnull !post.recipient><% post.recipient.id|xml><%/if> + <%if post.recipientId.present><% post.recipientId.get|xml><%/if> <% post.text|xml> @@ -42,7 +42,7 @@ <%foreach currentSone.replies reply> <% reply.id> - <% reply.post.id|xml> + <% reply.postId|xml> <% reply.text|xml> diff --git a/src/main/resources/templates/knownSones.html b/src/main/resources/templates/knownSones.html index 4ace659..c113748 100644 --- a/src/main/resources/templates/knownSones.html +++ b/src/main/resources/templates/knownSones.html @@ -48,6 +48,8 @@ <%/if> + +
    <%/if> diff --git a/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java b/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java new file mode 100644 index 0000000..ae1993a --- /dev/null +++ b/src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java @@ -0,0 +1,87 @@ +/* + * Sone - LockSoneCommandTest.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.fcp; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder; +import net.pterodactylus.sone.freenet.fcp.Command.Response; +import net.pterodactylus.sone.freenet.fcp.FcpException; + +import freenet.support.SimpleFieldSet; + +import com.google.common.base.Optional; +import org.junit.Test; + +/** + * Tests for {@link UnlockSoneCommand}. + * + * @author David ‘Bombe’ Roden + */ +public class LockSoneCommandTest { + + @Test + public void testLockingALocalSone() throws FcpException { + Sone localSone = mock(Sone.class); + when(localSone.getId()).thenReturn("LocalSone"); + when(localSone.isLocal()).thenReturn(true); + Core core = mock(Core.class); + when(core.getSone(eq("LocalSone"))).thenReturn(Optional.of(localSone)); + when(core.getLocalSone(eq("LocalSone"), anyBoolean())).thenReturn(localSone); + SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "LocalSone").get(); + + LockSoneCommand lockSoneCommand = new LockSoneCommand(core); + Response response = lockSoneCommand.execute(fields, null, null); + + verify(core).lockSone(eq(localSone)); + assertThat(response, notNullValue()); + assertThat(response.getReplyParameters(), notNullValue()); + assertThat(response.getReplyParameters().get("Message"), is("SoneLocked")); + assertThat(response.getReplyParameters().get("Sone"), is("LocalSone")); + } + + @Test(expected = FcpException.class) + public void testLockingARemoteSone() throws FcpException { + Sone removeSone = mock(Sone.class); + Core core = mock(Core.class); + when(core.getSone(eq("RemoteSone"))).thenReturn(Optional.of(removeSone)); + SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "RemoteSone").get(); + + LockSoneCommand lockSoneCommand = new LockSoneCommand(core); + lockSoneCommand.execute(fields, null, null); + } + + @Test(expected = FcpException.class) + public void testMissingSone() throws FcpException { + Core core = mock(Core.class); + SimpleFieldSet fields = new SimpleFieldSetBuilder().get(); + + LockSoneCommand lockSoneCommand = new LockSoneCommand(core); + lockSoneCommand.execute(fields, null, null); + } + +} diff --git a/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java b/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java new file mode 100644 index 0000000..b966b19 --- /dev/null +++ b/src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java @@ -0,0 +1,87 @@ +/* + * Sone - LockSoneCommandTest.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.fcp; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder; +import net.pterodactylus.sone.freenet.fcp.Command.Response; +import net.pterodactylus.sone.freenet.fcp.FcpException; + +import freenet.support.SimpleFieldSet; + +import com.google.common.base.Optional; +import org.junit.Test; + +/** + * Tests for {@link LockSoneCommand}. + * + * @author David ‘Bombe’ Roden + */ +public class UnlockSoneCommandTest { + + @Test + public void testUnlockingALocalSone() throws FcpException { + Sone localSone = mock(Sone.class); + when(localSone.getId()).thenReturn("LocalSone"); + when(localSone.isLocal()).thenReturn(true); + Core core = mock(Core.class); + when(core.getSone(eq("LocalSone"))).thenReturn(Optional.of(localSone)); + when(core.getLocalSone(eq("LocalSone"), anyBoolean())).thenReturn(localSone); + SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "LocalSone").get(); + + UnlockSoneCommand unlockSoneCommand = new UnlockSoneCommand(core); + Response response = unlockSoneCommand.execute(fields, null, null); + + verify(core).unlockSone(eq(localSone)); + assertThat(response, notNullValue()); + assertThat(response.getReplyParameters(), notNullValue()); + assertThat(response.getReplyParameters().get("Message"), is("SoneUnlocked")); + assertThat(response.getReplyParameters().get("Sone"), is("LocalSone")); + } + + @Test(expected = FcpException.class) + public void testUnlockingARemoteSone() throws FcpException { + Sone removeSone = mock(Sone.class); + Core core = mock(Core.class); + when(core.getSone(eq("RemoteSone"))).thenReturn(Optional.of(removeSone)); + SimpleFieldSet fields = new SimpleFieldSetBuilder().put("Sone", "RemoteSone").get(); + + UnlockSoneCommand unlockSoneCommand = new UnlockSoneCommand(core); + unlockSoneCommand.execute(fields, null, null); + } + + @Test(expected = FcpException.class) + public void testMissingSone() throws FcpException { + Core core = mock(Core.class); + SimpleFieldSet fields = new SimpleFieldSetBuilder().get(); + + UnlockSoneCommand unlockSoneCommand = new UnlockSoneCommand(core); + unlockSoneCommand.execute(fields, null, null); + } + +} diff --git a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java index 7baceaa..7286001 100644 --- a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java +++ b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java @@ -1,5 +1,5 @@ /* - * Sone - SoneTextParserTest.java - Copyright © 2011–2012 David Roden + * Sone - SoneTextParserTest.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 @@ -19,10 +19,14 @@ package net.pterodactylus.sone.text; import java.io.IOException; import java.io.StringReader; +import java.util.Arrays; +import java.util.Collection; + +import com.google.common.base.Optional; import junit.framework.TestCase; -import net.pterodactylus.sone.core.SoneProvider; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.SoneProvider; /** * JUnit test case for {@link SoneTextParser}. @@ -106,6 +110,24 @@ public class SoneTextParserTest extends TestCase { assertEquals("Part Text", "Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff.", convertText(parts, PlainTextPart.class, SonePart.class)); } + /** + * Test for a bug discovered in Sone 0.8.4 where a plain “http://” would be + * parsed into a link. + * + * @throws IOException + * if an I/O error occurs + */ + @SuppressWarnings({ "synthetic-access", "static-method" }) + public void testEmpyHttpLinks() throws IOException { + SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null); + Iterable parts; + + /* check empty http links. */ + parts = soneTextParser.parse(null, new StringReader("Some text. Empty link: http:// – nice!")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Some text. Empty link: http:// – nice!", convertText(parts, PlainTextPart.class)); + } + // // PRIVATE METHODS // @@ -133,7 +155,7 @@ public class SoneTextParserTest extends TestCase { } } if (!classValid) { - assertEquals("Part’s Class", null, part.getClass()); + fail("Part’s Class (" + part.getClass() + ") is not one of " + Arrays.toString(validClasses)); } if (part instanceof PlainTextPart) { text.append(((PlainTextPart) part).getText()); @@ -162,8 +184,8 @@ public class SoneTextParserTest extends TestCase { * {@inheritDoc} */ @Override - public Sone getSone(final String soneId, boolean create) { - return new Sone(soneId) { + public Optional getSone(final String soneId) { + return Optional. fromNullable(new Sone(soneId, false) { /** * {@inheritDoc} @@ -172,7 +194,31 @@ public class SoneTextParserTest extends TestCase { public String getName() { return soneId; } - }; + }); + } + + /** + * {@inheritDocs} + */ + @Override + public Collection getSones() { + return null; + } + + /** + * {@inheritDocs} + */ + @Override + public Collection getLocalSones() { + return null; + } + + /** + * {@inheritDocs} + */ + @Override + public Collection getRemoteSones() { + return null; } }