group = 'net.pterodactylus'
-version = '0.9.7'
+version = '0.9.8'
buildscript {
- ext.kotlinVersion = '1.1.51'
+ ext.kotlinVersion = '1.2.0'
repositories {
mavenCentral()
}
testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test'
testCompile group: 'junit', name: 'junit', version: '4.11'
- testCompile group: 'org.mockito', name: 'mockito-core', version: '2.1.0'
+ testCompile group: 'org.mockito', name: 'mockito-core', version: '2.10.0'
testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
}
import net.pterodactylus.sone.core.SoneChangeDetector.PostProcessor;
import net.pterodactylus.sone.core.SoneChangeDetector.PostReplyProcessor;
import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent;
+import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
import net.pterodactylus.sone.core.event.MarkPostKnownEvent;
import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent;
import net.pterodactylus.sone.core.event.MarkSoneKnownEvent;
import net.pterodactylus.util.thread.NamedThreadFactory;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import kotlin.jvm.functions.Function1;
/**
* The Sone core.
return database.getSones();
}
+ @Nonnull
@Override
- public Function<String, Optional<Sone>> soneLoader() {
- return database.soneLoader();
+ public Function1<String, Sone> getSoneLoader() {
+ return database.getSoneLoader();
}
/**
* Sone
*/
@Override
- public Optional<Sone> getSone(String id) {
+ @Nullable
+ public Sone getSone(@Nonnull String id) {
return database.getSone(id);
}
* @return The Sone with the given ID, or {@code null}
*/
public Sone getLocalSone(String id) {
- Optional<Sone> sone = database.getSone(id);
- if (sone.isPresent() && sone.get().isLocal()) {
- return sone.get();
+ Sone sone = database.getSone(id);
+ if ((sone != null) && sone.isLocal()) {
+ return sone;
}
return null;
}
* @return The Sone with the given ID
*/
public Sone getRemoteSone(String id) {
- return database.getSone(id).orNull();
+ return database.getSone(id);
}
/**
return database.newPostBuilder();
}
- /**
- * {@inheritDoc}
- */
+ @Nullable
@Override
- public Optional<Post> getPost(String postId) {
+ public Post getPost(@Nonnull String postId) {
return database.getPost(postId);
}
/**
* {@inheritDoc}
*/
+ @Nullable
@Override
- public Optional<PostReply> getPostReply(String replyId) {
+ public PostReply getPostReply(String replyId) {
return database.getPostReply(replyId);
}
*/
@Nullable
public Album getAlbum(@Nonnull String albumId) {
- return database.getAlbum(albumId).orNull();
+ return database.getAlbum(albumId);
}
public ImageBuilder imageBuilder() {
*/
@Nullable
public Image getImage(String imageId, boolean create) {
- Optional<Image> image = database.getImage(imageId);
- if (image.isPresent()) {
- return image.get();
+ Image image = database.getImage(imageId);
+ if (image != null) {
+ return image;
}
if (!create) {
return null;
sone.setClient(new Client("Sone", SonePlugin.getPluginVersion()));
sone.setKnown(true);
SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId());
+ soneInserter.insertionDelayChanged(new InsertionDelayChangedEvent(preferences.getInsertionDelay()));
eventBus.register(soneInserter);
synchronized (soneInserters) {
soneInserters.put(sone, soneInserter);
}
String property = fromNullable(identity.getProperty("Sone.LatestEdition")).or("0");
long latestEdition = fromNullable(tryParse(property)).or(0L);
- Optional<Sone> existingSone = getSone(identity.getId());
- if (existingSone.isPresent() && existingSone.get().isLocal()) {
- return existingSone.get();
+ Sone existingSone = getSone(identity.getId());
+ if ((existingSone != null )&& existingSone.isLocal()) {
+ return existingSone;
}
- boolean newSone = !existingSone.isPresent();
- Sone sone = !newSone ? existingSone.get() : database.newSoneBuilder().from(identity).build();
+ boolean newSone = existingSone == null;
+ Sone sone = !newSone ? existingSone : database.newSoneBuilder().from(identity).build();
sone.setLatestEdition(latestEdition);
if (newSone) {
synchronized (knownSones) {
if (!soneFollowingTimes.containsKey(soneId)) {
long now = System.currentTimeMillis();
soneFollowingTimes.put(soneId, now);
- Optional<Sone> followedSone = getSone(soneId);
- if (!followedSone.isPresent()) {
+ Sone followedSone = getSone(soneId);
+ if (followedSone == null) {
return;
}
- for (Post post : followedSone.get().getPosts()) {
+ for (Post post : followedSone.getPosts()) {
if (post.getTime() < now) {
markPostKnown(post);
}
}
- for (PostReply reply : followedSone.get().getReplies()) {
+ for (PostReply reply : followedSone.getReplies()) {
if (reply.getTime() < now) {
markReplyKnown(reply);
}
* of the age of the given Sone
*/
public void updateSone(final Sone sone, boolean soneRescueMode) {
- Optional<Sone> storedSone = getSone(sone.getId());
- if (storedSone.isPresent()) {
- if (!soneRescueMode && !(sone.getTime() > storedSone.get().getTime())) {
+ Sone storedSone = getSone(sone.getId());
+ if (storedSone != null) {
+ if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) {
logger.log(Level.FINE, String.format("Downloaded Sone %s is not newer than stored Sone %s.", sone, storedSone));
return;
}
List<Object> events =
- collectEventsForChangesInSone(storedSone.get(), sone);
+ collectEventsForChangesInSone(storedSone, sone);
database.storeSone(sone);
for (Object event : events) {
eventBus.post(event);
}
- sone.setOptions(storedSone.get().getOptions());
- sone.setKnown(storedSone.get().isKnown());
+ sone.setOptions(storedSone.getOptions());
+ sone.setKnown(storedSone.isKnown());
sone.setStatus((sone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle);
if (sone.isLocal()) {
touchConfiguration();
if (sone.isLocal()) {
return;
}
- sone.setLatestEdition(fromNullable(tryParse(identity.getProperty("Sone.LatestEdition"))).or(sone.getLatestEdition()));
+ String newLatestEdition = identity.getProperty("Sone.LatestEdition");
+ if (newLatestEdition != null) {
+ Long parsedNewLatestEdition = tryParse(newLatestEdition);
+ if (parsedNewLatestEdition != null) {
+ sone.setLatestEdition(parsedNewLatestEdition);
+ }
+ }
soneDownloader.addSone(sone);
soneDownloaders.execute(soneDownloader.fetchSoneAction(sone));
}
return;
}
}
- Optional<Sone> sone = getSone(identity.getId());
- if (!sone.isPresent()) {
+ Sone sone = getSone(identity.getId());
+ if (sone == null) {
/* TODO - we don’t have the Sone anymore. should this happen? */
return;
}
- for (PostReply postReply : sone.get().getReplies()) {
+ for (PostReply postReply : sone.getReplies()) {
eventBus.post(new PostReplyRemovedEvent(postReply));
}
- for (Post post : sone.get().getPosts()) {
+ for (Post post : sone.getPosts()) {
eventBus.post(new PostRemovedEvent(post));
}
- eventBus.post(new SoneRemovedEvent(sone.get()));
- database.removeSone(sone.get());
+ eventBus.post(new SoneRemovedEvent(sone));
+ database.removeSone(sone);
}
/**
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
-import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Ordering;
import com.google.common.eventbus.EventBus;
this(core, eventBus, freenetInterface, soneId, new SoneModificationDetector(new LockableFingerprintProvider() {
@Override
public boolean isLocked() {
- final Optional<Sone> sone = core.getSone(soneId);
- if (!sone.isPresent()) {
+ Sone sone = core.getSone(soneId);
+ if (sone == null) {
return false;
}
- return core.isLocked(sone.get());
+ return core.isLocked(sone);
}
@Override
public String getFingerprint() {
- final Optional<Sone> sone = core.getSone(soneId);
- if (!sone.isPresent()) {
+ Sone sone = core.getSone(soneId);
+ if (sone == null) {
return null;
}
- return sone.get().getFingerprint();
+ return sone.getFingerprint();
}
}, insertionDelay), 1000);
}
sleep(delay);
if (soneModificationDetector.isEligibleForInsert()) {
- Optional<Sone> soneOptional = core.getSone(soneId);
- if (!soneOptional.isPresent()) {
+ Sone sone = core.getSone(soneId);
+ if (sone == null) {
logger.log(Level.WARNING, format("Sone %s has disappeared, exiting inserter.", soneId));
return;
}
- Sone sone = soneOptional.get();
InsertInformation insertInformation = new InsertInformation(sone);
logger.log(Level.INFO, String.format("Inserting Sone “%s”…", sone.getName()));
package net.pterodactylus.sone.data.impl;
+import static com.google.common.base.Optional.fromNullable;
+
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.database.SoneProvider;
*/
@Override
public Sone getSone() {
- return soneProvider.getSone(soneId).get();
+ return soneProvider.getSone(soneId);
}
/**
*/
@Override
public Optional<String> getRecipientId() {
- return Optional.fromNullable(recipientId);
+ return fromNullable(recipientId);
}
/**
*/
@Override
public Optional<Sone> getRecipient() {
- return soneProvider.getSone(recipientId);
+ return fromNullable(soneProvider.getSone(recipientId));
}
/**
package net.pterodactylus.sone.data.impl;
+import static com.google.common.base.Optional.fromNullable;
+
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.database.PostProvider;
*/
@Override
public Optional<Post> getPost() {
- return postProvider.getPost(postId);
+ return fromNullable(postProvider.getPost(postId));
}
}
*/
@Override
public Sone getSone() {
- return soneProvider.getSone(soneId).get();
+ return soneProvider.getSone(soneId);
}
/**
+++ /dev/null
-/*
- * Sone - AlbumBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Sone;
-
-/**
- * Builder for {@link Album} objects.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface AlbumBuilder {
-
- /**
- * Configures this builder to create an album with a random ID.
- *
- * @return This album builder
- */
- AlbumBuilder randomId();
-
- /**
- * Configures this builder to create an album with the given ID.
- *
- * @param id
- * The ID of the album
- * @return This album builder
- */
- AlbumBuilder withId(String id);
-
- AlbumBuilder by(Sone sone);
-
- /**
- * Creates the album.
- *
- * @return The created album
- * @throws IllegalStateException
- * if the album could not be created
- */
- Album build() throws IllegalStateException;
-
-}
--- /dev/null
+/*
+ * Sone - AlbumBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Album
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Builder for [Album] objects.
+ */
+interface AlbumBuilder {
+
+ fun randomId(): AlbumBuilder
+ fun withId(id: String): AlbumBuilder
+
+ fun by(sone: Sone): AlbumBuilder
+
+ fun build(): Album
+
+}
+++ /dev/null
-/*
- * Sone - AlbumBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-/**
- * Factory for {@link AlbumBuilder}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface AlbumBuilderFactory {
-
- /**
- * Creates a new album builder.
- *
- * @return A new album builder
- */
- AlbumBuilder newAlbumBuilder();
-
-}
--- /dev/null
+/*
+ * Sone - AlbumBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+/**
+ * Factory for [AlbumBuilder]s.
+ */
+interface AlbumBuilderFactory {
+
+ fun newAlbumBuilder(): AlbumBuilder
+
+}
+++ /dev/null
-/*
- * Sone - AlbumDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-/**
- * Combines an {@link AlbumProvider} and an {@link AlbumStore} into an album
- * database.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface AlbumDatabase extends AlbumProvider, AlbumBuilderFactory, AlbumStore {
-
- /* nothing here. */
-
-}
--- /dev/null
+/*
+ * Sone - AlbumDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+/**
+ * Combines an [AlbumProvider] and an [AlbumStore] into an album
+ * database.
+ */
+interface AlbumDatabase : AlbumProvider, AlbumBuilderFactory, AlbumStore
+++ /dev/null
-/*
- * Sone - AlbumProvider.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Album;
-
-import com.google.common.base.Optional;
-
-/**
- * Interface for objects that can provide {@link Album}s by their ID.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface AlbumProvider {
-
- /**
- * Returns the album with the given ID, or {@link Optional#absent()} if no such
- * album exists.
- *
- * @param albumId
- * The ID of the album
- * @return The album, or {@link Optional#absent()} if the album does not exist
- */
- Optional<Album> getAlbum(String albumId);
-
-}
--- /dev/null
+/*
+ * Sone - AlbumProvider.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Album
+
+import com.google.common.base.Optional
+
+/**
+ * Interface for objects that can provide [Album]s by their ID.
+ */
+interface AlbumProvider {
+
+ fun getAlbum(albumId: String): Album?
+
+}
+++ /dev/null
-/*
- * Sone - AlbumStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Album;
-
-/**
- * Interface for a store of albums.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface AlbumStore {
-
- /**
- * Stores the given album.
- *
- * @param album
- * The album to store
- */
- void storeAlbum(Album album);
-
- /**
- * Removes the given album from the store.
- *
- * @param album
- * The album to remove
- */
- void removeAlbum(Album album);
-
-}
--- /dev/null
+/*
+ * Sone - AlbumStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Album
+
+/**
+ * Interface for a store of albums.
+ */
+interface AlbumStore {
+
+ fun storeAlbum(album: Album)
+ fun removeAlbum(album: Album)
+
+}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-import java.util.Set;
-
-import net.pterodactylus.sone.data.Post;
-
-/**
- * Database interface for bookmark-related functionality.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface BookmarkDatabase {
-
- void bookmarkPost(Post post);
- void unbookmarkPost(Post post);
- boolean isPostBookmarked(Post post);
- Set<Post> getBookmarkedPosts();
-
-}
+++ /dev/null
-/*
- * Sone - Database.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.database.memory.MemoryDatabase;
-
-import com.google.common.util.concurrent.Service;
-import com.google.inject.ImplementedBy;
-
-/**
- * Database for Sone data. This interface combines the various provider,
- * store, and builder factory interfaces into a single interface.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-@ImplementedBy(MemoryDatabase.class)
-public interface Database extends Service, SoneDatabase, FriendDatabase, PostDatabase, PostReplyDatabase, AlbumDatabase, ImageDatabase, BookmarkDatabase {
-
- /**
- * Saves the database.
- *
- * @throws DatabaseException
- * if an error occurs while saving
- */
- public void save() throws DatabaseException;
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-/**
- * Combines a {@link FriendProvider} and a {@link FriendStore} into a friend database.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface FriendDatabase extends FriendProvider, FriendStore {
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-import java.util.Collection;
-
-import net.pterodactylus.sone.data.Sone;
-
-/**
- * Provides information about {@link Sone#getFriends() friends} of a {@link Sone}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface FriendProvider {
-
- Collection<String> getFriends(Sone localSone);
- boolean isFriend(Sone localSone, String friendSoneId);
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Sone;
-
-/**
- * Stores information about the {@link Sone#getFriends() friends} of a {@link Sone}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface FriendStore {
-
- void addFriend(Sone localSone, String friendSoneId);
- void removeFriend(Sone localSone, String friendSoneId);
-
-}
+++ /dev/null
-/*
- * Sone - ImageBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Image;
-
-/**
- * Builder for {@link Image} objects.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
- */
-public interface ImageBuilder {
-
- ImageBuilder randomId();
-
- ImageBuilder withId(String id);
-
- Image build() throws IllegalStateException;
-
-}
+++ /dev/null
-/*
- * Sone - ImageBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-/**
- * Factory for {@link ImageBuilder}s.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
- */
-public interface ImageBuilderFactory {
-
- ImageBuilder newImageBuilder();
-
-}
+++ /dev/null
-/*
- * Sone - ImageDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-/**
- * Combines an {@link ImageProvider}, an {@link ImageBuilderFactory}, and an
- * {@link ImageStore} into an image database.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface ImageDatabase extends ImageProvider, ImageBuilderFactory, ImageStore {
-
- /* nothing here. */
-
-}
+++ /dev/null
-/*
- * Sone - ImageProvider.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Image;
-
-import com.google.common.base.Optional;
-
-/**
- * Provides {@link Image}.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
- */
-public interface ImageProvider {
-
- Optional<Image> getImage(String imageId);
-
-}
+++ /dev/null
-/*
- * Sone - ImageStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Image;
-
-/**
- * Manages {@link Image} storage.
- *
- * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
- */
-public interface ImageStore {
-
- void storeImage(Image image);
-
- void removeImage(Image image);
-
-}
+++ /dev/null
-/*
- * Sone - PostBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Sone;
-
-/**
- * Builder for {@link Post} objects.
- * <p>
- * A {@link Post} consists of the following elements:
- * <ul>
- * <li>an ID,</li>
- * <li>a {@link Sone sender},</li>
- * <li>an optional {@link Sone recipient},</li>
- * <li>a time,</li>
- * <li>and a text.</li>
- * </ul>
- * 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 <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-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.
- * <p>
- * The following conditions must be met in order for this builder to be
- * configured correctly:
- * <ul>
- * <li>Exactly one of {@link #randomId()} or {@link #withId(String)} must
- * have been called.</li>
- * <li>The {@link #from(String) sender} must not be {@code null}.</li>
- * <li>Exactly one of {@link #currentTime()} or {@link #withTime(long)} must
- * have been called.</li>
- * <li>The {@link #withText(String) text} must not be {@code null} and must
- * contain something other than whitespace.</li>
- * <li>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}.</li>
- * </ul>
- *
- * @return A new post
- * @throws IllegalStateException
- * if this builder’s configuration is not valid
- */
- public Post build() throws IllegalStateException;
-
-}
+++ /dev/null
-/*
- * Sone - PostBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.database.memory.MemoryDatabase;
-
-import com.google.inject.ImplementedBy;
-
-/**
- * Factory for {@link PostBuilder}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-@ImplementedBy(MemoryDatabase.class)
-public interface PostBuilderFactory {
-
- /**
- * Creates a new post builder.
- *
- * @return A new post builder
- */
- public PostBuilder newPostBuilder();
-
-}
+++ /dev/null
-/*
- * Sone - PostDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-/**
- * Combines a {@link PostProvider}, a {@link PostBuilderFactory}, and a
- * {@link PostStore} into a complete post database.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface PostDatabase extends PostProvider, PostBuilderFactory, PostStore {
-
- /* nothing here. */
-
-}
+++ /dev/null
-/*
- * Sone - PostProvider.java - Copyright © 2011–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import java.util.Collection;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.database.memory.MemoryDatabase;
-
-import com.google.common.base.Optional;
-import com.google.inject.ImplementedBy;
-
-/**
- * Interface for objects that can provide {@link Post}s by their ID.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-@ImplementedBy(MemoryDatabase.class)
-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<Post> 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<Post> 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<Post> getDirectedPosts(String recipientId);
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.PostReply;
-
-/**
- * Builder for a {@link PostReply} object.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface PostReplyBuilder extends ReplyBuilder<PostReplyBuilder> {
-
- /**
- * 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.
- * <p>
- * The following conditions must be met in order for the configuration to be
- * considered valid:
- * <ul>
- * <li>Exactly one of {@link #randomId()} or {@link #withId(String)} must
- * have been called.</li>
- * <li>The {@link #from(String) sender} must not be {@code null}.</li>
- * <li>Exactly one of {@link #currentTime()} or {@link #withTime(long)} must
- * have been called.</li>
- * <li>The {@link #withText(String) text} must not be {@code null} and must
- * contain something other than whitespace.</li>
- * <li>The {@link #to(String) post} have been set.</li>
- * </ul>
- *
- * @return The created post reply
- * @throws IllegalStateException
- * if this builder’s configuration is not valid
- */
- public PostReply build() throws IllegalStateException;
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.database.memory.MemoryDatabase;
-
-import com.google.inject.ImplementedBy;
-
-/**
- * Factory for {@link PostReplyBuilder}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-@ImplementedBy(MemoryDatabase.class)
-public interface PostReplyBuilderFactory {
-
- /**
- * Creates a new post reply builder.
- *
- * @return A new post reply builder
- */
- public PostReplyBuilder newPostReplyBuilder();
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-/**
- * Combines a {@link PostReplyProvider}, a {@link PostReplyBuilderFactory}, and
- * a {@link PostReplyStore} into a complete post reply database.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface PostReplyDatabase extends PostReplyProvider, PostReplyBuilderFactory, PostReplyStore {
-
- /* nothing here. */
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyProvider.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-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 <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-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<PostReply> 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<PostReply> getReplies(String postId);
-
-}
+++ /dev/null
-/*
- * Sone - PostReplyStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-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 <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface PostReplyStore {
-
- /**
- * Stores the given post reply.
- *
- * @param postReply
- * The post reply
- */
- public void storePostReply(PostReply postReply);
-
- /**
- * Removes the given post reply from this store.
- *
- * @param postReply
- * The post reply to remove
- */
- public void removePostReply(PostReply postReply);
-
-}
+++ /dev/null
-/*
- * Sone - PostStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-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 <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-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);
-
-}
+++ /dev/null
-/*
- * Sone - ReplyBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-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 <B>
- * The type of the builder
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface ReplyBuilder<B extends ReplyBuilder<B>> {
-
- /**
- * 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);
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.freenet.wot.Identity;
-
-/**
- * Builder for {@link Sone} objects.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface SoneBuilder {
-
- SoneBuilder from(Identity identity);
- SoneBuilder local();
-
- Sone build() throws IllegalStateException;
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-/**
- * Factory for {@link SoneBuilder}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface SoneBuilderFactory {
-
- SoneBuilder newSoneBuilder();
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-/**
- * Combines a {@link SoneProvider} and a {@link SoneStore} into a Sone
- * database.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface SoneDatabase extends SoneProvider, SoneBuilderFactory, SoneStore {
-
-}
+++ /dev/null
-/*
- * Sone - SoneProvider.java - Copyright © 2011–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.database;
-
-import java.util.Collection;
-
-import javax.annotation.Nonnull;
-
-import net.pterodactylus.sone.core.Core;
-import net.pterodactylus.sone.data.Sone;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.inject.ImplementedBy;
-
-/**
- * Interface for objects that can provide {@link Sone}s by their ID.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-@ImplementedBy(Core.class)
-public interface SoneProvider {
-
- Function<String, Optional<Sone>> soneLoader();
-
- /**
- * 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<Sone> getSone(String soneId);
-
- /**
- * Returns all Sones.
- *
- * @return All Sones
- */
- @Nonnull
- public Collection<Sone> getSones();
-
- /**
- * Returns all local Sones.
- *
- * @return All local Sones
- */
- public Collection<Sone> getLocalSones();
-
- /**
- * Returns all remote Sones.
- *
- * @return All remote Sones
- */
- public Collection<Sone> getRemoteSones();
-
-}
+++ /dev/null
-package net.pterodactylus.sone.database;
-
-import net.pterodactylus.sone.data.Sone;
-
-/**
- * Interface for a store for {@link Sone}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public interface SoneStore {
-
- void storeSone(Sone sone);
- void removeSone(Sone sone);
-
-}
package net.pterodactylus.sone.database.memory;
+import static com.google.common.base.Optional.fromNullable;
import static com.google.common.collect.FluentIterable.from;
import java.util.HashSet;
new Function<String, Post>() {
@Override
public Post apply(String postId) {
- return memoryDatabase.getPost(postId)
+ return fromNullable(memoryDatabase.getPost(postId))
.or(new EmptyPost(postId));
}
}).toSet();
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.util.config.Configuration;
import net.pterodactylus.util.config.ConfigurationException;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.AbstractService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import kotlin.jvm.functions.Function1;
/**
* Memory-based {@link PostDatabase} implementation.
}
}
+ @Nonnull
@Override
- public Function<String, Optional<Sone>> soneLoader() {
- return new Function<String, Optional<Sone>>() {
+ public Function1<String, Sone> getSoneLoader() {
+ return new Function1<String, Sone>() {
@Override
- public Optional<Sone> apply(String soneId) {
+ public Sone invoke(String soneId) {
return getSone(soneId);
}
};
}
@Override
- public Optional<Sone> getSone(String soneId) {
+ public Sone getSone(String soneId) {
lock.readLock().lock();
try {
- return fromNullable(allSones.get(soneId));
+ return allSones.get(soneId);
} finally {
lock.readLock().unlock();
}
// POSTPROVIDER METHODS
//
- /** {@inheritDocs} */
+ @Nullable
@Override
- public Optional<Post> getPost(String postId) {
+ public Post getPost(@Nonnull String postId) {
lock.readLock().lock();
try {
- return fromNullable(allPosts.get(postId));
+ return allPosts.get(postId);
} finally {
lock.readLock().unlock();
}
// POSTREPLYPROVIDER METHODS
//
- /** {@inheritDocs} */
+ @Nullable
@Override
- public Optional<PostReply> getPostReply(String id) {
+ public PostReply getPostReply(String id) {
lock.readLock().lock();
try {
- return fromNullable(allPostReplies.get(id));
+ return allPostReplies.get(id);
} finally {
lock.readLock().unlock();
}
// ALBUMPROVDER METHODS
//
+ @Nullable
@Override
- public Optional<Album> getAlbum(String albumId) {
+ public Album getAlbum(@Nonnull String albumId) {
lock.readLock().lock();
try {
- return fromNullable(allAlbums.get(albumId));
+ return allAlbums.get(albumId);
} finally {
lock.readLock().unlock();
}
// IMAGEPROVIDER METHODS
//
+ @Nullable
@Override
- public Optional<Image> getImage(String imageId) {
+ public Image getImage(@Nonnull String imageId) {
lock.readLock().lock();
try {
- return fromNullable(allImages.get(imageId));
+ return allImages.get(imageId);
} finally {
lock.readLock().unlock();
}
package net.pterodactylus.sone.database.memory;
+import static com.google.common.base.Optional.fromNullable;
+
import java.util.UUID;
import net.pterodactylus.sone.data.Post;
*/
@Override
public Sone getSone() {
- return soneProvider.getSone(soneId).get();
+ return soneProvider.getSone(soneId);
}
/**
*/
@Override
public Optional<String> getRecipientId() {
- return Optional.fromNullable(recipientId);
+ return fromNullable(recipientId);
}
/**
*/
@Override
public Optional<Sone> getRecipient() {
- return soneProvider.getSone(recipientId);
+ return fromNullable(soneProvider.getSone(recipientId));
}
/**
package net.pterodactylus.sone.database.memory;
+import static com.google.common.base.Optional.fromNullable;
+
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
*/
@Override
public Sone getSone() {
- return soneProvider.getSone(soneId).get();
+ return soneProvider.getSone(soneId);
}
/**
*/
@Override
public Optional<Post> getPost() {
- return database.getPost(postId);
+ return fromNullable(database.getPost(postId));
}
//
if (mandatory && (soneId == null)) {
throw new FcpException("Could not load Sone ID from “" + parameterName + "”.");
}
- Optional<Sone> sone = core.getSone(soneId);
- if ((mandatory && !sone.isPresent()) || (sone.isPresent() && localOnly && !sone.get().isLocal())) {
+ Sone sone = core.getSone(soneId);
+ if ((mandatory && (sone == null)) || ((sone != null) && localOnly && !sone.isLocal())) {
throw new FcpException("Could not load Sone from “" + soneId + "”.");
}
- return sone;
+ return Optional.fromNullable(sone);
}
/**
protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
try {
String postId = simpleFieldSet.getString(parameterName);
- Optional<Post> post = core.getPost(postId);
- if (!post.isPresent()) {
+ Post post = core.getPost(postId);
+ if (post == null) {
throw new FcpException("Could not load post from “" + postId + "”.");
}
- return post.get();
+ return post;
} catch (FSParseException fspe1) {
throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1);
}
protected PostReply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
try {
String replyId = simpleFieldSet.getString(parameterName);
- Optional<PostReply> reply = core.getPostReply(replyId);
- if (!reply.isPresent()) {
+ PostReply reply = core.getPostReply(replyId);
+ if (reply == null) {
throw new FcpException("Could not load reply from “" + replyId + "”.");
}
- return reply.get();
+ return reply;
} catch (FSParseException fspe1) {
throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1);
}
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.fcp.FcpException;
-import com.google.common.base.Optional;
import com.google.common.collect.Collections2;
import freenet.support.SimpleFieldSet;
Collection<Post> allPosts = new HashSet<Post>();
allPosts.addAll(sone.getPosts());
for (String friendSoneId : sone.getFriends()) {
- Optional<Sone> friendSone = getCore().getSone(friendSoneId);
- if (!friendSone.isPresent()) {
+ Sone friendSone = getCore().getSone(friendSoneId);
+ if (friendSone == null) {
continue;
}
- allPosts.addAll(friendSone.get().getPosts());
+ allPosts.addAll(friendSone.getPosts());
}
allPosts.addAll(getCore().getDirectedPosts(sone.getId()));
allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER);
import java.util.logging.LogRecord;
import java.util.logging.Logger;
+import javax.inject.Singleton;
+
import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.database.Database;
+import net.pterodactylus.sone.database.memory.MemoryDatabase;
import net.pterodactylus.sone.fcp.FcpInterface;
import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
import net.pterodactylus.sone.freenet.wot.Context;
/** The current year at time of release. */
private static final int YEAR = 2017;
private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
- private static final int LATEST_EDITION = 76;
+ private static final int LATEST_EDITION = 77;
/** The logger. */
private static final Logger logger = getLogger(SonePlugin.class.getName());
bind(PluginVersion.class).toInstance(new PluginVersion(getVersion()));
bind(PluginYear.class).toInstance(new PluginYear(getYear()));
bind(PluginHomepage.class).toInstance(new PluginHomepage(getHomepage()));
+ bind(Database.class).to(MemoryDatabase.class).in(Singleton.class);
if (startConfiguration.getBooleanValue("Developer.LoadFromFilesystem").getValue(false)) {
String path = startConfiguration.getStringValue("Developer.FilesystemPath").getValue(null);
if (path != null) {
+++ /dev/null
-/*
- * Sone - SoneTextParser.java - Copyright © 2010–2016 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 <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.text;
-
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.of;
-import static java.util.logging.Logger.getLogger;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.data.impl.IdOnlySone;
-import net.pterodactylus.sone.database.PostProvider;
-import net.pterodactylus.sone.database.SoneProvider;
-
-import com.google.common.base.Optional;
-import org.bitpedia.util.Base32;
-
-import freenet.keys.FreenetURI;
-import freenet.support.Base64;
-
-/**
- * {@link Parser} implementation that can recognize Freenet URIs.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class SoneTextParser implements Parser<SoneTextParserContext> {
-
- /** The logger. */
- private static final Logger logger = getLogger(SoneTextParser.class.getName());
-
- /** Pattern to detect whitespace. */
- private static final Pattern whitespacePattern = Pattern.compile("[\\u000a\u0020\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u2800\u3000]");
-
- private static class NextLink {
-
- private final int position;
- private final String link;
- private final String remainder;
- private final LinkType linkType;
-
- private NextLink(int position, String link, String remainder, LinkType linkType) {
- this.position = position;
- this.link = link;
- this.remainder = remainder;
- this.linkType = linkType;
- }
-
- public int getPosition() {
- return position;
- }
-
- public String getLink() {
- return link;
- }
-
- public String getRemainder() {
- return remainder;
- }
-
- public LinkType getLinkType() {
- return linkType;
- }
-
- }
-
- /**
- * Enumeration for all recognized link types.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
- private enum LinkType {
-
- KSK("KSK@", true),
- CHK("CHK@", true),
- SSK("SSK@", true),
- USK("USK@", true),
- HTTP("http://", false),
- HTTPS("https://", false),
- SONE("sone://", false),
- POST("post://", false),
-
- FREEMAIL("", true) {
- @Override
- public Optional<NextLink> findNext(String line) {
- int nextFreemailSuffix = line.indexOf(".freemail");
- if (nextFreemailSuffix < 54) {
- /* 52 chars for the id, 1 on @, at least 1 for the local part. */
- return absent();
- }
- if (line.charAt(nextFreemailSuffix - 53) != '@') {
- return absent();
- }
- if (!line.substring(nextFreemailSuffix - 52, nextFreemailSuffix).matches("^[a-z2-7]*$")) {
- return absent();
- }
- int startOfLocalPart = nextFreemailSuffix - 54;
- if (!isAllowedInLocalPart(line.charAt(startOfLocalPart))) {
- return absent();
- }
- while ((startOfLocalPart > 0) && isAllowedInLocalPart(line.charAt(startOfLocalPart - 1))) {
- startOfLocalPart--;
- }
- return of(new NextLink(startOfLocalPart, line.substring(startOfLocalPart, nextFreemailSuffix + 9), line.substring(nextFreemailSuffix + 9), this));
- }
-
- private boolean isAllowedInLocalPart(char character) {
- return ((character >= 'A') && (character <= 'Z'))
- || ((character >= 'a') && (character <= 'z'))
- || ((character >= '0') && (character <= '9'))
- || (character == '.') || (character == '-') || (character == '_');
- }
- };
-
- private final String scheme;
- private final boolean freenetLink;
-
- LinkType(String scheme, boolean freenetLink) {
- this.scheme = scheme;
- this.freenetLink = freenetLink;
- }
-
- /**
- * Returns the scheme of this link type.
- *
- * @return The scheme of this link type
- */
- public String getScheme() {
- return scheme;
- }
-
- public boolean isFreenetLink() {
- return freenetLink;
- }
-
- public Optional<NextLink> findNext(String line) {
- int nextLinkPosition = line.indexOf(getScheme());
- if (nextLinkPosition == -1) {
- return absent();
- }
- int endOfLink = findEndOfLink(line.substring(nextLinkPosition));
- return of(new NextLink(nextLinkPosition, line.substring(nextLinkPosition, nextLinkPosition + endOfLink), line.substring(nextLinkPosition + endOfLink), this));
- }
-
- private static int findEndOfLink(String line) {
- Matcher matcher = whitespacePattern.matcher(line);
- int endOfLink = matcher.find() ? matcher.start() : line.length();
- while (isPunctuation(line.charAt(endOfLink - 1))) {
- endOfLink--;
- }
- int openParens = 0;
- for (int i = 0; i < endOfLink; i++) {
- switch (line.charAt(i)) {
- case '(':
- openParens++;
- break;
- case ')':
- openParens--;
- if (openParens < 0) {
- return i;
- }
- default:
- }
- }
- return endOfLink;
- }
-
- }
-
- /** The Sone provider. */
- private final SoneProvider soneProvider;
-
- /** The post provider. */
- private final PostProvider postProvider;
-
- /**
- * Creates a new freenet link parser.
- *
- * @param soneProvider
- * The Sone provider
- * @param postProvider
- * The post provider
- */
- public SoneTextParser(SoneProvider soneProvider, PostProvider postProvider) {
- this.soneProvider = soneProvider;
- this.postProvider = postProvider;
- }
-
- //
- // PART METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Nonnull
- @Override
- public Iterable<Part> parse(@Nonnull String source, @Nullable SoneTextParserContext context) {
- List<Part> parts = new ArrayList<>();
- try (Reader sourceReader = new StringReader(source);
- BufferedReader bufferedReader = new BufferedReader(sourceReader)) {
- String line;
- boolean lastLineEmpty = true;
- int emptyLines = 0;
- while ((line = bufferedReader.readLine()) != null) {
- if (line.trim().length() == 0) {
- if (lastLineEmpty) {
- continue;
- }
- parts.add(new PlainTextPart("\n"));
- ++emptyLines;
- lastLineEmpty = emptyLines == 2;
- continue;
- }
- emptyLines = 0;
- /*
- * lineComplete tracks whether the block you are parsing is the
- * first block of the line. this is important because sometimes
- * you have to add an additional line break.
- */
- boolean lineComplete = true;
- while (line.length() > 0) {
- Optional<NextLink> nextLink = findNextLink(line);
- if (!nextLink.isPresent()) {
- if (lineComplete && !lastLineEmpty) {
- parts.add(new PlainTextPart("\n" + line));
- } else {
- parts.add(new PlainTextPart(line));
- }
- break;
- }
- LinkType linkType = nextLink.get().getLinkType();
- int next = nextLink.get().getPosition();
-
- /* cut off “freenet:” from before keys. */
- if (linkType.isFreenetLink() && (next >= 8) && (line.substring(next - 8, next).equals("freenet:"))) {
- next -= 8;
- line = line.substring(0, next) + line.substring(next + 8);
- }
-
- /* if there is text before the next item, write it out. */
- if (lineComplete && !lastLineEmpty) {
- parts.add(new PlainTextPart("\n"));
- }
- if (next > 0) {
- parts.add(new PlainTextPart(line.substring(0, next)));
- line = line.substring(next);
- }
- lineComplete = false;
-
- String link = nextLink.get().getLink();
- logger.log(Level.FINER, String.format("Found link: %s", link));
-
- /* 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;
- }
-
- switch (linkType) {
- case SONE:
- renderSoneLink(parts, link);
- break;
- case POST:
- renderPostLink(parts, link);
- break;
- case KSK:
- case CHK:
- case SSK:
- case USK:
- renderFreenetLink(parts, link, linkType, context);
- break;
- case HTTP:
- case HTTPS:
- renderHttpLink(parts, link, linkType);
- break;
- case FREEMAIL:
- renderFreemailLink(parts, link);
- }
-
- line = nextLink.get().getRemainder();
- }
- lastLineEmpty = false;
- }
- } catch (IOException ioe1) {
- // a buffered reader around a string reader should never throw.
- throw new RuntimeException(ioe1);
- }
- for (int partIndex = parts.size() - 1; partIndex >= 0; --partIndex) {
- Part part = parts.get(partIndex);
- if (!(part instanceof PlainTextPart) || !"\n".equals(part.getText())) {
- break;
- }
- parts.remove(partIndex);
- }
- return parts;
- }
-
- public static Optional<NextLink> findNextLink(String line) {
- int earliestLinkPosition = Integer.MAX_VALUE;
- NextLink earliestNextLink = null;
- for (LinkType possibleLinkType : LinkType.values()) {
- Optional<NextLink> nextLink = possibleLinkType.findNext(line);
- if (nextLink.isPresent()) {
- if (nextLink.get().getPosition() < earliestLinkPosition) {
- earliestNextLink = nextLink.get();
- earliestLinkPosition = earliestNextLink.getPosition();
- }
- }
- }
- return Optional.fromNullable(earliestNextLink);
- }
-
- private void renderSoneLink(List<Part> parts, String line) {
- if (line.length() >= (7 + 43)) {
- String soneId = line.substring(7, 50);
- Optional<Sone> sone = soneProvider.getSone(soneId);
- parts.add(new SonePart(sone.or(new IdOnlySone(soneId))));
- } else {
- parts.add(new PlainTextPart(line));
- }
- }
-
- private void renderPostLink(List<Part> parts, String line) {
- if (line.length() >= (7 + 36)) {
- String postId = line.substring(7, 43);
- Optional<Post> post = postProvider.getPost(postId);
- if (post.isPresent()) {
- parts.add(new PostPart(post.get()));
- } else {
- parts.add(new PlainTextPart(line.substring(0, 43)));
- }
- } else {
- parts.add(new PlainTextPart(line));
- }
- }
-
- private void renderFreenetLink(List<Part> parts, String link, LinkType linkType, @Nullable SoneTextParserContext context) {
- String name = link;
- String linkWithoutParameters = link;
- if (name.indexOf('?') > -1) {
- linkWithoutParameters = name = name.substring(0, name.indexOf('?'));
- }
- if (name.endsWith("/")) {
- name = name.substring(0, name.length() - 1);
- }
- try {
- FreenetURI uri = new FreenetURI(name);
- name = uri.lastMetaString();
- if (name == null) {
- name = uri.getDocName();
- }
- if (name == null) {
- name = link.substring(0, Math.min(9, link.length()));
- }
- boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
- parts.add(new FreenetLinkPart(link, name, linkWithoutParameters, fromPostingSone));
- } catch (MalformedURLException mue1) {
- /* not a valid link, insert as plain text. */
- parts.add(new PlainTextPart(link));
- } catch (NullPointerException npe1) {
- /* FreenetURI sometimes throws these, too. */
- parts.add(new PlainTextPart(link));
- } catch (ArrayIndexOutOfBoundsException aioobe1) {
- /* oh, and these, too. */
- parts.add(new PlainTextPart(link));
- }
- }
-
- private void renderHttpLink(List<Part> parts, String link, LinkType linkType) {
- String name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
- int firstSlash = name.indexOf('/');
- int lastSlash = name.lastIndexOf('/');
- if ((lastSlash - firstSlash) > 3) {
- name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
- }
- if (name.endsWith("/")) {
- name = name.substring(0, name.length() - 1);
- }
- if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
- name = name.substring(4);
- }
- if (name.indexOf('?') > -1) {
- name = name.substring(0, name.indexOf('?'));
- }
- parts.add(new LinkPart(link, name));
- }
-
- private void renderFreemailLink(List<Part> parts, String line) {
- int separator = line.indexOf('@');
- String freemailId = line.substring(separator + 1, separator + 53);
- String identityId = Base64.encode(Base32.decode(freemailId));
- String emailLocalPart = line.substring(0, separator);
- parts.add(new FreemailPart(emailLocalPart, freemailId, identityId));
- }
-
- private static boolean isPunctuation(char character) {
- return (character == '.') || (character == ',') || (character == '!') || (character == '?');
- }
-
-}
--- /dev/null
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Post
+
+/**
+ * Database interface for bookmark-related functionality.
+ */
+interface BookmarkDatabase {
+
+ val bookmarkedPosts: Set<Post>
+
+ fun bookmarkPost(post: Post)
+ fun unbookmarkPost(post: Post)
+ fun isPostBookmarked(post: Post): Boolean
+
+}
--- /dev/null
+/*
+ * Sone - Database.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+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.
+ */
+interface Database : Service, SoneDatabase, FriendDatabase, PostDatabase, PostReplyDatabase, AlbumDatabase, ImageDatabase, BookmarkDatabase {
+
+ @Throws(DatabaseException::class)
+ fun save()
+
+}
--- /dev/null
+package net.pterodactylus.sone.database
+
+/**
+ * Combines a [FriendProvider] and a [FriendStore] into a friend database.
+ */
+interface FriendDatabase : FriendProvider, FriendStore
--- /dev/null
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Provides information about [friends][Sone.getFriends] of a [Sone].
+ */
+interface FriendProvider {
+
+ fun getFriends(localSone: Sone): Collection<String>
+ fun isFriend(localSone: Sone, friendSoneId: String): Boolean
+
+}
--- /dev/null
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Stores information about the [friends][Sone.getFriends] of a [Sone].
+ */
+interface FriendStore {
+
+ fun addFriend(localSone: Sone, friendSoneId: String)
+ fun removeFriend(localSone: Sone, friendSoneId: String)
+
+}
--- /dev/null
+/*
+ * Sone - ImageBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Image
+
+/**
+ * Builder for [Image] objects.
+ */
+interface ImageBuilder {
+
+ fun randomId(): ImageBuilder
+ fun withId(id: String): ImageBuilder
+
+ fun build(): Image
+
+}
--- /dev/null
+/*
+ * Sone - ImageBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+/**
+ * Factory for [ImageBuilder]s.
+ */
+interface ImageBuilderFactory {
+
+ fun newImageBuilder(): ImageBuilder
+
+}
--- /dev/null
+/*
+ * Sone - ImageDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+/**
+ * Combines an [ImageProvider], an [ImageBuilderFactory], and an
+ * [ImageStore] into an image database.
+ */
+interface ImageDatabase : ImageProvider, ImageBuilderFactory, ImageStore
--- /dev/null
+/*
+ * Sone - ImageProvider.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Image
+
+import com.google.common.base.Optional
+
+/**
+ * Provides [Image]s.
+ */
+interface ImageProvider {
+
+ fun getImage(imageId: String): Image?
+
+}
--- /dev/null
+/*
+ * Sone - ImageStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Image
+
+/**
+ * Manages [Image] storage.
+ */
+interface ImageStore {
+
+ fun storeImage(image: Image)
+ fun removeImage(image: Image)
+
+}
--- /dev/null
+/*
+ * Sone - PostBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Builder for [Post] objects.
+ *
+ *
+ * A [Post] consists of the following elements:
+ *
+ * * an ID,
+ * * a [sender][Sone],
+ * * an optional [recipient][Sone],
+ * * 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.
+ */
+interface PostBuilder {
+
+ fun copyPost(post: Post): PostBuilder
+
+ fun from(senderId: String): PostBuilder
+
+ fun randomId(): PostBuilder
+ fun withId(id: String): PostBuilder
+
+ fun currentTime(): PostBuilder
+ fun withTime(time: Long): PostBuilder
+
+ fun withText(text: String): PostBuilder
+
+ fun to(recipientId: String): PostBuilder
+
+ /**
+ * 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 [randomId] or [withId] must have been called.
+ * * The [sender][from] must not be `null`.
+ * * Exactly one of [currentTime] or [withTime] must have been called.
+ * * The [text][withText] must not be `null` and must contain something other than whitespace.
+ * * The [recipient][to] must either not have been set, or it must have been set to a [Sone] other than [the sender][from].
+ *
+ * @return A new post
+ * @throws IllegalStateException if this builder’s configuration is not valid
+ */
+ fun build(): Post
+
+}
--- /dev/null
+/*
+ * Sone - PostBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.database.memory.MemoryDatabase
+
+import com.google.inject.ImplementedBy
+
+/**
+ * Factory for [PostBuilder]s.
+ */
+@ImplementedBy(MemoryDatabase::class)
+interface PostBuilderFactory {
+
+ fun newPostBuilder(): PostBuilder
+
+}
--- /dev/null
+/*
+ * Sone - PostDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+/**
+ * Combines a [PostProvider], a [PostBuilderFactory], and a
+ * [PostStore] into a complete post database.
+ */
+interface PostDatabase : PostProvider, PostBuilderFactory, PostStore
--- /dev/null
+/*
+ * Sone - PostProvider.java - Copyright © 2011–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.database.memory.MemoryDatabase
+
+import com.google.common.base.Optional
+import com.google.inject.ImplementedBy
+
+/**
+ * Interface for objects that can provide [Post]s by their ID.
+ */
+@ImplementedBy(MemoryDatabase::class)
+interface PostProvider {
+
+ fun getPost(postId: String): Post?
+ fun getPosts(soneId: String): Collection<Post>
+ fun getDirectedPosts(recipientId: String): Collection<Post>
+
+}
--- /dev/null
+/*
+ * Sone - PostReplyBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.PostReply
+
+/**
+ * Builder for a [PostReply] object.
+ */
+interface PostReplyBuilder : ReplyBuilder<PostReplyBuilder> {
+
+ fun to(postId: String): PostReplyBuilder
+
+ /**
+ * 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 [randomId] or [withId] must have been called.
+ * * The [sender][from] must not be `null`.
+ * * Exactly one of [currentTime] or [withTime] must have been called.
+ * * The [text][withText] must not be `null` and must contain something other than whitespace.
+ * * The [post][to] must have been set.
+ *
+ * @return The created post reply
+ * @throws IllegalStateException if this builder’s configuration is not valid
+ */
+ fun build(): PostReply
+
+}
--- /dev/null
+/*
+ * Sone - PostReplyBuilderFactory.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.database.memory.MemoryDatabase
+
+import com.google.inject.ImplementedBy
+
+/**
+ * Factory for [PostReplyBuilder]s.
+ */
+@ImplementedBy(MemoryDatabase::class)
+interface PostReplyBuilderFactory {
+
+ fun newPostReplyBuilder(): PostReplyBuilder
+
+}
--- /dev/null
+/*
+ * Sone - PostReplyDatabase.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+/**
+ * Combines a [PostReplyProvider], a [PostReplyBuilderFactory], and
+ * a [PostReplyStore] into a complete post reply database.
+ */
+interface PostReplyDatabase : PostReplyProvider, PostReplyBuilderFactory, PostReplyStore
--- /dev/null
+/*
+ * Sone - PostReplyProvider.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import com.google.common.base.Optional
+import net.pterodactylus.sone.data.PostReply
+
+/**
+ * Interface for objects that can provide [PostReply]s.
+ */
+interface PostReplyProvider {
+
+ fun getPostReply(id: String): PostReply?
+
+ /**
+ * 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
+ */
+ fun getReplies(postId: String): List<PostReply>
+
+}
--- /dev/null
+/*
+ * Sone - PostReplyStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Defines a store for [post replies][PostReply].
+ */
+interface PostReplyStore {
+
+ fun storePostReply(postReply: PostReply)
+ fun removePostReply(postReply: PostReply)
+
+}
--- /dev/null
+/*
+ * Sone - PostStore.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Interface for a store for posts.
+ */
+interface PostStore {
+
+ fun storePost(post: Post)
+ fun removePost(post: Post)
+
+}
--- /dev/null
+/*
+ * Sone - ReplyBuilder.java - Copyright © 2013–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+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 [Reply].
+ *
+ * @param B The type of the builder
+ */
+interface ReplyBuilder<B : ReplyBuilder<B>> {
+
+ fun randomId(): B
+ fun withId(id: String): B
+
+ fun from(senderId: String): B
+ fun currentTime(): B
+ fun withTime(time: Long): B
+ fun withText(text: String): B
+
+}
--- /dev/null
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.freenet.wot.Identity
+
+/**
+ * Builder for [Sone] objects.
+ */
+interface SoneBuilder {
+
+ fun from(identity: Identity): SoneBuilder
+ fun local(): SoneBuilder
+
+ fun build(): Sone
+
+}
--- /dev/null
+package net.pterodactylus.sone.database
+
+/**
+ * Factory for [SoneBuilder]s.
+ */
+interface SoneBuilderFactory {
+
+ fun newSoneBuilder(): SoneBuilder
+
+}
--- /dev/null
+package net.pterodactylus.sone.database
+
+/**
+ * Combines a [SoneProvider] and a [SoneStore] into a Sone
+ * database.
+ */
+interface SoneDatabase : SoneProvider, SoneBuilderFactory, SoneStore
--- /dev/null
+/*
+ * Sone - SoneProvider.java - Copyright © 2011–2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.core.Core
+import net.pterodactylus.sone.data.Sone
+
+import com.google.common.base.Function
+import com.google.common.base.Optional
+import com.google.inject.ImplementedBy
+
+/**
+ * Interface for objects that can provide [Sone]s by their ID.
+ */
+@ImplementedBy(Core::class)
+interface SoneProvider {
+
+ val sones: Collection<Sone>
+ val localSones: Collection<Sone>
+ val remoteSones: Collection<Sone>
+ val soneLoader: (String) -> Sone?
+
+ fun getSone(soneId: String): Sone?
+
+}
--- /dev/null
+package net.pterodactylus.sone.database
+
+import net.pterodactylus.sone.data.Sone
+
+/**
+ * Interface for a store for [Sone]s.
+ */
+interface SoneStore {
+
+ fun storeSone(sone: Sone)
+ fun removeSone(sone: Sone)
+
+}
val text = data?.toString() ?: return listOf<Part>()
val soneParameter = parameters?.get("sone")
val sone = when (soneParameter) {
- is String -> core.getSone(soneParameter).orNull()
+ is String -> core.getSone(soneParameter)
is Sone -> soneParameter
else -> null
}
private fun render(writer: Writer, freemailPart: FreemailPart) {
val sone = core.getSone(freemailPart.identityId)
- val soneName = sone.transform(SoneAccessor::getNiceName).or(freemailPart.identityId)
+ val soneName = sone?.let(SoneAccessor::getNiceName) ?: freemailPart.identityId
renderLink(writer,
"/Freemail/NewMessage?to=${freemailPart.identityId}",
"${freemailPart.emailLocalPart}@$soneName.freemail",
* link is an SSK or USK link and the post was created by an identity that owns
* the keyspace in question.
*/
-data class FreenetLinkPart(val link: String, override val text: String, val title: String, val trusted: Boolean) : Part {
+data class FreenetLinkPart(val link: String, override val text: String, val title: String, val trusted: Boolean = false) : Part {
constructor(link: String, text: String, trusted: Boolean) : this(link, text, link, trusted)
* attributes: the link itself, the text that is shown instead of the link, and
* an explanatory text that can be displayed e.g. as a tooltip.
*/
-data class LinkPart(val link: String, override val text: String, val title: String) : Part {
-
- constructor(link: String, text: String) : this(link, text, link)
-
-}
+data class LinkPart @JvmOverloads constructor(val link: String, override val text: String, val title: String = link) : Part
--- /dev/null
+package net.pterodactylus.sone.text
+
+import freenet.keys.FreenetURI
+import freenet.support.Base64
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.data.impl.IdOnlySone
+import net.pterodactylus.sone.database.PostProvider
+import net.pterodactylus.sone.database.SoneProvider
+import net.pterodactylus.sone.text.LinkType.CHK
+import net.pterodactylus.sone.text.LinkType.FREEMAIL
+import net.pterodactylus.sone.text.LinkType.HTTP
+import net.pterodactylus.sone.text.LinkType.HTTPS
+import net.pterodactylus.sone.text.LinkType.KSK
+import net.pterodactylus.sone.text.LinkType.POST
+import net.pterodactylus.sone.text.LinkType.SONE
+import net.pterodactylus.sone.text.LinkType.SSK
+import net.pterodactylus.sone.text.LinkType.USK
+import net.pterodactylus.sone.utils.let
+import org.bitpedia.util.Base32
+import java.net.MalformedURLException
+
+/**
+ * [Parser] implementation that can recognize Freenet URIs.
+ */
+class SoneTextParser(private val soneProvider: SoneProvider?, private val postProvider: PostProvider?) {
+
+ fun parse(source: String, context: SoneTextParserContext?) =
+ source.split("\n")
+ .dropWhile { it.trim() == "" }
+ .dropLastWhile { it.trim() == "" }
+ .mergeMultipleEmptyLines()
+ .flatMap { splitLineIntoParts(it, context) }
+ .removeEmptyPlainTextParts()
+ .mergeAdjacentPlainTextParts()
+
+ private fun splitLineIntoParts(line: String, context: SoneTextParserContext?) =
+ generateSequence(PlainTextPart("") as Part to line) { remainder ->
+ if (remainder.second == "")
+ null
+ else
+ LinkType.values()
+ .mapNotNull { it.findNext(remainder.second) }
+ .minBy { it.position }
+ .let {
+ when {
+ it == null -> PlainTextPart(remainder.second) to ""
+ it.position == 0 -> it.toPart(context) to it.remainder
+ else -> PlainTextPart(remainder.second.substring(0, it.position)) to (it.link + it.remainder)
+ }
+ }
+ }.map { it.first }.toList()
+
+ private fun NextLink.toPart(context: SoneTextParserContext?) = when (linkType) {
+ KSK, CHK -> try {
+ FreenetURI(link).let { freenetUri ->
+ FreenetLinkPart(
+ link,
+ if (freenetUri.isKSK) {
+ freenetUri.guessableKey
+ } else {
+ freenetUri.metaString ?: freenetUri.docName ?: link.substring(0, 9)
+ },
+ link.split('?').first()
+ )
+ }
+ } catch (e: MalformedURLException) {
+ PlainTextPart(link)
+ }
+ SSK, USK ->
+ try {
+ FreenetLinkPart(link, FreenetURI(link).docName, trusted = context?.routingKey?.contentEquals(FreenetURI(link).routingKey) == true)
+ } catch (e: MalformedURLException) {
+ PlainTextPart(link)
+ }
+ SONE -> link.substring(7).let { SonePart(soneProvider?.getSone(it) ?: IdOnlySone(it)) }
+ POST -> postProvider?.getPost(link.substring(7))?.let { PostPart(it) } ?: PlainTextPart(link)
+ FREEMAIL -> link.indexOf('@').let { atSign ->
+ link.substring(atSign + 1, link.length - 9).let { freemailId ->
+ FreemailPart(link.substring(0, atSign), freemailId, freemailId.decodedId)
+ }
+ }
+ HTTP, HTTPS -> LinkPart(link, link
+ .withoutProtocol
+ .withoutWwwPrefix
+ .withoutUrlParameters
+ .withoutMiddlePathComponents
+ .withoutTrailingSlash)
+ }
+
+}
+
+private fun List<String>.mergeMultipleEmptyLines() = fold(emptyList<String>()) { previous, current ->
+ if (previous.isEmpty()) {
+ previous + current
+ } else {
+ if ((previous.last() == "\n") && (current == "")) {
+ previous
+ } else {
+ previous + ("\n" + current)
+ }
+ }
+}
+
+private fun List<Part>.mergeAdjacentPlainTextParts() = fold(emptyList<Part>()) { parts, part ->
+ if ((parts.lastOrNull() is PlainTextPart) && (part is PlainTextPart)) {
+ parts.dropLast(1) + PlainTextPart(parts.last().text + part.text)
+ } else {
+ parts + part
+ }
+}
+
+private fun List<Part>.removeEmptyPlainTextParts() = filterNot { it == PlainTextPart("") }
+
+private val String.decodedId: String get() = Base64.encode(Base32.decode(this))
+private val String.withoutProtocol get() = substring(indexOf("//") + 2)
+private val String.withoutUrlParameters get() = split('?').first()
+
+private val String.withoutWwwPrefix
+ get() = split("/")
+ .replaceFirst { it.split(".").dropWhile { it == "www" }.joinToString(".") }
+ .joinToString("/")
+
+private fun <T> List<T>.replaceFirst(replacement: (T) -> T) = mapIndexed { index, element ->
+ if (index == 0) replacement(element) else element
+}
+
+private val String.withoutMiddlePathComponents
+ get() = split("/").let {
+ if (it.size > 2) {
+ "${it.first()}/…/${it.last()}"
+ } else {
+ it.joinToString("/")
+ }
+ }
+private val String.withoutTrailingSlash get() = if (endsWith("/")) substring(0, length - 1) else this
+private val SoneTextParserContext.routingKey: ByteArray? get() = postingSone?.routingKey
+private val Sone.routingKey: ByteArray get() = Base64.decode(id)
+
+private enum class LinkType(private val scheme: String, private val freenetLink: Boolean) {
+
+ KSK("KSK@", true),
+ CHK("CHK@", true),
+ SSK("SSK@", true),
+ USK("USK@", true),
+ HTTP("http://", false),
+ HTTPS("https://", false),
+ SONE("sone://", false) {
+ override fun validateLinkLength(length: Int) = length.takeIf { it == 50 }
+ },
+ POST("post://", false),
+ FREEMAIL("", true) {
+ override fun findNext(line: String): NextLink? {
+ val nextFreemailSuffix = line.indexOf(".freemail").takeIf { it >= 54 } ?: return null
+ if (line[nextFreemailSuffix - 53] != '@') return null
+ if (!line.substring(nextFreemailSuffix - 52, nextFreemailSuffix).matches(Regex("^[a-z2-7]*\$"))) return null
+ val firstCharacterIndex = generateSequence(nextFreemailSuffix - 53) {
+ it.minus(1).takeIf { (it >= 0) && line[it].validLocalPart }
+ }.lastOrNull() ?: return null
+ return NextLink(firstCharacterIndex, this, line.substring(firstCharacterIndex, nextFreemailSuffix + 9), line.substring(nextFreemailSuffix + 9))
+ }
+
+ private val Char.validLocalPart get() = (this in ('A'..'Z')) || (this in ('a'..'z')) || (this in ('0'..'9')) || (this == '-') || (this == '_') || (this == '.')
+ };
+
+ open fun findNext(line: String): NextLink? {
+ val nextLinkPosition = line.indexOf(scheme).takeIf { it != -1 } ?: return null
+ val endOfLink = line.substring(nextLinkPosition).findEndOfLink().validate() ?: return null
+ val link = line.substring(nextLinkPosition, nextLinkPosition + endOfLink)
+ val realNextLinkPosition = if (freenetLink && line.substring(0, nextLinkPosition).endsWith("freenet:")) nextLinkPosition - 8 else nextLinkPosition
+ return NextLink(realNextLinkPosition, this, link, line.substring(nextLinkPosition + endOfLink))
+ }
+
+ private fun String.findEndOfLink() =
+ substring(0, whitespace.find(this)?.range?.start ?: length)
+ .dropLastWhile(::isPunctuation)
+ .upToFirstUnmatchedParen()
+
+ private fun Int.validate() = validateLinkLength(this)
+ protected open fun validateLinkLength(length: Int) = length.takeIf { it > scheme.length }
+
+ private fun String.upToFirstUnmatchedParen() =
+ foldIndexed(Pair<Int, Int?>(0, null)) { index, (openParens, firstUnmatchedParen), currentChar ->
+ when (currentChar) {
+ '(' -> (openParens + 1) to firstUnmatchedParen
+ ')' -> ((openParens - 1) to (if (openParens == 0) (firstUnmatchedParen ?: index) else firstUnmatchedParen))
+ else -> openParens to firstUnmatchedParen
+ }
+ }.second ?: length
+
+}
+
+private val punctuationChars = listOf('.', ',', '?', '!')
+private fun isPunctuation(char: Char) = char in punctuationChars
+
+private val whitespace = Regex("[\\u000a\u0020\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u2800\u3000]")
+
+private data class NextLink(val position: Int, val linkType: LinkType, val link: String, val remainder: String)
--- /dev/null
+package net.pterodactylus.sone.utils
+
+class Memoize<in T, out R>(private val calc: (T) -> R) : (T) -> R {
+
+ private val values = mutableMapOf<T, R>()
+
+ override fun invoke(value: T) =
+ values.getOrPut(value, { calc(value) })
+
+}
+
+fun <T, R> ((T) -> R).memoize(): (T) -> R = Memoize(this)
package net.pterodactylus.sone.web.ajax
-import net.pterodactylus.sone.utils.also
import net.pterodactylus.sone.utils.emptyToNull
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
override fun createJsonObject(request: FreenetRequest) =
request.parameters["post"].emptyToNull
- ?.let(core::getPost)
- ?.also(core::bookmarkPost)
- ?.let { createSuccessJsonObject() }
+ ?.let { postId ->
+ core.getPost(postId)?.also(core::bookmarkPost)
+ createSuccessJsonObject()
+ }
?: createErrorJsonObject("invalid-post-id")
}
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
+import net.pterodactylus.sone.utils.asOptional
import net.pterodactylus.sone.utils.emptyToNull
import net.pterodactylus.sone.utils.headers
import net.pterodactylus.sone.utils.let
request.parameters["text"].emptyToNull
?.let { TextFilter.filter(request.headers["Host"], it) }
?.let { text ->
- val sender = request.parameters["sender"].emptyToNull?.let(core::getSone)?.orNull() ?: currentSone
- val recipient = request.parameters["recipient"].let(core::getSone)
- core.createPost(sender, recipient, text).let { post ->
+ val sender = request.parameters["sender"].emptyToNull?.let(core::getSone) ?: currentSone
+ val recipient = request.parameters["recipient"]?.let(core::getSone)
+ core.createPost(sender, recipient.asOptional(), text).let { post ->
createSuccessJsonObject().apply {
put("postId", post.id)
put("sone", sender.id)
- put("recipient", recipient.let(Sone::getId))
+ put("recipient", recipient?.id)
}
}
} ?: createErrorJsonObject("text-required")
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["post"]
- .let(core::getPost)
+ ?.let(core::getPost)
?.let { post ->
post.sone.isLocal.ifTrue {
createSuccessJsonObject().also {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["reply"]
- .let(core::getPostReply)
+ ?.let(core::getPostReply)
?.let { reply ->
reply.sone.isLocal.ifTrue {
createSuccessJsonObject().also {
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
- .let(core::getSone)
+ ?.let(core::getSone)
?.let { sone ->
createSuccessJsonObject()
.put("trustValue", core.preferences.negativeTrust)
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
- .let(core::getSone)
+ ?.let(core::getSone)
?.also { core.followSone(currentSone, it.id) }
?.also(core::markSoneKnown)
?.let { createSuccessJsonObject() }
override fun createJsonObject(request: FreenetRequest) =
when (request.parameters["type"]) {
"post" -> request.parameters["post"]
- .let(core::getPost)
+ ?.let(core::getPost)
?.let(core::getLikes)
?.toReply()
?: createErrorJsonObject("invalid-post-id")
"reply" -> request.parameters["reply"]
- .let(core::getPostReply)
+ ?.let(core::getPostReply)
?.let(core::getLikes)
?.toReply()
?: createErrorJsonObject("invalid-reply-id")
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["post"]
- .let(core::getPost)
- .let { post ->
+ ?.let(core::getPost)
+ ?.let { post ->
createSuccessJsonObject().
put("post", jsonObject(
"id" to post.id,
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["reply"]
- .let(core::getPostReply)
+ ?.let(core::getPostReply)
?.let { it.toJson(currentSone, request) }
?.let { replyJson ->
createSuccessJsonObject().apply {
this["loggedIn"] = currentSone != null
this["options"] = currentSone?.options?.toJsonOptions() ?: jsonObject {}
this["notificationHash"] = webInterface.getNotifications(currentSone).sortedBy { it.createdTime }.hashCode()
- this["sones"] = request.httpRequest.getParam("soneIds").split(',').mapPresent(core::getSone).plus(currentSone).filterNotNull().toJsonSones()
+ this["sones"] = request.httpRequest.getParam("soneIds").split(',').mapNotNull(core::getSone).plus(currentSone).filterNotNull().toJsonSones()
this["newPosts"] = webInterface.getNewPosts(currentSone).toJsonPosts()
this["newReplies"] = webInterface.getNewReplies(currentSone).toJsonReplies()
this["linkedElements"] = request.httpRequest.getParam("elements", "[]").asJson().map(JsonNode::asText).map(elementLoader::loadElement).toJsonElements()
import net.pterodactylus.sone.freenet.L10nFilter
import net.pterodactylus.sone.text.TimeTextConverter
import net.pterodactylus.sone.utils.jsonObject
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
when (request.parameters["type"]) {
"post" -> request.parameters["post"]
- .let(core::getPost)
+ ?.let(core::getPost)
?.let { currentSone.addLikedPostId(it.id) }
?.also { core.touchConfiguration() }
?.let { createSuccessJsonObject() }
?: createErrorJsonObject("invalid-post-id")
"reply" -> request.parameters["reply"]
- .let(core::getPostReply)
+ ?.let(core::getPostReply)
?.let { currentSone.addLikedReplyId(it.id) }
?.also { core.touchConfiguration() }
?.let { createSuccessJsonObject() }
package net.pterodactylus.sone.web.ajax
-import com.google.common.base.Optional
-import net.pterodactylus.sone.utils.mapPresent
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
else -> createErrorJsonObject("invalid-type")
}
- private fun <T> processIds(request: FreenetRequest, getter: (String) -> Optional<T>, marker: (T) -> Unit) =
+ private fun <T : Any> processIds(request: FreenetRequest, getter: (String) -> T?, marker: (T) -> Unit) =
request.parameters["id"]
?.split(Regex(" +"))
- ?.mapPresent(getter)
+ ?.mapNotNull(getter)
?.onEach(marker)
.let { createSuccessJsonObject() }
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
- .let(core::getSone)
+ ?.let(core::getSone)
?.let { core.trustSone(currentSone, it) }
?.let { createSuccessJsonObject().put("trustValue", core.preferences.positiveTrust) }
?: createErrorJsonObject("invalid-sone-id")
override fun createJsonObject(currentSone: Sone, request: FreenetRequest) =
request.parameters["sone"]
- ?.takeIf { core.getSone(it).isPresent }
- ?.also { core.unfollowSone(currentSone, it) }
+ ?.let(core::getSone)
+ ?.also { core.unfollowSone(currentSone, it.id) }
?.let { createSuccessJsonObject() }
?: createErrorJsonObject("invalid-sone-id")
if (freenetRequest.isPOST) {
val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
val postId = freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36)
- webInterface.core.getPost(postId).orNull()?.let {
+ webInterface.core.getPost(postId)?.let {
webInterface.core.bookmarkPost(it)
}
throw RedirectException(returnPage)
package net.pterodactylus.sone.web.pages
import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
* Page that lets the user create a new album.
*/
class CreateAlbumPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("createAlbum.html", template, "Page.CreateAlbum.Title", webInterface, true) {
+ LoggedInPage("createAlbum.html", template, "Page.CreateAlbum.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val name = freenetRequest.httpRequest.getPartAsStringFailsafe("name", 64).trim()
if (name.isEmpty()) {
return
}
val description = freenetRequest.httpRequest.getPartAsStringFailsafe("description", 256).trim()
- val currentSone = webInterface.getCurrentSoneCreatingSession(freenetRequest.toadletContext)
val parentId = freenetRequest.httpRequest.getPartAsStringFailsafe("parent", 36)
val parent = if (parentId == "") currentSone.rootAlbum else webInterface.core.getAlbum(parentId)
val album = webInterface.core.createAlbum(currentSone, parent)
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
+import net.pterodactylus.sone.utils.asOptional
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* This page lets the user create a new [Post].
*/
class CreatePostPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("createPost.html", template, "Page.CreatePost.Title", webInterface, true) {
+ LoggedInPage("createPost.html", template, "Page.CreatePost.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
templateContext["returnPage"] = returnPage
if (freenetRequest.isPOST) {
templateContext["errorTextEmpty"] = true
return
}
- val sender = webInterface.core.getLocalSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: getCurrentSone(freenetRequest.toadletContext)
+ val sender = webInterface.core.getLocalSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone
val recipient = webInterface.core.getSone(freenetRequest.httpRequest.getPartAsStringFailsafe("recipient", 43))
- webInterface.core.createPost(sender, recipient, TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), text))
+ webInterface.core.createPost(sender, recipient.asOptional(), TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), text))
throw RedirectException(returnPage)
}
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
* This page lets the user post a reply to a post.
*/
class CreateReplyPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("createReply.html", template, "Page.CreateReply.Title", webInterface, true) {
+ LoggedInPage("createReply.html", template, "Page.CreateReply.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
val postId = freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36).apply { templateContext["postId"] = this }
val text = freenetRequest.httpRequest.getPartAsStringFailsafe("text", 65536).trim().apply { templateContext["text"] = this }
val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256).apply { templateContext["returnPage"] = this }
templateContext["errorTextEmpty"] = true
return
}
- val post = webInterface.core.getPost(postId).orNull() ?: throw RedirectException("noPermission.html")
- val sender = webInterface.core.getLocalSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: getCurrentSone(freenetRequest.toadletContext)
+ val post = webInterface.core.getPost(postId) ?: throw RedirectException("noPermission.html")
+ val sender = webInterface.core.getLocalSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone
webInterface.core.createReply(sender, post, TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), text))
throw RedirectException(returnPage)
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* Page that lets the user delete an {@link Album}.
*/
class DeleteAlbumPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("deleteAlbum.html", template, "Page.DeleteAlbum.Title", webInterface, true) {
+ LoggedInPage("deleteAlbum.html", template, "Page.DeleteAlbum.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val album = webInterface.core.getAlbum(freenetRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html")
if (!album.sone.isLocal) {
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* Page that lets the user delete an {@link Image}.
*/
class DeleteImagePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("deleteImage.html", template, "Page.DeleteImage.Title", webInterface, true) {
+ LoggedInPage("deleteImage.html", template, "Page.DeleteImage.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val image = webInterface.core.getImage(freenetRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html")
if (!image.sone.isLocal) {
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* Lets the user delete a post they made.
*/
class DeletePostPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("deletePost.html", template, "Page.DeletePost.Title", webInterface, true) {
+ LoggedInPage("deletePost.html", template, "Page.DeletePost.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
- val post = webInterface.core.getPost(freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36)).orNull() ?: throw RedirectException("noPermission.html")
+ val post = webInterface.core.getPost(freenetRequest.httpRequest.getPartAsStringFailsafe("post", 36)) ?: throw RedirectException("noPermission.html")
val returnPage = freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)
if (!post.sone.isLocal) {
throw RedirectException("noPermission.html")
templateContext["returnPage"] = returnPage
return
}
- templateContext["post"] = webInterface.core.getPost(freenetRequest.httpRequest.getParam("post")).orNull() ?: throw RedirectException("noPermission.html")
+ templateContext["post"] = webInterface.core.getPost(freenetRequest.httpRequest.getParam("post")) ?: throw RedirectException("noPermission.html")
templateContext["returnPage"] = freenetRequest.httpRequest.getParam("returnPage")
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* Page that lets the user confirm the deletion of a profile field.
*/
class DeleteProfileFieldPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("deleteProfileField.html", template, "Page.DeleteProfileField.Title", webInterface, true) {
+ LoggedInPage("deleteProfileField.html", template, "Page.DeleteProfileField.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- val currentSone = getCurrentSone(freenetRequest.toadletContext)!!
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val field = currentSone.profile.getFieldById(freenetRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
if (freenetRequest.httpRequest.getPartAsStringFailsafe("confirm", 4) == "true") {
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* This page lets the user delete a reply.
*/
class DeleteReplyPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("deleteReply.html", template, "Page.DeleteReply.Title", webInterface, true) {
+ LoggedInPage("deleteReply.html", template, "Page.DeleteReply.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val replyId = freenetRequest.httpRequest.getPartAsStringFailsafe("reply", 36)
- val reply = webInterface.core.getPostReply(replyId).orNull() ?: throw RedirectException("noPermission.html")
+ val reply = webInterface.core.getPostReply(replyId) ?: throw RedirectException("noPermission.html")
if (!reply.sone.isLocal) {
throw RedirectException("noPermission.html")
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* installation.
*/
class DeleteSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("deleteSone.html", template, "Page.DeleteSone.Title", webInterface, true) {
+ LoggedInPage("deleteSone.html", template, "Page.DeleteSone.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
if (freenetRequest.httpRequest.isPartSet("deleteSone")) {
- webInterface.core.deleteSone(getCurrentSone(freenetRequest.toadletContext))
+ webInterface.core.deleteSone(currentSone)
}
throw RedirectException("index.html")
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* @see net.pterodactylus.sone.core.Core#distrustSone(Sone, Sone)
*/
class DistrustPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("distrust.html", template, "Page.Distrust.Title", webInterface, true) {
+ LoggedInPage("distrust.html", template, "Page.Distrust.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
- val sone = webInterface.core.getSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sone", 44)).orNull()
- sone?.run { webInterface.core.distrustSone(getCurrentSone(freenetRequest.toadletContext), this) }
+ webInterface.core.getSone(freenetRequest.httpRequest.getPartAsStringFailsafe("sone", 44))
+ ?.run { webInterface.core.distrustSone(currentSone, this) }
throw RedirectException(freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256))
}
}
package net.pterodactylus.sone.web.pages
import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* Page that lets the user edit the name and description of an album.
*/
class EditAlbumPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("editAlbum.html", template, "Page.EditAlbum.Title", webInterface, true) {
+ LoggedInPage("editAlbum.html", template, "Page.EditAlbum.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val album = webInterface.core.getAlbum(freenetRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html")
album.takeUnless { it.sone.isLocal }?.run { throw RedirectException("noPermission.html") }
package net.pterodactylus.sone.web.pages
import net.pterodactylus.sone.data.Image.Modifier.ImageTitleMustNotBeEmpty
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
* Page that lets the user edit title and description of an {@link Image}.
*/
class EditImagePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("editImage.html", template, "Page.EditImage.Title", webInterface, true) {
+ LoggedInPage("editImage.html", template, "Page.EditImage.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val image = webInterface.core.getImage(freenetRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html")
if (!image.sone.isLocal) {
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
/**
* Page that lets the user edit the name of a profile field.
*/
-class EditProfileFieldPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("editProfileField.html", template, "Page.EditProfileField.Title", webInterface, true) {
+class EditProfileFieldPage(template: Template, webInterface: WebInterface) :
+ LoggedInPage("editProfileField.html", template, "Page.EditProfileField.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- sessionProvider.getCurrentSone(freenetRequest.toadletContext)!!.let { currentSone ->
- currentSone.profile.let { profile ->
- if (freenetRequest.isPOST) {
- if (freenetRequest.httpRequest.getPartAsStringFailsafe("cancel", 4) == "true") {
- throw RedirectException("editProfile.html#profile-fields")
- }
- val field = profile.getFieldById(freenetRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
- freenetRequest.httpRequest.getPartAsStringFailsafe("name", 256).let { name ->
- try {
- if (name != field.name) {
- field.name = name
- currentSone.profile = profile
- }
- throw RedirectException("editProfile.html#profile-fields")
- } catch (e: IllegalArgumentException) {
- templateContext["duplicateFieldName"] = true
- return
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
+ currentSone.profile.let { profile ->
+ if (freenetRequest.isPOST) {
+ if (freenetRequest.httpRequest.getPartAsStringFailsafe("cancel", 4) == "true") {
+ throw RedirectException("editProfile.html#profile-fields")
+ }
+ val field = profile.getFieldById(freenetRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html")
+ freenetRequest.httpRequest.getPartAsStringFailsafe("name", 256).let { name ->
+ try {
+ if (name != field.name) {
+ field.name = name
+ currentSone.profile = profile
}
+ throw RedirectException("editProfile.html#profile-fields")
+ } catch (e: IllegalArgumentException) {
+ templateContext["duplicateFieldName"] = true
+ return
}
}
- templateContext["field"] = profile.getFieldById(freenetRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html")
}
+ templateContext["field"] = profile.getFieldById(freenetRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html")
}
}
package net.pterodactylus.sone.web.pages
import net.pterodactylus.sone.data.Profile.DuplicateField
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
/**
* This page lets the user edit her profile.
*/
-class EditProfilePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("editProfile.html", template, "Page.EditProfile.Title", webInterface, true) {
+class EditProfilePage(template: Template, webInterface: WebInterface) :
+ LoggedInPage("editProfile.html", template, "Page.EditProfile.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- freenetRequest.currentSone!!.profile.let { profile ->
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
+ currentSone.profile.let { profile ->
templateContext["firstName"] = profile.firstName
templateContext["middleName"] = profile.middleName
templateContext["lastName"] = profile.lastName
profile.fields.forEach { field ->
field.value = TextFilter.filter(freenetRequest.httpRequest.getHeader("Host"), freenetRequest.httpRequest.getPartAsStringFailsafe("field-${field.id}", 400).trim())
}
+ currentSone.profile = profile
webInterface.core.touchConfiguration()
throw RedirectException("editProfile.html")
} else if (freenetRequest.httpRequest.getPartAsStringFailsafe("add-field", 4) == "true") {
val fieldName = freenetRequest.httpRequest.getPartAsStringFailsafe("field-name", 100)
try {
profile.addField(fieldName)
- freenetRequest.currentSone!!.profile = profile
+ currentSone.profile = profile
webInterface.core.touchConfiguration()
throw RedirectException("editProfile.html#profile-fields")
} catch (e: DuplicateField) {
throw RedirectException("editProfileField.html?field=${field.id}")
} else if (freenetRequest.httpRequest.getPartAsStringFailsafe("move-down-field-${field.id}", 4) == "true") {
profile.moveFieldDown(field)
- freenetRequest.currentSone!!.profile = profile
+ currentSone.profile = profile
throw RedirectException("editProfile.html#profile-fields")
} else if (freenetRequest.httpRequest.getPartAsStringFailsafe("move-up-field-${field.id}", 4) == "true") {
profile.moveFieldUp(field)
- freenetRequest.currentSone!!.profile = profile
+ currentSone.profile = profile
throw RedirectException("editProfile.html#profile-fields")
}
}
}
}
- private val FreenetRequest.currentSone get() = sessionProvider.getCurrentSone(toadletContext)
-
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* This page lets the user follow another Sone.
*/
class FollowSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("followSone.html", template, "Page.FollowSone.Title", webInterface, true) {
+ LoggedInPage("followSone.html", template, "Page.FollowSone.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
freenetRequest.httpRequest.getPartAsStringFailsafe("sone", 1200).split(Regex("[ ,]+"))
.map { it to webInterface.core.getSone(it) }
- .filter { it.second.isPresent }
- .map { it.first to it.second.get() }
+ .filterNot { it.second == null }
.forEach { sone ->
- webInterface.core.followSone(freenetRequest.currentSone, sone.first)
+ webInterface.core.followSone(currentSone, sone.first)
webInterface.core.markSoneKnown(sone.second)
}
throw RedirectException(freenetRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256))
}
}
- private val FreenetRequest.currentSone get() = sessionProvider.getCurrentSone(toadletContext)
-
}
* The image browser page is the entry page for the image management.
*/
class ImageBrowserPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("imageBrowser.html", template, "Page.ImageBrowser.Title", webInterface, true) {
+ LoggedInPage("imageBrowser.html", template, "Page.ImageBrowser.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if ("album" in freenetRequest.parameters) {
templateContext["albumRequested"] = true
templateContext["album"] = webInterface.core.getAlbum(freenetRequest.parameters["album"]!!)
}
} else {
templateContext["soneRequested"] = true
- templateContext["sone"] = webInterface.core.getSone(freenetRequest.httpRequest.getParam("sone")).orNull() ?: getCurrentSone(freenetRequest.toadletContext)
+ templateContext["sone"] = webInterface.core.getSone(freenetRequest.httpRequest.getParam("sone")) ?: currentSone
}
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.notify.PostVisibilityFilter
import net.pterodactylus.sone.utils.Pagination
import net.pterodactylus.sone.utils.parameters
* of all friends of the current user.
*/
class IndexPage(template: Template, webInterface: WebInterface, private val postVisibilityFilter: PostVisibilityFilter):
- SoneTemplatePage("index.html", template, "Page.Index.Title", webInterface, true) {
+ LoggedInPage("index.html", template, "Page.Index.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- getCurrentSone(freenetRequest.toadletContext)!!.let { currentSone ->
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
(currentSone.posts +
currentSone.friends
- .map { webInterface.core.getSone(it) }
- .filter { it.isPresent }
- .map { it.get() }
+ .mapNotNull(webInterface.core::getSone)
.flatMap { it.posts } +
webInterface.core.getDirectedPosts(currentSone.id)
).distinct()
templateContext["posts"] = pagination.items
}
}
- }
}
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
/**
* Page that lets the user like [net.pterodactylus.sone.data.Post]s and [net.pterodactylus.sone.data.Reply]s.
*/
-class LikePage(template: Template, webInterface: WebInterface)
- : SoneTemplatePage("like.html", template, "Page.Like.Title", webInterface, true) {
+class LikePage(template: Template, webInterface: WebInterface) :
+ LoggedInPage("like.html", template, "Page.Like.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
- getCurrentSone(freenetRequest.toadletContext)!!.let { currentSone ->
- freenetRequest.parameters["type", 16]?.also { type ->
- when(type) {
- "post" -> currentSone.addLikedPostId(freenetRequest.parameters["post", 36]!!)
- "reply" -> currentSone.addLikedReplyId(freenetRequest.parameters["reply", 36]!!)
- }
+ freenetRequest.parameters["type", 16]?.also { type ->
+ when (type) {
+ "post" -> currentSone.addLikedPostId(freenetRequest.parameters["post", 36]!!)
+ "reply" -> currentSone.addLikedReplyId(freenetRequest.parameters["reply", 36]!!)
}
- throw RedirectException(freenetRequest.parameters["returnPage", 256]!!)
}
+ throw RedirectException(freenetRequest.parameters["returnPage", 256]!!)
}
}
--- /dev/null
+package net.pterodactylus.sone.web.pages
+
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.web.WebInterface
+import net.pterodactylus.sone.web.page.FreenetRequest
+import net.pterodactylus.util.template.Template
+import net.pterodactylus.util.template.TemplateContext
+
+/**
+ * Base class for [SoneTemplatePage] implementations that require a logged in user.
+ */
+abstract class LoggedInPage(path: String, template: Template, pageTitleKey: String, webInterface: WebInterface) :
+ SoneTemplatePage(path, template, pageTitleKey, webInterface, true) {
+
+ final override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ handleRequest(freenetRequest, getCurrentSone(freenetRequest.toadletContext, false)!!, templateContext)
+ }
+
+ protected abstract fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext)
+
+}
package net.pterodactylus.sone.web.pages
import freenet.clients.http.ToadletContext
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
import net.pterodactylus.util.template.Template
* Logs a user out.
*/
class LogoutPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("logout.html", template, "Page.Logout.Title", webInterface, true) {
+ LoggedInPage("logout.html", template, "Page.Logout.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
setCurrentSone(freenetRequest.toadletContext, null)
throw RedirectException("index.html")
}
override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
val ids = freenetRequest.parameters["id", 65536]!!.split(" ")
when (freenetRequest.parameters["type", 5]) {
- "sone" -> ids.mapPresent(webInterface.core::getSone).forEach(webInterface.core::markSoneKnown)
- "post" -> ids.mapPresent(webInterface.core::getPost).forEach(webInterface.core::markPostKnown)
- "reply" -> ids.mapPresent(webInterface.core::getPostReply).forEach(webInterface.core::markReplyKnown)
+ "sone" -> ids.mapNotNull(webInterface.core::getSone).forEach(webInterface.core::markSoneKnown)
+ "post" -> ids.mapNotNull(webInterface.core::getPost).forEach(webInterface.core::markPostKnown)
+ "reply" -> ids.mapNotNull(webInterface.core::getPostReply).forEach(webInterface.core::markReplyKnown)
else -> throw RedirectException("invalid.html")
}
throw RedirectException(freenetRequest.parameters["returnPage", 256]!!)
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
* Page that lets the user control the rescue mode for a Sone.
*/
class RescuePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("rescue.html", template, "Page.Rescue.Title", webInterface, true) {
+ LoggedInPage("rescue.html", template, "Page.Rescue.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- val soneRescuer = webInterface.core.getSoneRescuer(getCurrentSone(freenetRequest.toadletContext)!!)
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
+ val soneRescuer = webInterface.core.getSoneRescuer(currentSone)
templateContext["soneRescuer"] = soneRescuer
if (freenetRequest.isPOST) {
freenetRequest.parameters["edition", 9]?.toIntOrNull()?.also {
import com.google.common.base.Ticker
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
+import freenet.support.Logger
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.Pagination
import net.pterodactylus.sone.utils.emptyToNull
+import net.pterodactylus.sone.utils.memoize
import net.pterodactylus.sone.utils.paginate
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
private val cache: Cache<Iterable<Phrase>, Pagination<Post>> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5, MINUTES).build()
override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ val startTime = System.currentTimeMillis()
val phrases = try {
freenetRequest.parameters["query"].emptyToNull?.parse()
} catch (te: TextException) {
0 -> redirect("index.html")
1 -> phrases.first().phrase.also { word ->
when {
- word.removePrefix("sone://").let(webInterface.core::getSone).isPresent -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}")
- word.removePrefix("post://").let(webInterface.core::getPost).isPresent -> redirect("viewPost.html?post=${word.removePrefix("post://")}")
- word.removePrefix("reply://").let(webInterface.core::getPostReply).isPresent -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(webInterface.core::getPostReply).get().postId}")
+ word.removePrefix("sone://").let(webInterface.core::getSone) != null -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}")
+ word.removePrefix("post://").let(webInterface.core::getPost) != null -> redirect("viewPost.html?post=${word.removePrefix("post://")}")
+ word.removePrefix("reply://").let(webInterface.core::getPostReply) != null -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(webInterface.core::getPostReply)?.postId}")
word.removePrefix("album://").let(webInterface.core::getAlbum) != null -> redirect("imageBrowser.html?album=${word.removePrefix("album://")}")
word.removePrefix("image://").let { webInterface.core.getImage(it, false) } != null -> redirect("imageBrowser.html?image=${word.removePrefix("image://")}")
}
}
}
+ val soneNameCache = { sone: Sone -> sone.names() }.memoize()
val sonePagination = webInterface.core.sones
- .scoreAndPaginate(phrases) { it.allText() }
+ .scoreAndPaginate(phrases) { it.allText(soneNameCache) }
.apply { page = freenetRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 }
val postPagination = cache.get(phrases) {
webInterface.core.sones
.flatMap(Sone::getPosts)
.filter { Post.FUTURE_POSTS_FILTER.apply(it) }
- .scoreAndPaginate(phrases) { it.allText() }
+ .scoreAndPaginate(phrases) { it.allText(soneNameCache) }
}.apply { page = freenetRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 }
+ Logger.normal(SearchPage::class.java, "Finished search for “${freenetRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.")
templateContext["sonePagination"] = sonePagination
templateContext["soneHits"] = sonePagination.items
templateContext["postPagination"] = postPagination
.paginate(webInterface.core.preferences.postsPerPage)
private fun Sone.names() =
- listOf(name, profile.firstName, profile.middleName, profile.lastName)
- .filterNotNull()
- .joinToString("")
+ with(profile) {
+ listOf(name, firstName, middleName, lastName)
+ .filterNotNull()
+ .joinToString("")
+ }
- private fun Sone.allText() =
- (names() + profile.fields.map { "${it.name} ${it.value}" }.joinToString(" ", " ")).toLowerCase()
+ private fun Sone.allText(soneNameCache: (Sone) -> String) =
+ (soneNameCache(this) + profile.fields.map { "${it.name} ${it.value}" }.joinToString(" ", " ")).toLowerCase()
- private fun Post.allText() =
- (text + recipient.orNull()?.let { " ${it.names()}" } + webInterface.core.getReplies(id)
+ private fun Post.allText(soneNameCache: (Sone) -> String) =
+ (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + webInterface.core.getReplies(id)
.filter { PostReply.FUTURE_REPLY_FILTER.apply(it) }
- .map { "${it.sone.names()} ${it.text}" }.joinToString(" ", " ")).toLowerCase()
+ .map { "${soneNameCache(it.sone)} ${it.text}" }.joinToString(" ", " ")).toLowerCase()
private fun score(text: String, phrases: Iterable<Phrase>): Double {
val requiredPhrases = phrases.count { it.required }
return requiredHits * 3 + optionalHits + (requiredHits - requiredPhrases) * 5 - (forbiddenHits * 2)
}
- private fun String.findAll(needle: String): List<Int> {
- var nextIndex = indexOf(needle)
- val positions = mutableListOf<Int>()
- while (nextIndex != -1) {
- positions += nextIndex
- nextIndex = indexOf(needle, nextIndex + 1)
- }
- return positions
- }
+ private fun String.findAll(needle: String) =
+ generateSequence(indexOf(needle).takeIf { it > -1 }) { lastPosition ->
+ lastPosition
+ .let { indexOf(needle, it + 1) }
+ .takeIf { it > -1 }
+ }.toList()
private fun String.parse() =
StringEscaper.parseLine(this)
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
import net.pterodactylus.sone.web.page.FreenetRequest
* Page that lets the user trust another Sone. This will assign a configurable
* amount of trust to an identity.
*/
-class TrustPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("trust.html", template, "Page.Trust.Title", webInterface, true) {
+class TrustPage(template: Template, webInterface: WebInterface) :
+ LoggedInPage("trust.html", template, "Page.Trust.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
- getCurrentSone(freenetRequest.toadletContext)?.also { currentSone ->
- webInterface.core.getSone(freenetRequest.parameters["sone"]).let { sone ->
- webInterface.core.trustSone(currentSone, sone)
- }
+ webInterface.core.getSone(freenetRequest.parameters["sone"]!!)?.let { sone ->
+ webInterface.core.trustSone(currentSone, sone)
}
throw RedirectException(freenetRequest.parameters["returnPage", 256])
}
}
freenetRequest.isPOST -> {
freenetRequest.parameters["post", 36]
- .let(webInterface.core::getPost)
- .also(webInterface.core::unbookmarkPost)
+ ?.let(webInterface.core::getPost)
+ ?.also(webInterface.core::unbookmarkPost)
throw RedirectException(freenetRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
/**
* This page lets the user unfollow another Sone.
*/
-class UnfollowSonePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("unfollowSone.html", template, "Page.UnfollowSone.Title", webInterface, true) {
+class UnfollowSonePage(template: Template, webInterface: WebInterface) :
+ LoggedInPage("unfollowSone.html", template, "Page.UnfollowSone.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
- getCurrentSone(freenetRequest.toadletContext)!!.also { currentSone ->
- freenetRequest.parameters["sone"]!!.split(Regex("[ ,]+"))
- .forEach { webInterface.core.unfollowSone(currentSone, it) }
- }
+ freenetRequest.parameters["sone"]!!.split(Regex("[ ,]+"))
+ .forEach { webInterface.core.unfollowSone(currentSone, it) }
throw RedirectException(freenetRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
* Page that lets the user unlike a [net.pterodactylus.sone.data.Post] or [net.pterodactylus.sone.data.Reply].
*/
class UnlikePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("unlike.html", template, "Page.Unlike.Title", webInterface, true) {
+ LoggedInPage("unlike.html", template, "Page.Unlike.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
when (freenetRequest.parameters["type"]) {
- "post" -> getCurrentSone(freenetRequest.toadletContext)!!.removeLikedPostId(freenetRequest.parameters["post"]!!)
- "reply" -> getCurrentSone(freenetRequest.toadletContext)!!.removeLikedReplyId(freenetRequest.parameters["reply"]!!)
+ "post" -> currentSone.removeLikedPostId(freenetRequest.parameters["post"]!!)
+ "reply" -> currentSone.removeLikedReplyId(freenetRequest.parameters["reply"]!!)
}
throw RedirectException(freenetRequest.parameters["returnPage", 256])
}
package net.pterodactylus.sone.web.pages
-import net.pterodactylus.sone.utils.also
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.utils.isPOST
import net.pterodactylus.sone.utils.parameters
import net.pterodactylus.sone.web.WebInterface
* Page that lets the user untrust another Sone. This will remove all trust
* assignments for an identity.
*/
-class UntrustPage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("untrust.html", template, "Page.Untrust.Title", webInterface, true) {
+class UntrustPage(template: Template, webInterface: WebInterface) :
+ LoggedInPage("untrust.html", template, "Page.Untrust.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
- getCurrentSone(freenetRequest.toadletContext)!!.also { currentSone ->
- freenetRequest.parameters["sone", 44]
- .let(webInterface.core::getSone)
- .also { webInterface.core.untrustSone(currentSone, it) }
- }
+ freenetRequest.parameters["sone", 44]!!
+ .let(webInterface.core::getSone)
+ ?.also { webInterface.core.untrustSone(currentSone, it) }
throw RedirectException(freenetRequest.parameters["returnPage", 256])
}
}
package net.pterodactylus.sone.web.pages
import freenet.support.api.Bucket
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.text.TextFilter
import net.pterodactylus.sone.utils.emptyToNull
import net.pterodactylus.sone.utils.headers
* Page implementation that lets the user upload an image.
*/
class UploadImagePage(template: Template, webInterface: WebInterface):
- SoneTemplatePage("uploadImage.html", template, "Page.UploadImage.Title", webInterface, true) {
+ LoggedInPage("uploadImage.html", template, "Page.UploadImage.Title", webInterface) {
- override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+ override fun handleRequest(freenetRequest: FreenetRequest, currentSone: Sone, templateContext: TemplateContext) {
if (freenetRequest.isPOST) {
val parentAlbum = freenetRequest.parameters["parent"]!!.let(webInterface.core::getAlbum) ?: throw RedirectException("noPermission.html")
- if (parentAlbum.sone != getCurrentSone(freenetRequest.toadletContext)) {
+ if (parentAlbum.sone != currentSone) {
throw RedirectException("noPermission.html")
}
val title = freenetRequest.parameters["title", 200].emptyToNull ?: throw RedirectException("emptyImageTitle.html")
}
val temporaryImage = webInterface.core.createTemporaryImage(bytes.mimeType, bytes)
- webInterface.core.createImage(getCurrentSone(freenetRequest.toadletContext), parentAlbum, temporaryImage).modify().apply {
+ webInterface.core.createImage(currentSone, parentAlbum, temporaryImage).modify().apply {
setWidth(bufferedImage.width)
setHeight(bufferedImage.height)
setTitle(title)
SoneTemplatePage("viewPost.html", template, "Page.ViewPost.Title", webInterface, false) {
override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
- templateContext["post"] = freenetRequest.parameters["post"].let(webInterface.core::getPost).orNull()
+ templateContext["post"] = freenetRequest.parameters["post"]?.let(webInterface.core::getPost)
templateContext["raw"] = freenetRequest.parameters["raw"] == "true"
}
override fun isLinkExcepted(link: URI?) = true
public override fun getPageTitle(freenetRequest: FreenetRequest) =
- (freenetRequest.parameters["post"].let(webInterface.core::getPost).let {
+ (freenetRequest.parameters["post"]?.let(webInterface.core::getPost)?.let {
if (it.text.length > 20) {
it.text.substring(0..19) + "…"
} else {
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.template.SoneAccessor
-import net.pterodactylus.sone.utils.let
import net.pterodactylus.sone.utils.mapPresent
import net.pterodactylus.sone.utils.paginate
import net.pterodactylus.sone.utils.parameters
override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
templateContext["soneId"] = freenetRequest.parameters["sone"]
- freenetRequest.parameters["sone"].let(webInterface.core::getSone).let { sone ->
+ freenetRequest.parameters["sone"]!!.let(webInterface.core::getSone)?.let { sone ->
templateContext["sone"] = sone
val sonePosts = sone.posts
val directedPosts = webInterface.core.getDirectedPosts(sone.id)
override fun isLinkExcepted(link: URI?) = true
public override fun getPageTitle(freenetRequest: FreenetRequest): String =
- freenetRequest.parameters["sone"].let(webInterface.core::getSone).let { sone ->
+ freenetRequest.parameters["sone"]!!.let(webInterface.core::getSone)?.let { sone ->
"${SoneAccessor.getNiceName(sone)} - ${webInterface.l10n.getString("Page.ViewSone.Title")}"
} ?: webInterface.l10n.getString("Page.ViewSone.Page.TitleWithoutSone")
import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent;
import net.pterodactylus.util.config.Configuration;
-import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.EventBus;
Identity identity = mock(Identity.class);
when(identity.getId()).thenReturn("sone-id");
Sone sone = mock(Sone.class);
- when(database.getSone("sone-id")).thenReturn(Optional.of(sone));
+ when(database.getSone("sone-id")).thenReturn(sone);
PostReply postReply1 = mock(PostReply.class);
PostReply postReply2 = mock(PostReply.class);
when(sone.getReplies()).thenReturn(ImmutableSet.of(postReply1, postReply2));
package net.pterodactylus.sone.core;
-import static com.google.common.base.Optional.of;
import static com.google.common.io.ByteStreams.toByteArray;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
import static java.lang.System.currentTimeMillis;
public void setupCore() {
UpdateChecker updateChecker = mock(UpdateChecker.class);
when(core.getUpdateChecker()).thenReturn(updateChecker);
- when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
+ when(core.getSone(anyString())).thenReturn(null);
}
@Test
when(sone.getInsertUri()).thenReturn(insertUri);
when(sone.getFingerprint()).thenReturn(fingerprint);
when(sone.getRootAlbum()).thenReturn(mock(Album.class));
- when(core.getSone(anyString())).thenReturn(of(sone));
+ when(core.getSone(anyString())).thenReturn(sone);
return sone;
}
new SoneInserter(core, eventBus, freenetInterface, "SoneId",
soneModificationDetector, 1);
when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
- when(core.getSone("SoneId")).thenReturn(Optional.<Sone>absent());
+ when(core.getSone("SoneId")).thenReturn(null);
soneInserter.serviceRun();
}
package net.pterodactylus.sone.database.memory;
-import static com.google.common.base.Optional.fromNullable;
import static net.pterodactylus.sone.test.Matchers.isPostWithId;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import net.pterodactylus.sone.data.Post;
-import com.google.common.base.Optional;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
@Before
public void setupMemoryDatabase() {
when(memoryDatabase.getPost(anyString())).thenAnswer(
- new Answer<Optional<Post>>() {
+ new Answer<Post>() {
@Override
- public Optional<Post> answer(
+ public Post answer(
InvocationOnMock invocation) {
- return fromNullable(
- posts.get(invocation.getArguments()[0]));
+ return posts.get(invocation.getArguments()[0]);
}
});
}
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.emptyIterable;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
firstAlbum.addImage(thirdImage);
secondAlbum.addImage(secondImage);
memoryDatabase.storeSone(sone);
- assertThat(memoryDatabase.getPost("post1").get(),
+ assertThat(memoryDatabase.getPost("post1"),
isPost(firstPost.getId(), 1000L, "post1",
Optional.<String>absent()));
- assertThat(memoryDatabase.getPost("post2").get(),
+ assertThat(memoryDatabase.getPost("post2"),
isPost(secondPost.getId(), 2000L, "post2", of(RECIPIENT_ID)));
- assertThat(memoryDatabase.getPost("post3").isPresent(), is(false));
- assertThat(memoryDatabase.getPostReply("reply1").get(),
+ assertThat(memoryDatabase.getPost("post3"), nullValue());
+ assertThat(memoryDatabase.getPostReply("reply1"),
isPostReply("reply1", "post1", 3000L, "reply1"));
- assertThat(memoryDatabase.getPostReply("reply2").get(),
+ assertThat(memoryDatabase.getPostReply("reply2"),
isPostReply("reply2", "post2", 4000L, "reply2"));
- assertThat(memoryDatabase.getPostReply("reply3").get(),
+ assertThat(memoryDatabase.getPostReply("reply3"),
isPostReply("reply3", "post1", 5000L, "reply3"));
- assertThat(memoryDatabase.getPostReply("reply4").isPresent(),
- is(false));
- assertThat(memoryDatabase.getAlbum("album1").get(),
+ assertThat(memoryDatabase.getPostReply("reply4"), nullValue());
+ assertThat(memoryDatabase.getAlbum("album1"),
isAlbum("album1", null, "album1", "album-description1"));
- assertThat(memoryDatabase.getAlbum("album2").get(),
+ assertThat(memoryDatabase.getAlbum("album2"),
isAlbum("album2", null, "album2", "album-description2"));
- assertThat(memoryDatabase.getAlbum("album3").get(),
+ assertThat(memoryDatabase.getAlbum("album3"),
isAlbum("album3", "album1", "album3", "album-description3"));
- assertThat(memoryDatabase.getAlbum("album4").isPresent(), is(false));
- assertThat(memoryDatabase.getImage("image1").get(),
+ assertThat(memoryDatabase.getAlbum("album4"), nullValue());
+ assertThat(memoryDatabase.getImage("image1"),
isImage("image1", 1000L, "KSK@image1", "image1",
"image-description1", 16, 9));
- assertThat(memoryDatabase.getImage("image2").get(),
+ assertThat(memoryDatabase.getImage("image2"),
isImage("image2", 2000L, "KSK@image2", "image2",
"image-description2", 32, 18));
- assertThat(memoryDatabase.getImage("image3").get(),
+ assertThat(memoryDatabase.getImage("image3"),
isImage("image3", 3000L, "KSK@image3", "image3",
"image-description3", 48, 27));
- assertThat(memoryDatabase.getImage("image4").isPresent(), is(false));
+ assertThat(memoryDatabase.getImage("image4"), nullValue());
}
@Test
@Test
public void testBasicAlbumFunctionality() {
Album newAlbum = new AlbumImpl(mock(Sone.class));
- assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(Optional.<Album>absent()));
+ assertThat(memoryDatabase.getAlbum(newAlbum.getId()), nullValue());
memoryDatabase.storeAlbum(newAlbum);
- assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(of(newAlbum)));
+ assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(newAlbum));
memoryDatabase.removeAlbum(newAlbum);
- assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(Optional.<Album>absent()));
+ assertThat(memoryDatabase.getAlbum(newAlbum.getId()), nullValue());
}
private void initializeFriends() {
import java.io.IOException;
import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.impl.IdOnlySone;
import net.pterodactylus.sone.database.PostProvider;
import net.pterodactylus.sone.database.SoneProvider;
-import com.google.common.base.Function;
import com.google.common.base.Optional;
+import kotlin.jvm.functions.Function1;
import org.junit.Test;
/**
}
@Test
+ public void invalidSskAndUskLinkIsParsedAsText() {
+ Iterable<Part> parts = soneTextParser.parse("SSK@a USK@a", null);
+ assertThat("Part Text", convertText(parts), is("SSK@a USK@a"));
+ }
+
+ @Test
public void sskLinkWithoutContextIsNotTrusted() {
Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", null);
assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test]"));
*/
private static class TestSoneProvider implements SoneProvider {
+ @Nonnull
@Override
- public Function<String, Optional<Sone>> soneLoader() {
- return new Function<String, Optional<Sone>>() {
+ public Function1<String, Sone> getSoneLoader() {
+ return new Function1<String, Sone>() {
@Override
- public Optional<Sone> apply(String soneId) {
+ public Sone invoke(String soneId) {
return getSone(soneId);
}
};
}
- /**
- * {@inheritDoc}
- */
+ @Nullable
@Override
- public Optional<Sone> getSone(final String soneId) {
- return Optional.<Sone>of(new IdOnlySone(soneId));
+ public Sone getSone(final String soneId) {
+ return new IdOnlySone(soneId);
}
/**
private static class AbsentSoneProvider extends TestSoneProvider {
@Override
- public Optional<Sone> getSone(String soneId) {
- return Optional.absent();
+ public Sone getSone(String soneId) {
+ return null;
}
}
private static class TestPostProvider implements PostProvider {
+ @Nullable
@Override
- public Optional<Post> getPost(final String postId) {
- return Optional.<Post>of(new Post() {
+ public Post getPost(@Nonnull final String postId) {
+ return new Post() {
@Override
public String getId() {
return postId;
public Post setKnown(boolean known) {
return null;
}
- });
+ };
}
@Override
private static class AbsentPostProvider extends TestPostProvider {
+ @Nullable
@Override
- public Optional<Post> getPost(String postId) {
- return Optional.absent();
+ public Post getPost(@Nonnull String postId) {
+ return null;
}
}
package net.pterodactylus.sone.fcp
-import com.google.common.base.Optional
import com.google.common.base.Optional.absent
import com.google.common.base.Optional.of
import net.pterodactylus.sone.core.Core
@Test
fun `request without text results in fcp exception`() {
parameters += "Sone" to "LocalSoneId"
- whenever(core.getSone("LocalSoneId")).thenReturn(Optional.of(localSone))
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
executeCommandAndExpectFcpException()
}
fun `request with text creates post`() {
parameters += "Sone" to "LocalSoneId"
parameters += "Text" to "Test"
- whenever(core.getSone("LocalSoneId")).thenReturn(of(localSone))
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
val post = mock<Post>().apply { whenever(id).thenReturn("PostId") }
whenever(core.createPost(localSone, absent(), "Test")).thenReturn(post)
val response = command.execute(parameters)
parameters += "Sone" to "LocalSoneId"
parameters += "Text" to "Test"
parameters += "Recipient" to "InvalidSoneId"
- whenever(core.getSone("LocalSoneId")).thenReturn(of(localSone))
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
executeCommandAndExpectFcpException()
}
parameters += "Sone" to "LocalSoneId"
parameters += "Text" to "Test"
parameters += "Recipient" to "LocalSoneId"
- whenever(core.getSone("LocalSoneId")).thenReturn(of(localSone))
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
val response = command.execute(parameters)
assertThat(response.replyParameters["Message"], equalTo("Error"))
assertThat(response.replyParameters["ErrorMessage"], notNullValue())
parameters += "Sone" to "LocalSoneId"
parameters += "Text" to "Test"
parameters += "Recipient" to "RemoteSoneId"
- whenever(core.getSone("LocalSoneId")).thenReturn(of(localSone))
- whenever(core.getSone("RemoteSoneId")).thenReturn(of(remoteSone))
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
+ whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone)
val post = mock<Post>().apply { whenever(id).thenReturn("PostId") }
whenever(core.createPost(localSone, of(remoteSone), "Test")).thenReturn(post)
val response = command.execute(parameters)
private fun addValidLocalSoneParameter() {
parameters += "Sone" to "LocalSoneId"
- whenever(core.getSone("LocalSoneId")).thenReturn(of(localSone))
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
}
@Test
private fun addValidPostParameter() {
parameters += "Post" to "ValidPostId"
- whenever(core.getPost("ValidPostId")).thenReturn(of(post))
+ whenever(core.getPost("ValidPostId")).thenReturn(post)
}
@Test
@Test
fun `request with post from remote sone returns error response`() {
parameters += "Post" to "RemotePostId"
- whenever(core.getPost("RemotePostId")).thenReturn(of(postFromRemoteSone))
+ whenever(core.getPost("RemotePostId")).thenReturn(postFromRemoteSone)
val response = command.execute(parameters)
assertThat(response.replyParameters["Message"], equalTo("Error"))
assertThat(response.replyParameters["ErrorCode"], equalTo("401"))
@Test
fun `request with post from local sone deletes posts`() {
parameters += "Post" to "LocalPostId"
- whenever(core.getPost("LocalPostId")).thenReturn(of(postFromLocalSone))
+ whenever(core.getPost("LocalPostId")).thenReturn(postFromLocalSone)
val response = command.execute(parameters)
assertThat(response.replyParameters["Message"], equalTo("PostDeleted"))
verify(core).deletePost(postFromLocalSone)
@Test
fun `request with remote post reply parameter results in error response`() {
parameters += "Reply" to "RemoteReplyId"
- whenever(core.getPostReply("RemoteReplyId")).thenReturn(of(remotePostReply))
+ whenever(core.getPostReply("RemoteReplyId")).thenReturn(remotePostReply)
val response = command.execute(parameters)
assertThat(response.replyParameters["Message"], equalTo("Error"))
assertThat(response.replyParameters["ErrorCode"], equalTo("401"))
@Test
fun `request with local post reply parameter deletes reply`() {
parameters += "Reply" to "RemoteReplyId"
- whenever(core.getPostReply("RemoteReplyId")).thenReturn(of(localPostReply))
+ whenever(core.getPostReply("RemoteReplyId")).thenReturn(localPostReply)
val response = command.execute(parameters)
assertThat(response.replyParameters["Message"], equalTo("ReplyDeleted"))
verify(core).deleteReply(localPostReply)
@Before
fun setupPostWithLikesAndReplies() {
- whenever(core.getPost("ValidPostId")).thenReturn(post.asOptional())
+ whenever(core.getPost("ValidPostId")).thenReturn(post)
whenever(core.getLikes(post)).thenReturn(setOf(sone1, sone2))
val replies = listOf(postReply1, postReply2)
whenever(core.getReplies("ValidPostId")).thenReturn(replies)
import freenet.support.SimpleFieldSet
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.containsInAnyOrder
import org.hamcrest.Matchers.equalTo
private fun setupAllPostsAndReplies() {
parameters += "Sone" to "ValidSoneId"
whenever(localSone.id).thenReturn("ValidSoneId")
- whenever(core.getSone("ValidSoneId")).thenReturn(localSone.asOptional())
- whenever(core.getSone("Friend1")).thenReturn(friend1.asOptional())
+ whenever(core.getSone("ValidSoneId")).thenReturn(localSone)
+ whenever(core.getSone("Friend1")).thenReturn(friend1)
whenever(core.getLikes(post1)).thenReturn(setOf(sone3, sone4))
whenever(core.getLikes(post1Reply1)).thenReturn(setOf(sone2, sone3))
whenever(core.getLikes(post1Reply2)).thenReturn(setOf(sone3))
import freenet.support.SimpleFieldSet
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.containsInAnyOrder
import org.hamcrest.Matchers.equalTo
whenever(core.getReplies("Post2")).thenReturn(listOf(post2Reply1, post2Reply2))
whenever(localSone.id).thenReturn("LocalSone")
whenever(remoteSone.id).thenReturn("RemoteSone")
- whenever(core.getSone("LocalSone")).thenReturn(localSone.asOptional())
- whenever(core.getSone("ValidSoneId")).thenReturn(remoteSone.asOptional())
+ whenever(core.getSone("LocalSone")).thenReturn(localSone)
+ whenever(core.getSone("ValidSoneId")).thenReturn(remoteSone)
whenever(remoteSone.posts).thenReturn(listOf(post2, post1))
parameters += "Sone" to "ValidSoneId"
}
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.freenet.fcp.FcpException
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.nullValue
@Test
fun `request with valid Sone parameter results in response with Sone information`() {
- whenever(core.getSone("SoneId")).thenReturn(sone.asOptional())
- whenever(core.getSone(null)).thenReturn(null.asOptional())
+ whenever(core.getSone("SoneId")).thenReturn(sone)
parameters += "Sone" to "SoneId"
val replyParameters = command.execute(parameters).replyParameters
assertThat(replyParameters["Message"], equalTo("Sone"))
@Test
fun `request with local sone parameter results in followed being true for friend sone`() {
- whenever(core.getSone("SoneId")).thenReturn(sone.asOptional())
- whenever(core.getSone("LocalSone")).thenReturn(localSone.asOptional())
+ whenever(core.getSone("SoneId")).thenReturn(sone)
+ whenever(core.getSone("LocalSone")).thenReturn(localSone)
whenever(localSone.id).thenReturn("LocalSone")
whenever(localSone.hasFriend("SoneId")).thenReturn(true)
parameters += "Sone" to "SoneId"
@Test
fun `request with local sone parameter results in followed being false for non-friend sone`() {
- whenever(core.getSone("SoneId")).thenReturn(sone.asOptional())
- whenever(core.getSone("LocalSone")).thenReturn(localSone.asOptional())
+ whenever(core.getSone("SoneId")).thenReturn(sone)
+ whenever(core.getSone("LocalSone")).thenReturn(localSone)
whenever(localSone.id).thenReturn("LocalSone")
parameters += "Sone" to "SoneId"
parameters += "LocalSone" to "LocalSone"
@Test
fun `request with remote sone as local sone parameter results in fcp exception`() {
- whenever(core.getSone("SoneId")).thenReturn(sone.asOptional())
- whenever(core.getSone("RemoteSone")).thenReturn(remoteSone.asOptional())
+ whenever(core.getSone("SoneId")).thenReturn(sone)
+ whenever(core.getSone("RemoteSone")).thenReturn(remoteSone)
whenever(localSone.id).thenReturn("RemoteSone")
parameters += "Sone" to "SoneId"
parameters += "LocalSone" to "RemoteSone"
@Before
fun setupPostAndSones() {
- whenever(core.getPost("PostId")).thenReturn(post.asOptional())
- whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone.asOptional())
- whenever(core.getSone("LocalSoneId")).thenReturn(localSone.asOptional())
+ whenever(core.getPost("PostId")).thenReturn(post)
+ whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone)
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
}
@Test
@Before
fun setupRepliesAndSones() {
- whenever(core.getPostReply("ReplyId")).thenReturn(reply.asOptional())
- whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone.asOptional())
- whenever(core.getSone("LocalSoneId")).thenReturn(localSone.asOptional())
+ whenever(core.getPostReply("ReplyId")).thenReturn(reply)
+ whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone)
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
}
@Test
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Before
@Before
fun setupSones() {
- whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone.asOptional())
- whenever(core.getSone("LocalSoneId")).thenReturn(localSone.asOptional())
+ whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone)
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
whenever(localSone.id).thenReturn("LocalSoneId")
}
package net.pterodactylus.sone.fcp
-import com.google.common.base.Optional
import com.google.common.base.Optional.absent
import freenet.support.SimpleFieldSet
import net.pterodactylus.sone.core.Core
@Before
fun setupCore() {
- whenever(core.getSone(anyString())).thenReturn(absent())
- whenever(core.getPost(anyString())).thenReturn(absent())
- whenever(core.getPostReply(anyString())).thenReturn(absent())
+ whenever(core.getSone(anyString())).thenReturn(null)
+ whenever(core.getPost(anyString())).thenReturn(null)
+ whenever(core.getPostReply(anyString())).thenReturn(null)
}
protected fun createSone(id: String, name: String, firstName: String, lastName: String, time: Long) = mock<Sone>().apply {
fun requestWithValidRemoteSoneParameterResultsInFcpException() {
parameters += "Sone" to "RemoteSoneId"
- whenever(core.getSone("RemoteSoneId")).thenReturn(Optional.of(remoteSone))
+ whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone)
executeCommandAndExpectFcpException()
}
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.sone.utils.asOptional
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Before
@Before
fun setupSones() {
- whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone.asOptional())
- whenever(core.getSone("LocalSoneId")).thenReturn(localSone.asOptional())
+ whenever(core.getSone("RemoteSoneId")).thenReturn(remoteSone)
+ whenever(core.getSone("LocalSoneId")).thenReturn(localSone)
whenever(localSone.id).thenReturn("LocalSoneId")
}
package net.pterodactylus.sone.template
-import com.google.common.base.Optional.of
import com.google.inject.Guice
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.data.Sone
private fun setupSone(identity: String): Sone {
val sone = mock<Sone>()
`when`(sone.id).thenReturn(identity)
- `when`(core.getSone(identity)).thenReturn(of(sone))
+ `when`(core.getSone(identity)).thenReturn(sone)
return sone
}
parameters.put("sone", soneOrSoneId)
filter.format(templateContext, "text", parameters)
val context = forClass(SoneTextParserContext::class.java)
- verify(soneTextParser).parse(eq<String>("text"), context.capture())
+ verify(soneTextParser).parse(eq<String>("text") ?: "", context.capture())
assertThat(context.value.postingSone, `is`(sone))
}
package net.pterodactylus.sone.template
-import com.google.common.base.Optional
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.Profile
`when`(sone.profile).thenReturn(Profile(sone))
`when`(sone.name).thenReturn(name)
sone.profile.firstName = firstName
- `when`(core.getSone(identity)).thenReturn(Optional.of<Sone>(sone))
+ `when`(core.getSone(identity)).thenReturn(sone)
return sone
}
whenever(core.preferences).thenReturn(preferences)
whenever(core.updateChecker).thenReturn(updateChecker)
- whenever(core.getSone(ArgumentMatchers.anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
+ whenever(core.getSone(ArgumentMatchers.anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)] }
whenever(core.getLocalSone(ArgumentMatchers.anyString())).thenAnswer { localSones[it[0]] }
- whenever(core.getPost(ArgumentMatchers.anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
+ whenever(core.getPost(ArgumentMatchers.anyString())).thenAnswer { (posts + newPosts)[it[0]] }
whenever(core.getLikes(ArgumentMatchers.any<Post>())).then { postLikes[it[0]] ?: emptySet<Sone>() }
whenever(core.getLikes(ArgumentMatchers.any<PostReply>())).then { replyLikes[it[0]] ?: emptySet<Sone>() }
- whenever(core.getPostReply(ArgumentMatchers.anyString())).then { replies[it[0]].asOptional() }
+ whenever(core.getPostReply(ArgumentMatchers.anyString())).then { replies[it[0]] }
whenever(core.getAlbum(ArgumentMatchers.anyString())).then { albums[it[0]] }
whenever(core.getImage(ArgumentMatchers.anyString())).then { images[it[0]] }
whenever(core.getImage(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())).then { images[it[0]] }
package net.pterodactylus.sone.web.ajax
+import net.pterodactylus.sone.data.Sone
import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Test
@Test
fun `request with valid sone unfollows sone`() {
- addSone(mock(), "sone-id")
+ addSone(mock<Sone>().apply { whenever(id).thenReturn("sone-id") })
addRequestParameter("sone", "sone-id")
assertThatJsonIsSuccessful()
verify(core).unfollowSone(currentSone, "sone-id")
addHttpRequestPart("save-profile", "true")
addHttpRequestPart(fieldName, newValue.toString())
verifyRedirect("editProfile.html") {
+ verify(currentSone).profile = profile
verify(core).touchConfiguration()
assertThat(fieldAccessor(), equalTo(expectedValue))
}
whenever(core.preferences).thenReturn(preferences)
whenever(core.identityManager.allOwnIdentities).then { ownIdentities }
whenever(core.sones).then { allSones.values }
- whenever(core.getSone(anyString())).then { allSones[it[0]].asOptional() }
+ whenever(core.getSone(anyString())).then { allSones[it[0]] }
whenever(core.localSones).then { localSones.values }
whenever(core.getLocalSone(anyString())).then { localSones[it[0]] }
- whenever(core.getPost(anyString())).then { allPosts[it[0]].asOptional() }
- whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]].asOptional() }
+ whenever(core.getPost(anyString())).then { allPosts[it[0]] }
+ whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
whenever(core.getImage(anyString())).then { allImages[it[0]]}