From: David ‘Bombe’ Roden Date: Sat, 12 Jan 2013 16:41:27 +0000 (+0100) Subject: Merge branch 'next' into new-database-38 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=refs%2Fheads%2Fnew-database-38;hp=-c Merge branch 'next' into new-database-38 Conflicts: src/main/java/net/pterodactylus/sone/core/Core.java src/main/java/net/pterodactylus/sone/web/SearchPage.java --- 771e8efcd92c7325423441d57c5a6ed90a835e6f diff --combined src/main/java/net/pterodactylus/sone/core/Core.java index 0b1379a,5843b30..1bb638a --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@@ -19,7 -19,6 +19,7 @@@ package net.pterodactylus.sone.core import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@@ -48,7 -47,6 +48,7 @@@ import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.Sone.SoneStatus; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.data.impl.PostImpl; +import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; import net.pterodactylus.sone.freenet.wot.Identity; @@@ -61,6 -59,7 +61,7 @@@ import net.pterodactylus.util.config.Co import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.service.AbstractService; + import net.pterodactylus.util.thread.NamedThreadFactory; import net.pterodactylus.util.thread.Ticker; import net.pterodactylus.util.validation.EqualityValidator; import net.pterodactylus.util.validation.IntegerRangeValidator; @@@ -79,6 -78,9 +80,9 @@@ public class Core extends AbstractServi /** The logger. */ private static final Logger logger = Logging.getLogger(Core.class); + /** The start time. */ + private final long startupTime = System.currentTimeMillis(); + /** The options. */ private final Options options = new Options(); @@@ -94,9 -96,6 +98,9 @@@ /** Whether we’re currently saving the configuration. */ private boolean storingConfiguration = false; + /** The database. */ + private Database database; + /** The identity manager. */ private final IdentityManager identityManager; @@@ -110,7 -109,7 +114,7 @@@ private final ImageInserter imageInserter; /** Sone downloader thread-pool. */ - private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10); + private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10, new NamedThreadFactory("Sone Downloader %2$d")); /** The update checker. */ private final UpdateChecker updateChecker; @@@ -136,6 -135,14 +140,6 @@@ /* synchronize access on this on localSones. */ private final Map soneRescuers = new HashMap(); - /** All local Sones. */ - /* synchronize access on this on itself. */ - private final Map localSones = new HashMap(); - - /** All remote Sones. */ - /* synchronize access on this on itself. */ - private final Map remoteSones = new HashMap(); - /** All known Sones. */ private final Set knownSones = new HashSet(); @@@ -178,8 -185,6 +182,8 @@@ * * @param configuration * The configuration of the core + * @param database + * The database to use * @param freenetInterface * The freenet interface * @param identityManager @@@ -187,10 -192,9 +191,10 @@@ * @param webOfTrustUpdater * The WebOfTrust updater */ - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater) { + public Core(Configuration configuration, Database database, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater) { super("Sone Core"); this.configuration = configuration; + this.database = database; this.freenetInterface = freenetInterface; this.identityManager = identityManager; this.soneDownloader = new SoneDownloader(this, freenetInterface); @@@ -228,6 -232,15 +232,15 @@@ // /** + * Returns the time Sone was started. + * + * @return The startup time (in milliseconds since Jan 1, 1970 UTC) + */ + public long getStartupTime() { + return startupTime; + } + + /** * Sets the configuration to use. This will automatically save the current * configuration to the given configuration. * @@@ -284,8 -297,8 +297,8 @@@ * @return The Sone rescuer for the given Sone */ public SoneRescuer getSoneRescuer(Sone sone) { - Validation.begin().isNotNull("Sone", sone).check().is("Local Sone", isLocalSone(sone)).check(); - synchronized (localSones) { + Validation.begin().isNotNull("Sone", sone).check().is("Local Sone", sone.isLocal()).check(); + synchronized (soneRescuers) { SoneRescuer soneRescuer = soneRescuers.get(sone); if (soneRescuer == null) { soneRescuer = new SoneRescuer(this, soneDownloader, sone); @@@ -314,8 -327,11 +327,8 @@@ * * @return All Sones */ - public Set getSones() { - Set allSones = new HashSet(); - allSones.addAll(getLocalSones()); - allSones.addAll(getRemoteSones()); - return allSones; + public Collection getSones() { + return database.getSones(); } /** @@@ -346,7 -362,10 +359,7 @@@ */ @Override public Sone getSone(String id, boolean create) { - if (isLocalSone(id)) { - return getLocalSone(id); - } - return getRemoteSone(id, create); + return database.getSone(id, create); } /** @@@ -358,7 -377,34 +371,7 @@@ * otherwise */ public boolean hasSone(String id) { - return isLocalSone(id) || isRemoteSone(id); - } - - /** - * Returns whether the given Sone is a local Sone. - * - * @param sone - * The Sone to check for its locality - * @return {@code true} if the given Sone is local, {@code false} otherwise - */ - public boolean isLocalSone(Sone sone) { - synchronized (localSones) { - return localSones.containsKey(sone.getId()); - } - } - - /** - * Returns whether the given ID is the ID of a local Sone. - * - * @param id - * The Sone ID to check for its locality - * @return {@code true} if the given ID is a local Sone, {@code false} - * otherwise - */ - public boolean isLocalSone(String id) { - synchronized (localSones) { - return localSones.containsKey(id); - } + return database.getSone(id, false) != null; } /** @@@ -366,8 -412,10 +379,8 @@@ * * @return All local Sones */ - public Set getLocalSones() { - synchronized (localSones) { - return new HashSet(localSones.values()); - } + public Collection getLocalSones() { + return database.getLocalSones(); } /** @@@ -392,7 -440,14 +405,7 @@@ * @return The Sone with the given ID, or {@code null} */ public Sone getLocalSone(String id, boolean create) { - synchronized (localSones) { - Sone sone = localSones.get(id); - if ((sone == null) && create) { - sone = new Sone(id); - localSones.put(id, sone); - } - return sone; - } + return database.getLocalSone(id, create); } /** @@@ -400,8 -455,10 +413,8 @@@ * * @return All remote Sones */ - public Set getRemoteSones() { - synchronized (remoteSones) { - return new HashSet(remoteSones.values()); - } + public Collection getRemoteSones() { + return database.getRemoteSones(); } /** @@@ -415,7 -472,42 +428,7 @@@ * @return The Sone with the given ID */ public Sone getRemoteSone(String id, boolean create) { - synchronized (remoteSones) { - Sone sone = remoteSones.get(id); - if ((sone == null) && create && (id != null) && (id.length() == 43)) { - sone = new Sone(id); - remoteSones.put(id, sone); - } - return sone; - } - } - - /** - * Returns whether the given Sone is a remote Sone. - * - * @param sone - * The Sone to check - * @return {@code true} if the given Sone is a remote Sone, {@code false} - * otherwise - */ - public boolean isRemoteSone(Sone sone) { - synchronized (remoteSones) { - return remoteSones.containsKey(sone.getId()); - } - } - - /** - * Returns whether the Sone with the given ID is a remote Sone. - * - * @param id - * The ID of the Sone to check - * @return {@code true} if the Sone with the given ID is a remote Sone, - * {@code false} otherwise - */ - public boolean isRemoteSone(String id) { - synchronized (remoteSones) { - return remoteSones.containsKey(id); - } + return database.getRemoteSone(id, create); } /** @@@ -462,6 -554,17 +475,6 @@@ } /** - * Returns the post with the given ID. - * - * @param postId - * The ID of the post to get - * @return The post with the given ID, or a new post with the given ID - */ - public Post getPost(String postId) { - return getPost(postId, true); - } - - /** * Returns the post with the given ID, optionally creating a new post. * * @param postId @@@ -547,7 -650,7 +560,7 @@@ * @return All replies for the given post */ public List getReplies(Post post) { - Set sones = getSones(); + Collection sones = getSones(); List replies = new ArrayList(); for (Sone sone : sones) { for (PostReply reply : sone.getReplies()) { @@@ -768,25 -871,27 +781,25 @@@ logger.log(Level.WARNING, "Given OwnIdentity is null!"); return null; } - synchronized (localSones) { - final Sone sone; - try { - sone = getLocalSone(ownIdentity.getId()).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); - } catch (MalformedURLException mue1) { - logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1); - return null; - } - sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0)); - sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); - sone.setKnown(true); - /* TODO - load posts ’n stuff */ - localSones.put(ownIdentity.getId(), sone); - final SoneInserter soneInserter = new SoneInserter(this, freenetInterface, sone); - soneInserter.addSoneInsertListener(this); - soneInserters.put(sone, soneInserter); - sone.setStatus(SoneStatus.idle); - loadSone(sone); - soneInserter.start(); - return sone; + Sone sone; + try { + sone = getLocalSone(ownIdentity.getId()).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); + } catch (MalformedURLException mue1) { + logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1); + return null; } + sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0)); + sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); + sone.setKnown(true); + /* TODO - load posts ’n stuff */ + final SoneInserter soneInserter = new SoneInserter(this, freenetInterface, sone); + soneInserter.addSoneInsertListener(this); + soneInserters.put(sone, soneInserter); + sone.setStatus(SoneStatus.idle); + loadSone(sone); + soneInserter.start(); + database.saveSone(sone); + return sone; } /** @@@ -826,36 -931,37 +839,36 @@@ logger.log(Level.WARNING, "Given Identity is null!"); return null; } - synchronized (remoteSones) { - final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity); - boolean newSone = sone.getRequestUri() == null; - sone.setRequestUri(getSoneUri(identity.getRequestUri())); - sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)); + final Sone sone = getRemoteSone(identity.getId(), true).setIdentity(identity); + boolean newSone = sone.getRequestUri() == null; + sone.setRequestUri(getSoneUri(identity.getRequestUri())); + sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), (long) 0)); + if (newSone) { + synchronized (knownSones) { + newSone = !knownSones.contains(sone.getId()); + } + sone.setKnown(!newSone); if (newSone) { - synchronized (knownSones) { - newSone = !knownSones.contains(sone.getId()); - } - sone.setKnown(!newSone); - if (newSone) { - coreListenerManager.fireNewSoneFound(sone); - for (Sone localSone : getLocalSones()) { - if (localSone.getOptions().getBooleanOption("AutoFollow").get()) { - followSone(localSone, sone); - } + coreListenerManager.fireNewSoneFound(sone); + for (Sone localSone : getLocalSones()) { + if (localSone.getOptions().getBooleanOption("AutoFollow").get()) { + followSone(localSone, sone); } } } - soneDownloader.addSone(sone); - soneDownloaders.execute(new Runnable() { + } + soneDownloader.addSone(sone); + soneDownloaders.execute(new Runnable() { - @Override - @SuppressWarnings("synthetic-access") - public void run() { - soneDownloader.fetchSone(sone, sone.getRequestUri()); - } + @Override + @SuppressWarnings("synthetic-access") + public void run() { + soneDownloader.fetchSone(sone, sone.getRequestUri()); + } - }); - return sone; - } + }); + database.saveSone(sone); + return sone; } /** @@@ -948,8 -1054,7 +961,8 @@@ } /** - * Sets the trust value of the given origin Sone for the target Sone. + * Sets the trust value of the given origin Sone for + * the target Sone. * * @param origin * The origin Sone @@@ -1056,8 -1161,8 +1069,8 @@@ if (!storedPosts.contains(post)) { if (post.getTime() < getSoneFollowingTime(sone)) { knownPosts.add(post.getId()); + post.setKnown(true); } else if (!knownPosts.contains(post.getId())) { - sone.setKnown(false); coreListenerManager.fireNewPostFound(post); } } @@@ -1081,8 -1186,8 +1094,8 @@@ if (!storedReplies.contains(reply)) { if (reply.getTime() < getSoneFollowingTime(sone)) { knownReplies.add(reply.getId()); + reply.setKnown(true); } else if (!knownReplies.contains(reply.getId())) { - reply.setKnown(false); coreListenerManager.fireNewReplyFound(reply); } } @@@ -1153,14 -1258,16 +1166,14 @@@ logger.log(Level.WARNING, String.format("Tried to delete Sone of non-own identity: %s", sone)); return; } - synchronized (localSones) { - if (!localSones.containsKey(sone.getId())) { - logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone)); - return; - } - localSones.remove(sone.getId()); - SoneInserter soneInserter = soneInserters.remove(sone); - soneInserter.removeSoneInsertListener(this); - soneInserter.stop(); + if (!sone.isLocal()) { + logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone)); + return; } + database.removeSone(sone.getId()); + SoneInserter soneInserter = soneInserters.remove(sone); + soneInserter.removeSoneInsertListener(this); + soneInserter.stop(); webOfTrustUpdater.removeContext((OwnIdentity) sone.getIdentity(), "Sone"); webOfTrustUpdater.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition"); try { @@@ -1196,7 -1303,7 +1209,7 @@@ * The Sone to load and update */ public void loadSone(Sone sone) { - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to load non-local Sone: %s", sone)); return; } @@@ -1253,7 -1360,7 +1266,7 @@@ logger.log(Level.WARNING, "Invalid post found, aborting load!"); return; } - Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText); + Post post = getPost(postId, true).setSone(sone).setTime(postTime).setText(postText); if ((postRecipientId != null) && (postRecipientId.length() == 43)) { post.setRecipient(getSone(postRecipientId)); } @@@ -1275,7 -1382,7 +1288,7 @@@ logger.log(Level.WARNING, "Invalid reply found, aborting load!"); return; } - replies.add(getReply(replyId).setSone(sone).setPost(getPost(postId)).setTime(replyTime).setText(replyText)); + replies.add(getReply(replyId).setSone(sone).setPost(getPost(postId, true)).setTime(replyTime).setText(replyText)); } /* load post likes. */ @@@ -1419,6 -1526,50 +1432,6 @@@ * * @param sone * The Sone that creates the post - * @param text - * The text of the post - * @return The created post - */ - public Post createPost(Sone sone, String text) { - return createPost(sone, System.currentTimeMillis(), text); - } - - /** - * Creates a new post. - * - * @param sone - * The Sone that creates the post - * @param time - * The time of the post - * @param text - * The text of the post - * @return The created post - */ - public Post createPost(Sone sone, long time, String text) { - return createPost(sone, null, time, text); - } - - /** - * Creates a new post. - * - * @param sone - * The Sone that creates the post - * @param recipient - * The recipient Sone, or {@code null} if this post does not have - * a recipient - * @param text - * The text of the post - * @return The created post - */ - public Post createPost(Sone sone, Sone recipient, String text) { - return createPost(sone, recipient, System.currentTimeMillis(), text); - } - - /** - * Creates a new post. - * - * @param sone - * The Sone that creates the post * @param recipient * The recipient Sone, or {@code null} if this post does not have * a recipient @@@ -1429,7 -1580,8 +1442,8 @@@ * @return The created post */ public Post createPost(Sone sone, Sone recipient, long time, String text) { + Validation.begin().isNotNull("Text", text).check().isGreater("Text Length", text.length(), 0).check(); - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone)); return null; } @@@ -1463,7 -1615,7 +1477,7 @@@ * The post to delete */ public void deletePost(Post post) { - if (!isLocalSone(post.getSone())) { + if (!post.getSone().isLocal()) { logger.log(Level.WARNING, String.format("Tried to delete post of non-local Sone: %s", post.getSone())); return; } @@@ -1569,7 -1721,8 +1583,8 @@@ * @return The created reply */ public PostReply createReply(Sone sone, Post post, long time, String text) { + Validation.begin().isNotNull("Text", text).check().isGreater("Text Length", text.trim().length(), 0).check(); - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone)); return null; } @@@ -1603,7 -1756,7 +1618,7 @@@ */ public void deleteReply(PostReply reply) { Sone sone = reply.getSone(); - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to delete non-local reply: %s", reply)); return; } @@@ -1678,7 -1831,7 +1693,7 @@@ * The album to remove */ public void deleteAlbum(Album album) { - Validation.begin().isNotNull("Album", album).check().is("Local Sone", isLocalSone(album.getSone())).check(); + Validation.begin().isNotNull("Album", album).check().is("Local Sone", album.getSone().isLocal()).check(); if (!album.isEmpty()) { return; } @@@ -1705,7 -1858,7 +1720,7 @@@ * @return The newly created image */ public Image createImage(Sone sone, Album album, TemporaryImage temporaryImage) { - Validation.begin().isNotNull("Sone", sone).isNotNull("Album", album).isNotNull("Temporary Image", temporaryImage).check().is("Local Sone", isLocalSone(sone)).check().isEqual("Owner and Album Owner", sone, album.getSone()).check(); + Validation.begin().isNotNull("Sone", sone).isNotNull("Album", album).isNotNull("Temporary Image", temporaryImage).check().is("Local Sone", sone.isLocal()).check().isEqual("Owner and Album Owner", sone, album.getSone()).check(); Image image = new Image(temporaryImage.getId()).setSone(sone).setCreationTime(System.currentTimeMillis()); album.addImage(image); synchronized (images) { @@@ -1724,7 -1877,7 +1739,7 @@@ * The image to delete */ public void deleteImage(Image image) { - Validation.begin().isNotNull("Image", image).check().is("Local Sone", isLocalSone(image.getSone())).check(); + Validation.begin().isNotNull("Image", image).check().is("Local Sone", image.getSone().isLocal()).check(); deleteTemporaryImage(image.getId()); image.getAlbum().removeImage(image); synchronized (images) { @@@ -1827,10 -1980,12 +1842,10 @@@ */ @Override public void serviceStop() { - synchronized (localSones) { - for (Entry soneInserter : soneInserters.entrySet()) { - soneInserter.getValue().removeSoneInsertListener(this); - soneInserter.getValue().stop(); - saveSone(soneInserter.getKey()); - } + for (Entry soneInserter : soneInserters.entrySet()) { + soneInserter.getValue().removeSoneInsertListener(this); + soneInserter.getValue().stop(); + saveSone(soneInserter.getKey()); } saveConfiguration(); webOfTrustUpdater.stop(); @@@ -1851,7 -2006,7 +1866,7 @@@ * The Sone to save */ private synchronized void saveSone(Sone sone) { - if (!isLocalSone(sone)) { + if (!sone.isLocal()) { logger.log(Level.FINE, String.format("Tried to save non-local Sone: %s", sone)); return; } @@@ -2260,7 -2415,7 +2275,7 @@@ */ @Override public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) { - new Thread(new Runnable() { + soneDownloaders.execute(new Runnable() { @Override @SuppressWarnings("synthetic-access") @@@ -2271,7 -2426,7 +2286,7 @@@ soneDownloader.addSone(sone); soneDownloader.fetchSone(sone); } - }).start(); + }); } /** @@@ -2314,7 -2469,9 +2329,7 @@@ } } } - synchronized (remoteSones) { - remoteSones.remove(identity.getId()); - } + database.removeSone(identity.getId()); coreListenerManager.fireSoneRemoved(sone); } diff --combined src/main/java/net/pterodactylus/sone/main/SonePlugin.java index 42e2fe1,5b28b85..1bc8b5a --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@@ -25,8 -25,6 +25,8 @@@ import java.util.logging.Logger import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.FreenetInterface; import net.pterodactylus.sone.core.WebOfTrustUpdater; +import net.pterodactylus.sone.database.Database; +import net.pterodactylus.sone.database.memory.MemoryDatabase; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend; import net.pterodactylus.sone.freenet.plugin.PluginConnector; @@@ -86,7 -84,7 +86,7 @@@ public class SonePlugin implements Fred } /** The version. */ - public static final Version VERSION = new Version(0, 8, 2); + public static final Version VERSION = new Version(0, 8, 4); /** The logger. */ private static final Logger logger = Logging.getLogger(SonePlugin.class); @@@ -188,18 -186,14 +188,17 @@@ /* create web of trust connector. */ PluginConnector pluginConnector = new PluginConnector(pluginRespirator); webOfTrustConnector = new WebOfTrustConnector(pluginConnector); - identityManager = new IdentityManager(webOfTrustConnector); - identityManager.setContext("Sone"); + identityManager = new IdentityManager(webOfTrustConnector, "Sone"); + /* create Sone database. */ + Database soneDatabase = new MemoryDatabase(); + /* create trust updater. */ WebOfTrustUpdater trustUpdater = new WebOfTrustUpdater(webOfTrustConnector); trustUpdater.init(); /* create core. */ - core = new Core(oldConfiguration, freenetInterface, identityManager, trustUpdater); + core = new Core(oldConfiguration, soneDatabase, freenetInterface, identityManager, trustUpdater); /* create the web interface. */ webInterface = new WebInterface(this); diff --combined src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index 19db65b,b3593ec..2c209d5 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@@ -30,7 -30,6 +30,7 @@@ import net.pterodactylus.sone.core.Post import net.pterodactylus.sone.core.SoneProvider; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.memory.MemorySone; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; import freenet.keys.FreenetURI; @@@ -56,28 -55,50 +56,50 @@@ public class SoneTextParser implements private enum LinkType { /** Link is a KSK. */ - KSK, + KSK("KSK@"), /** Link is a CHK. */ - CHK, + CHK("CHK@"), /** Link is an SSK. */ - SSK, + SSK("SSK@"), /** Link is a USK. */ - USK, + USK("USK@"), /** Link is HTTP. */ - HTTP, + HTTP("http://"), /** Link is HTTPS. */ - HTTPS, + HTTPS("https://"), /** Link is a Sone. */ - SONE, + SONE("sone://"), /** Link is a post. */ - POST, + POST("post://"); + + /** The scheme identifying this link type. */ + private final String scheme; + + /** + * Creates a new link type identified by the given scheme. + * + * @param scheme + * The scheme of the link type + */ + private LinkType(String scheme) { + this.scheme = scheme; + } + + /** + * Returns the scheme of this link type. + * + * @return The scheme of this link type + */ + public String getScheme() { + return scheme; + } } @@@ -201,6 -222,20 +223,20 @@@ } lineComplete = false; + Matcher matcher = whitespacePattern.matcher(line); + int nextSpace = matcher.find(0) ? matcher.start() : line.length(); + String link = line.substring(0, nextSpace); + String name = link; + logger.log(Level.FINER, String.format("Found link: %s", link)); + logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); + + /* if there is no text after the scheme, it’s not a link! */ + if (link.equals(linkType.getScheme())) { + parts.add(new PlainTextPart(linkType.getScheme())); + line = line.substring(linkType.getScheme().length()); + continue; + } + if (linkType == LinkType.SONE) { if (line.length() >= (7 + 43)) { String soneId = line.substring(7, 50); @@@ -210,7 -245,7 +246,7 @@@ * don’t use create=true above, we don’t want * the empty shell. */ - sone = new Sone(soneId); + sone = new MemorySone(soneId, false); } parts.add(new SonePart(sone)); line = line.substring(50); @@@ -236,12 -271,6 +272,6 @@@ } continue; } - Matcher matcher = whitespacePattern.matcher(line); - int nextSpace = matcher.find(0) ? matcher.start() : line.length(); - String link = line.substring(0, nextSpace); - String name = link; - logger.log(Level.FINER, String.format("Found link: %s", link)); - logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) { FreenetURI uri; diff --combined src/main/java/net/pterodactylus/sone/web/SearchPage.java index 691453d,0f12abf..524e501 --- a/src/main/java/net/pterodactylus/sone/web/SearchPage.java +++ b/src/main/java/net/pterodactylus/sone/web/SearchPage.java @@@ -111,7 -111,27 +111,27 @@@ public class SearchPage extends SoneTem throw new RedirectException("index.html"); } + /* check for a couple of shortcuts. */ + if (phrases.size() == 1) { + String phrase = phrases.get(0).getPhrase(); + + /* is it a Sone ID? */ + redirectIfNotNull(getSoneId(phrase), "viewSone.html?sone="); + + /* is it a post ID? */ + redirectIfNotNull(getPostId(phrase), "viewPost.html?post="); + + /* is it a reply ID? show the post. */ + redirectIfNotNull(getReplyPostId(phrase), "viewPost.html?post="); + + /* is it an album ID? */ + redirectIfNotNull(getAlbumId(phrase), "imageBrowser.html?album="); + + /* is it an image ID? */ + redirectIfNotNull(getImageId(phrase), "imageBrowser.html?image="); + } + - Set sones = webInterface.getCore().getSones(); + Collection sones = webInterface.getCore().getSones(); Set> soneHits = getHits(sones, phrases, SoneStringGenerator.COMPLETE_GENERATOR); Set> postHits; @@@ -271,6 -291,91 +291,91 @@@ } /** + * Throws a + * {@link net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException} + * if the given object is not {@code null}, appending the object to the + * given target URL. + * + * @param object + * The object on which to redirect + * @param target + * The target of the redirect + * @throws RedirectException + * if {@code object} is not {@code null} + */ + private static void redirectIfNotNull(String object, String target) throws RedirectException { + if (object != null) { + throw new RedirectException(target + object); + } + } + + /** + * If the given phrase contains a Sone ID (optionally prefixed by + * “sone://”), returns said Sone ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is a Sone ID + * @return The Sone ID, or {@code null} + */ + private String getSoneId(String phrase) { + String soneId = phrase.startsWith("sone://") ? phrase.substring(7) : phrase; + return (webInterface.getCore().getSone(soneId, false) != null) ? soneId : null; + } + + /** + * If the given phrase contains a post ID (optionally prefixed by + * “post://”), returns said post ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is a post ID + * @return The post ID, or {@code null} + */ + private String getPostId(String phrase) { + String postId = phrase.startsWith("post://") ? phrase.substring(7) : phrase; + return (webInterface.getCore().getPost(postId, false) != null) ? postId : null; + } + + /** + * If the given phrase contains a reply ID (optionally prefixed by + * “reply://”), returns the ID of the post the reply belongs to, otherwise + * return {@code null}. + * + * @param phrase + * The phrase that maybe is a reply ID + * @return The reply’s post ID, or {@code null} + */ + private String getReplyPostId(String phrase) { + String replyId = phrase.startsWith("reply://") ? phrase.substring(8) : phrase; + return (webInterface.getCore().getReply(replyId, false) != null) ? webInterface.getCore().getReply(replyId, false).getPost().getId() : null; + } + + /** + * If the given phrase contains an album ID (optionally prefixed by + * “album://”), returns said album ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is an album ID + * @return The album ID, or {@code null} + */ + private String getAlbumId(String phrase) { + String albumId = phrase.startsWith("album://") ? phrase.substring(8) : phrase; + return (webInterface.getCore().getAlbum(albumId, false) != null) ? albumId : null; + } + + /** + * If the given phrase contains an image ID (optionally prefixed by + * “image://”), returns said image ID, otherwise return {@code null}. + * + * @param phrase + * The phrase that maybe is an image ID + * @return The image ID, or {@code null} + */ + private String getImageId(String phrase) { + String imageId = phrase.startsWith("image://") ? phrase.substring(8) : phrase; + return (webInterface.getCore().getImage(imageId, false) != null) ? imageId : null; + } + + /** * Converts a given object into a {@link String}. * * @param diff --combined src/main/java/net/pterodactylus/sone/web/WebInterface.java index b5f6dfb,00f8cf9..75b5f50 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@@ -103,18 -103,13 +103,13 @@@ import net.pterodactylus.sone.web.ajax. import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.sone.web.page.PageToadlet; import net.pterodactylus.sone.web.page.PageToadletFactory; - import net.pterodactylus.util.cache.Cache; - import net.pterodactylus.util.cache.CacheException; - import net.pterodactylus.util.cache.CacheItem; - import net.pterodactylus.util.cache.DefaultCacheItem; - import net.pterodactylus.util.cache.MemoryCache; - import net.pterodactylus.util.cache.ValueRetriever; import net.pterodactylus.util.collection.SetBuilder; import net.pterodactylus.util.collection.filter.Filters; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.notify.Notification; import net.pterodactylus.util.notify.NotificationManager; import net.pterodactylus.util.notify.TemplateNotification; + import net.pterodactylus.util.template.ClassPathTemplateProvider; import net.pterodactylus.util.template.CollectionSortFilter; import net.pterodactylus.util.template.ContainsFilter; import net.pterodactylus.util.template.DateFilter; @@@ -123,15 -118,13 +118,13 @@@ import net.pterodactylus.util.template. import net.pterodactylus.util.template.MatchFilter; import net.pterodactylus.util.template.ModFilter; import net.pterodactylus.util.template.PaginationFilter; - import net.pterodactylus.util.template.Provider; import net.pterodactylus.util.template.ReflectionAccessor; import net.pterodactylus.util.template.ReplaceFilter; import net.pterodactylus.util.template.StoreFilter; import net.pterodactylus.util.template.Template; - import net.pterodactylus.util.template.TemplateContext; import net.pterodactylus.util.template.TemplateContextFactory; - import net.pterodactylus.util.template.TemplateException; import net.pterodactylus.util.template.TemplateParser; + import net.pterodactylus.util.template.TemplateProvider; import net.pterodactylus.util.template.XmlFilter; import net.pterodactylus.util.thread.Ticker; import net.pterodactylus.util.version.Version; @@@ -222,7 -215,6 +215,6 @@@ public class WebInterface implements Co * @param sonePlugin * The Sone plugin */ - @SuppressWarnings("synthetic-access") public WebInterface(SonePlugin sonePlugin) { this.sonePlugin = sonePlugin; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); @@@ -261,8 -253,8 +253,8 @@@ templateContextFactory.addFilter("unique", new UniqueElementFilter()); templateContextFactory.addFilter("mod", new ModFilter()); templateContextFactory.addFilter("paginate", new PaginationFilter()); - templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER); - templateContextFactory.addProvider(new ClassPathTemplateProvider()); + templateContextFactory.addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER); + templateContextFactory.addProvider(new ClassPathTemplateProvider(WebInterface.class, "/templates/")); templateContextFactory.addTemplateObject("webInterface", this); templateContextFactory.addTemplateObject("formPassword", formPassword); @@@ -380,7 -372,7 +372,7 @@@ * currently logged in */ public Sone getCurrentSone(ToadletContext toadletContext, boolean create) { - Set localSones = getCore().getLocalSones(); + Collection localSones = getCore().getLocalSones(); if (localSones.size() == 1) { return localSones.iterator().next(); } @@@ -733,8 -725,8 +725,8 @@@ } /** - * Returns all {@link Core#isLocalSone(Sone) local Sone}s that are - * referenced by {@link SonePart}s in the given text (after parsing it using + * Returns all {@link Core#getLocalSones() local Sone}s that are referenced + * by {@link SonePart}s in the given text (after parsing it using * {@link SoneTextParser}). * * @param text @@@ -797,14 -789,15 +789,14 @@@ */ @Override public void newPostFound(Post post) { - boolean isLocal = getCore().isLocalSone(post.getSone()); - if (isLocal) { + if (post.getSone().isLocal()) { localPostNotification.add(post); } else { newPostNotification.add(post); } if (!hasFirstStartNotification()) { - notificationManager.addNotification(isLocal ? localPostNotification : newPostNotification); - if (!getMentionedSones(post.getText()).isEmpty() && !isLocal) { + notificationManager.addNotification(post.getSone().isLocal() ? localPostNotification : newPostNotification); + if (!getMentionedSones(post.getText()).isEmpty() && !post.getSone().isLocal()) { mentionNotification.add(post); notificationManager.addNotification(mentionNotification); } @@@ -818,14 -811,15 +810,14 @@@ */ @Override public void newReplyFound(PostReply reply) { - boolean isLocal = getCore().isLocalSone(reply.getSone()); - if (isLocal) { + if (reply.getSone().isLocal()) { localReplyNotification.add(reply); } else { newReplyNotification.add(reply); } if (!hasFirstStartNotification()) { - notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification); - if (!getMentionedSones(reply.getText()).isEmpty() && !isLocal && (reply.getPost().getSone() != null) && (reply.getTime() <= System.currentTimeMillis())) { + notificationManager.addNotification(reply.getSone().isLocal() ? localReplyNotification : newReplyNotification); + if (!getMentionedSones(reply.getText()).isEmpty() && !reply.getSone().isLocal() && (reply.getPost().getSone() != null) && (reply.getTime() <= System.currentTimeMillis())) { mentionNotification.add(reply.getPost()); notificationManager.addNotification(mentionNotification); } @@@ -1011,66 -1005,4 +1003,4 @@@ notificationManager.addNotification(imageInsertFailedNotification); } - /** - * Template provider implementation that uses - * {@link WebInterface#createReader(String)} to load templates for - * inclusion. - * - * @author David ‘Bombe’ Roden - */ - private class ClassPathTemplateProvider implements Provider { - - /** Cache for templates. */ - private final Cache templateCache = new MemoryCache(new ValueRetriever() { - - @Override - @SuppressWarnings("synthetic-access") - public CacheItem