X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=06a1467b1364c8a353932927388c1f3cfa22e2c2;hp=763b024e99a7670217cf3f24d9e5723e0aa9f342;hb=7dfc9c778882083632f916b178a241f06c2a08fe;hpb=5c658688d44f4f087f7a9367a19b94305fbb369a diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 763b024..06a1467 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -31,9 +31,12 @@ import java.util.logging.Logger; import net.pterodactylus.sone.core.Options.DefaultOption; import net.pterodactylus.sone.core.Options.Option; import net.pterodactylus.sone.core.Options.OptionWatcher; +import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Client; +import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Profile; +import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.wot.Identity; @@ -48,6 +51,7 @@ import net.pterodactylus.util.config.ConfigurationException; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.validation.Validation; +import net.pterodactylus.util.version.Version; import freenet.keys.FreenetURI; /** @@ -55,7 +59,7 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Core implements IdentityListener { +public class Core implements IdentityListener, UpdateListener { /** * Enumeration for the possible states of a {@link Sone}. @@ -83,6 +87,9 @@ public class Core implements IdentityListener { /** The options. */ private final Options options = new Options(); + /** The preferences. */ + private final Preferences preferences = new Preferences(options); + /** The core listener manager. */ private final CoreListenerManager coreListenerManager = new CoreListenerManager(this); @@ -101,6 +108,9 @@ public class Core implements IdentityListener { /** The Sone downloader. */ private final SoneDownloader soneDownloader; + /** The update checker. */ + private final UpdateChecker updateChecker; + /** Whether the core has been stopped. */ private volatile boolean stopped; @@ -150,9 +160,19 @@ public class Core implements IdentityListener { /** All known replies. */ private Set knownReplies = new HashSet(); + /** All bookmarked posts. */ + /* synchronize access on itself. */ + private Set bookmarkedPosts = new HashSet(); + /** Trusted identities, sorted by own identities. */ private Map> trustedIdentities = Collections.synchronizedMap(new HashMap>()); + /** All known albums. */ + private Map albums = new HashMap(); + + /** All known images. */ + private Map images = new HashMap(); + /** * Creates a new core. * @@ -168,6 +188,7 @@ public class Core implements IdentityListener { this.freenetInterface = freenetInterface; this.identityManager = identityManager; this.soneDownloader = new SoneDownloader(this, freenetInterface); + this.updateChecker = new UpdateChecker(freenetInterface); } // @@ -215,27 +236,26 @@ public class Core implements IdentityListener { * * @return The options of the core */ - public Options getOptions() { - return options; + public Preferences getPreferences() { + return preferences; } /** - * Returns whether the “Sone rescue mode” is currently activated. + * Returns the identity manager used by the core. * - * @return {@code true} if the “Sone rescue mode” is currently activated, - * {@code false} if it is not + * @return The identity manager */ - public boolean isSoneRescueMode() { - return options.getBooleanOption("SoneRescueMode").get(); + public IdentityManager getIdentityManager() { + return identityManager; } /** - * Returns the identity manager used by the core. + * Returns the update checker. * - * @return The identity manager + * @return The update checker */ - public IdentityManager getIdentityManager() { - return identityManager; + public UpdateChecker getUpdateChecker() { + return updateChecker; } /** @@ -400,6 +420,7 @@ public class Core implements IdentityListener { if ((sone == null) && create) { sone = new Sone(id); localSones.put(id, sone); + setSoneStatus(sone, SoneStatus.unknown); } return sone; } @@ -443,6 +464,7 @@ public class Core implements IdentityListener { if ((sone == null) && create) { sone = new Sone(id); remoteSones.put(id, sone); + setSoneStatus(sone, SoneStatus.unknown); } return sone; } @@ -477,22 +499,15 @@ public class Core implements IdentityListener { } /** - * Returns whether the given Sone is a new Sone. After this check, the Sone - * is marked as known, i.e. a second call with the same parameters will - * always yield {@code false}. + * Returns whether the Sone with the given ID is a new Sone. * - * @param sone - * The sone to check for + * @param soneId + * The ID of the sone to check for * @return {@code true} if the given Sone is new, false otherwise */ - public boolean isNewSone(Sone sone) { + public boolean isNewSone(String soneId) { synchronized (newSones) { - boolean isNew = !knownSones.contains(sone.getId()) && newSones.remove(sone.getId()); - knownSones.add(sone.getId()); - if (isNew) { - coreListenerManager.fireMarkSoneKnown(sone); - } - return isNew; + return !knownSones.contains(soneId) && newSones.contains(soneId); } } @@ -554,8 +569,7 @@ public class Core implements IdentityListener { } /** - * Returns whether the given post ID is new. After this method returns it is - * marked a known post ID. + * Returns whether the given post ID is new. * * @param postId * The post ID @@ -563,32 +577,8 @@ public class Core implements IdentityListener { * otherwise */ public boolean isNewPost(String postId) { - return isNewPost(postId, true); - } - - /** - * Returns whether the given post ID is new. If {@code markAsKnown} is - * {@code true} then after this method returns the post ID is marked a known - * post ID. - * - * @param postId - * The post ID - * @param markAsKnown - * {@code true} to mark the post ID as known, {@code false} to - * not to mark it as known - * @return {@code true} if the post is considered to be new, {@code false} - * otherwise - */ - public boolean isNewPost(String postId, boolean markAsKnown) { synchronized (newPosts) { - boolean isNew = !knownPosts.contains(postId) && newPosts.contains(postId); - if (markAsKnown) { - Post post = getPost(postId, false); - if (post != null) { - markPostKnown(post); - } - } - return isNew; + return !knownPosts.contains(postId) && newPosts.contains(postId); } } @@ -657,30 +647,8 @@ public class Core implements IdentityListener { * otherwise */ public boolean isNewReply(String replyId) { - return isNewReply(replyId, true); - } - - /** - * Returns whether the reply with the given ID is new. - * - * @param replyId - * The ID of the reply to check - * @param markAsKnown - * {@code true} to mark the reply as known, {@code false} to not - * to mark it as known - * @return {@code true} if the reply is considered to be new, {@code false} - * otherwise - */ - public boolean isNewReply(String replyId, boolean markAsKnown) { synchronized (newReplies) { - boolean isNew = !knownReplies.contains(replyId) && newReplies.contains(replyId); - if (markAsKnown) { - Reply reply = getReply(replyId, false); - if (reply != null) { - markReplyKnown(reply); - } - } - return isNew; + return !knownReplies.contains(replyId) && newReplies.contains(replyId); } } @@ -718,6 +686,119 @@ public class Core implements IdentityListener { return sones; } + /** + * Returns whether the given post is bookmarked. + * + * @param post + * The post to check + * @return {@code true} if the given post is bookmarked, {@code false} + * otherwise + */ + public boolean isBookmarked(Post post) { + return isPostBookmarked(post.getId()); + } + + /** + * Returns whether the post with the given ID is bookmarked. + * + * @param id + * The ID of the post to check + * @return {@code true} if the post with the given ID is bookmarked, + * {@code false} otherwise + */ + public boolean isPostBookmarked(String id) { + synchronized (bookmarkedPosts) { + return bookmarkedPosts.contains(id); + } + } + + /** + * Returns all currently known bookmarked posts. + * + * @return All bookmarked posts + */ + public Set getBookmarkedPosts() { + Set posts = new HashSet(); + synchronized (bookmarkedPosts) { + for (String bookmarkedPostId : bookmarkedPosts) { + Post post = getPost(bookmarkedPostId, false); + if (post != null) { + posts.add(post); + } + } + } + return posts; + } + + /** + * Returns the album with the given ID, creating a new album if no album + * with the given ID can be found. + * + * @param albumId + * The ID of the album + * @return The album with the given ID + */ + public Album getAlbum(String albumId) { + return getAlbum(albumId, true); + } + + /** + * Returns the album with the given ID, optionally creating a new album if + * an album with the given ID can not be found. + * + * @param albumId + * The ID of the album + * @param create + * {@code true} to create a new album if none exists for the + * given ID + * @return The album with the given ID, or {@code null} if no album with the + * given ID exists and {@code create} is {@code false} + */ + public Album getAlbum(String albumId, boolean create) { + synchronized (albums) { + Album album = albums.get(albumId); + if (create && (album == null)) { + album = new Album(albumId); + albums.put(albumId, album); + } + return album; + } + } + + /** + * Returns the image with the given ID, creating it if necessary. + * + * @param imageId + * The ID of the image + * @return The image with the given ID + */ + public Image getImage(String imageId) { + return getImage(imageId, true); + } + + /** + * Returns the image with the given ID, optionally creating it if it does + * not exist. + * + * @param imageId + * The ID of the image + * @param create + * {@code true} to create an image if none exists with the given + * ID + * @return The image with the given ID, or {@code null} if none exists and + * none was created + */ + public Image getImage(String imageId, boolean create) { + synchronized (images) { + Image image = images.get(imageId); + if (create && (image == null)) { + image = new Image(imageId); + images.put(imageId, image); + } + return image; + } + } + // // ACTIONS // @@ -804,7 +885,7 @@ public class Core implements IdentityListener { soneInserters.put(sone, soneInserter); setSoneStatus(sone, SoneStatus.idle); loadSone(sone); - if (!isSoneRescueMode()) { + if (!preferences.isSoneRescueMode()) { soneInserter.start(); } new Thread(new Runnable() { @@ -812,7 +893,7 @@ public class Core implements IdentityListener { @Override @SuppressWarnings("synthetic-access") public void run() { - if (!isSoneRescueMode()) { + if (!preferences.isSoneRescueMode()) { soneDownloader.fetchSone(sone); return; } @@ -820,7 +901,7 @@ public class Core implements IdentityListener { coreListenerManager.fireRescuingSone(sone); lockSone(sone); long edition = sone.getLatestEdition(); - while (!stopped && (edition >= 0) && isSoneRescueMode()) { + while (!stopped && (edition >= 0) && preferences.isSoneRescueMode()) { logger.log(Level.FINE, "Downloading edition " + edition + "…"); soneDownloader.fetchSone(sone, sone.getRequestUri().setKeyType("SSK").setDocName("Sone-" + edition)); --edition; @@ -899,8 +980,10 @@ public class Core implements IdentityListener { } /** - * Retrieves the trust relationship from the origin to the target. + * Retrieves the trust relationship from the origin to the target. If the + * trust relationship can not be retrieved, {@code null} is returned. * + * @see Identity#getTrust(OwnIdentity) * @param origin * The origin of the trust tree * @param target @@ -912,12 +995,7 @@ public class Core implements IdentityListener { logger.log(Level.WARNING, "Tried to get trust from remote Sone: %s", origin); return null; } - try { - return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity()); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not get trust for Sone: " + target, wote1); - return null; - } + return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity()); } /** @@ -933,7 +1011,7 @@ public class Core implements IdentityListener { public void setTrust(Sone origin, Sone target, int trustValue) { Validation.begin().isNotNull("Trust Origin", origin).check().isInstanceOf("Trust Origin", origin.getIdentity(), OwnIdentity.class).isNotNull("Trust Target", target).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check(); try { - ((OwnIdentity) origin.getIdentity()).setTrust(target.getIdentity(), trustValue, "Set from Sone Web Interface"); + ((OwnIdentity) origin.getIdentity()).setTrust(target.getIdentity(), trustValue, preferences.getTrustComment()); } catch (WebOfTrustException wote1) { logger.log(Level.WARNING, "Could not set trust for Sone: " + target, wote1); } @@ -965,7 +1043,7 @@ public class Core implements IdentityListener { * The trust target */ public void trustSone(Sone origin, Sone target) { - setTrust(origin, target, options.getIntegerOption("PositiveTrust").get()); + setTrust(origin, target, preferences.getPositiveTrust()); } /** @@ -977,7 +1055,7 @@ public class Core implements IdentityListener { * The trust target */ public void distrustSone(Sone origin, Sone target) { - setTrust(origin, target, options.getIntegerOption("NegativeTrust").get()); + setTrust(origin, target, preferences.getNegativeTrust()); } /** @@ -1000,7 +1078,7 @@ public class Core implements IdentityListener { */ public void updateSone(Sone sone) { if (hasSone(sone.getId())) { - boolean soneRescueMode = isLocalSone(sone) && isSoneRescueMode(); + boolean soneRescueMode = isLocalSone(sone) && preferences.isSoneRescueMode(); Sone storedSone = getSone(sone.getId()); if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) { logger.log(Level.FINE, "Downloaded Sone %s is not newer than stored Sone %s.", new Object[] { sone, storedSone }); @@ -1015,10 +1093,11 @@ public class Core implements IdentityListener { } } } + List storedPosts = storedSone.getPosts(); synchronized (newPosts) { for (Post post : sone.getPosts()) { - post.setSone(getSone(post.getSone().getId())); - if (!storedSone.getPosts().contains(post) && !knownPosts.contains(post.getId())) { + post.setSone(storedSone); + if (!storedPosts.contains(post) && !knownPosts.contains(post.getId())) { newPosts.add(post.getId()); coreListenerManager.fireNewPostFound(post); } @@ -1035,10 +1114,11 @@ public class Core implements IdentityListener { } } } + Set storedReplies = storedSone.getReplies(); synchronized (newReplies) { for (Reply reply : sone.getReplies()) { - reply.setSone(getSone(reply.getSone().getId())); - if (!storedSone.getReplies().contains(reply) && !knownReplies.contains(reply.getId())) { + reply.setSone(storedSone); + if (!storedReplies.contains(reply) && !knownReplies.contains(reply.getId())) { newReplies.add(reply.getId()); coreListenerManager.fireNewReplyFound(reply); } @@ -1111,6 +1191,23 @@ public class Core implements IdentityListener { } /** + * Marks the given Sone as known. If the Sone was {@link #isNewPost(String) + * new} before, a {@link CoreListener#markSoneKnown(Sone)} event is fired. + * + * @param sone + * The Sone to mark as known + */ + public void markSoneKnown(Sone sone) { + synchronized (newSones) { + if (newSones.remove(sone.getId())) { + knownSones.add(sone.getId()); + coreListenerManager.fireMarkSoneKnown(sone); + saveConfiguration(); + } + } + } + + /** * Loads and updates the given Sone from the configuration. If any error is * encountered, loading is aborted and the given Sone is not changed. * @@ -1141,6 +1238,17 @@ public class Core implements IdentityListener { profile.setBirthMonth(configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").getValue(null)); profile.setBirthYear(configuration.getIntValue(sonePrefix + "/Profile/BirthYear").getValue(null)); + /* load profile fields. */ + while (true) { + String fieldPrefix = sonePrefix + "/Profile/Fields/" + profile.getFields().size(); + String fieldName = configuration.getStringValue(fieldPrefix + "/Name").getValue(null); + if (fieldName == null) { + break; + } + String fieldValue = configuration.getStringValue(fieldPrefix + "/Value").getValue(""); + profile.addField(fieldName).setValue(fieldValue); + } + /* load posts. */ Set posts = new HashSet(); while (true) { @@ -1211,6 +1319,38 @@ public class Core implements IdentityListener { friends.add(friendId); } + /* load albums. */ + List topLevelAlbums = new ArrayList(); + while (true) { + String albumPrefix = sonePrefix + "/Albums/" + albums.size(); + String albumId = configuration.getStringValue(albumPrefix + "/ID").getValue(null); + if (albumId == null) { + break; + } + String albumTitle = configuration.getStringValue(albumPrefix + "/Title").getValue(null); + String albumDescription = configuration.getStringValue(albumPrefix + "/Description").getValue(null); + String albumParentId = configuration.getStringValue(albumPrefix + "/Parent").getValue(null); + if ((albumTitle == null) || (albumDescription == null)) { + logger.log(Level.WARNING, "Invalid album found, aborting load!"); + return; + } + Album album = getAlbum(albumId).setSone(sone).setTitle(albumTitle).setDescription(albumDescription); + if (albumParentId != null) { + Album parentAlbum = getAlbum(albumParentId, false); + if (parentAlbum == null) { + logger.log(Level.WARNING, "Invalid parent album ID: " + albumParentId); + return; + } + parentAlbum.addAlbum(album); + } else { + topLevelAlbums.add(album); + } + } + + /* load options. */ + sone.getOptions().addBooleanOption("AutoFollow", new DefaultOption(false)); + sone.getOptions().getBooleanOption("AutoFollow").set(configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").getValue(null)); + /* if we’re still here, Sone was loaded successfully. */ synchronized (sone) { sone.setTime(soneTime); @@ -1220,6 +1360,7 @@ public class Core implements IdentityListener { sone.setLikePostIds(likedPostIds); sone.setLikeReplyIds(likedReplyIds); sone.setFriends(friends); + sone.setAlbums(topLevelAlbums); soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint); } synchronized (newSones) { @@ -1274,6 +1415,15 @@ public class Core implements IdentityListener { configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").setValue(profile.getBirthMonth()); configuration.getIntValue(sonePrefix + "/Profile/BirthYear").setValue(profile.getBirthYear()); + /* save profile fields. */ + int fieldCounter = 0; + for (Field profileField : profile.getFields()) { + String fieldPrefix = sonePrefix + "/Profile/Fields/" + fieldCounter++; + configuration.getStringValue(fieldPrefix + "/Name").setValue(profileField.getName()); + configuration.getStringValue(fieldPrefix + "/Value").setValue(profileField.getValue()); + } + configuration.getStringValue(sonePrefix + "/Profile/Fields/" + fieldCounter + "/Name").setValue(null); + /* save posts. */ int postCounter = 0; for (Post post : sone.getPosts()) { @@ -1317,6 +1467,31 @@ public class Core implements IdentityListener { } configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); + /* save albums. first, collect in a flat structure, top-level first. */ + List albums = new ArrayList(); + albums.addAll(sone.getAlbums()); + int lastAlbumIndex = 0; + while (lastAlbumIndex < albums.size()) { + int previousAlbumCount = albums.size(); + for (Album album : new ArrayList(albums.subList(lastAlbumIndex, albums.size()))) { + albums.addAll(album.getAlbums()); + } + lastAlbumIndex = previousAlbumCount; + } + + int albumCounter = 0; + for (Album album : albums) { + String albumPrefix = sonePrefix + "/Albums/" + albumCounter++; + configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId()); + configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle()); + configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription()); + configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent() == null ? null : album.getParent().getId()); + } + configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null); + + /* save options. */ + configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").setValue(sone.getOptions().getBooleanOption("AutoFollow").getReal()); + configuration.save(); logger.log(Level.INFO, "Sone %s saved.", sone); } catch (ConfigurationException ce1) { @@ -1440,6 +1615,50 @@ public class Core implements IdentityListener { } /** + * Bookmarks the given post. + * + * @param post + * The post to bookmark + */ + public void bookmark(Post post) { + bookmarkPost(post.getId()); + } + + /** + * Bookmarks the post with the given ID. + * + * @param id + * The ID of the post to bookmark + */ + public void bookmarkPost(String id) { + synchronized (bookmarkedPosts) { + bookmarkedPosts.add(id); + } + } + + /** + * Removes the given post from the bookmarks. + * + * @param post + * The post to unbookmark + */ + public void unbookmark(Post post) { + unbookmarkPost(post.getId()); + } + + /** + * Removes the post with the given ID from the bookmarks. + * + * @param id + * The ID of the post to unbookmark + */ + public void unbookmarkPost(String id) { + synchronized (bookmarkedPosts) { + bookmarkedPosts.remove(id); + } + } + + /** * Creates a new reply. * * @param sone @@ -1521,10 +1740,47 @@ public class Core implements IdentityListener { } /** + * Creates a new top-level album for the given Sone. + * + * @param sone + * The Sone to create the album for + * @return The new album + */ + public Album createAlbum(Sone sone) { + return createAlbum(sone, null); + } + + /** + * Creates a new album for the given Sone. + * + * @param sone + * The Sone to create the album for + * @param parent + * The parent of the album (may be {@code null} to create a + * top-level album) + * @return The new album + */ + public Album createAlbum(Sone sone, Album parent) { + Album album = new Album(); + synchronized (albums) { + albums.put(album.getId(), album); + } + album.setSone(sone); + if (parent != null) { + parent.addAlbum(album); + } else { + sone.addAlbum(album); + } + return album; + } + + /** * Starts the core. */ public void start() { loadConfiguration(); + updateChecker.addUpdateListener(this); + updateChecker.start(); } /** @@ -1536,6 +1792,8 @@ public class Core implements IdentityListener { soneInserter.stop(); } } + updateChecker.stop(); + updateChecker.removeUpdateListener(this); soneDownloader.stop(); saveConfiguration(); stopped = true; @@ -1555,9 +1813,11 @@ public class Core implements IdentityListener { /* store the options first. */ try { + configuration.getIntValue("Option/ConfigurationVersion").setValue(0); configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal()); configuration.getIntValue("Option/PositiveTrust").setValue(options.getIntegerOption("PositiveTrust").getReal()); configuration.getIntValue("Option/NegativeTrust").setValue(options.getIntegerOption("NegativeTrust").getReal()); + configuration.getStringValue("Option/TrustComment").setValue(options.getStringOption("TrustComment").getReal()); configuration.getBooleanValue("Option/SoneRescueMode").setValue(options.getBooleanOption("SoneRescueMode").getReal()); configuration.getBooleanValue("Option/ClearOnNextRestart").setValue(options.getBooleanOption("ClearOnNextRestart").getReal()); configuration.getBooleanValue("Option/ReallyClearOnNextRestart").setValue(options.getBooleanOption("ReallyClearOnNextRestart").getReal()); @@ -1589,6 +1849,15 @@ public class Core implements IdentityListener { configuration.getStringValue("KnownReplies/" + replyCounter + "/ID").setValue(null); } + /* save bookmarked posts. */ + int bookmarkedPostCounter = 0; + synchronized (bookmarkedPosts) { + for (String bookmarkedPostId : bookmarkedPosts) { + configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").setValue(bookmarkedPostId); + } + } + configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").setValue(null); + /* now save it. */ configuration.save(); @@ -1621,6 +1890,7 @@ public class Core implements IdentityListener { })); options.addIntegerOption("PositiveTrust", new DefaultOption(75)); options.addIntegerOption("NegativeTrust", new DefaultOption(-100)); + options.addStringOption("TrustComment", new DefaultOption("Set from Sone Web Interface")); options.addBooleanOption("SoneRescueMode", new DefaultOption(false)); options.addBooleanOption("ClearOnNextRestart", new DefaultOption(false)); options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption(false)); @@ -1639,6 +1909,7 @@ public class Core implements IdentityListener { options.getIntegerOption("InsertionDelay").set(configuration.getIntValue("Option/InsertionDelay").getValue(null)); options.getIntegerOption("PositiveTrust").set(configuration.getIntValue("Option/PositiveTrust").getValue(null)); options.getIntegerOption("NegativeTrust").set(configuration.getIntValue("Option/NegativeTrust").getValue(null)); + options.getStringOption("TrustComment").set(configuration.getStringValue("Option/TrustComment").getValue(null)); options.getBooleanOption("SoneRescueMode").set(configuration.getBooleanValue("Option/SoneRescueMode").getValue(null)); /* load known Sones. */ @@ -1677,6 +1948,18 @@ public class Core implements IdentityListener { } } + /* load bookmarked posts. */ + int bookmarkedPostCounter = 0; + while (true) { + String bookmarkedPostId = configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").getValue(null); + if (bookmarkedPostId == null) { + break; + } + synchronized (bookmarkedPosts) { + bookmarkedPosts.add(bookmarkedPostId); + } + } + } /** @@ -1743,6 +2026,7 @@ public class Core implements IdentityListener { public void run() { Sone sone = getRemoteSone(identity.getId()); sone.setIdentity(identity); + soneDownloader.addSone(sone); soneDownloader.fetchSone(sone); } }).start(); @@ -1756,4 +2040,203 @@ public class Core implements IdentityListener { trustedIdentities.get(ownIdentity).remove(identity); } + // + // INTERFACE UpdateListener + // + + /** + * {@inheritDoc} + */ + @Override + public void updateFound(Version version, long releaseTime, long latestEdition) { + coreListenerManager.fireUpdateFound(version, releaseTime, latestEdition); + } + + /** + * Convenience interface for external classes that want to access the core’s + * configuration. + * + * @author David ‘Bombe’ Roden + */ + public static class Preferences { + + /** The wrapped options. */ + private final Options options; + + /** + * Creates a new preferences object wrapped around the given options. + * + * @param options + * The options to wrap + */ + public Preferences(Options options) { + this.options = options; + } + + /** + * Returns the insertion delay. + * + * @return The insertion delay + */ + public int getInsertionDelay() { + return options.getIntegerOption("InsertionDelay").get(); + } + + /** + * Sets the insertion delay + * + * @param insertionDelay + * The new insertion delay, or {@code null} to restore it to + * the default value + * @return This preferences + */ + public Preferences setInsertionDelay(Integer insertionDelay) { + options.getIntegerOption("InsertionDelay").set(insertionDelay); + return this; + } + + /** + * Returns the positive trust. + * + * @return The positive trust + */ + public int getPositiveTrust() { + return options.getIntegerOption("PositiveTrust").get(); + } + + /** + * Sets the positive trust. + * + * @param positiveTrust + * The new positive trust, or {@code null} to restore it to + * the default vlaue + * @return This preferences + */ + public Preferences setPositiveTrust(Integer positiveTrust) { + options.getIntegerOption("PositiveTrust").set(positiveTrust); + return this; + } + + /** + * Returns the negative trust. + * + * @return The negative trust + */ + public int getNegativeTrust() { + return options.getIntegerOption("NegativeTrust").get(); + } + + /** + * Sets the negative trust. + * + * @param negativeTrust + * The negative trust, or {@code null} to restore it to the + * default value + * @return The preferences + */ + public Preferences setNegativeTrust(Integer negativeTrust) { + options.getIntegerOption("NegativeTrust").set(negativeTrust); + return this; + } + + /** + * Returns the trust comment. This is the comment that is set in the web + * of trust when a trust value is assigned to an identity. + * + * @return The trust comment + */ + public String getTrustComment() { + return options.getStringOption("TrustComment").get(); + } + + /** + * Sets the trust comment. + * + * @param trustComment + * The trust comment, or {@code null} to restore it to the + * default value + * @return This preferences + */ + public Preferences setTrustComment(String trustComment) { + options.getStringOption("TrustComment").set(trustComment); + return this; + } + + /** + * Returns whether the rescue mode is active. + * + * @return {@code true} if the rescue mode is active, {@code false} + * otherwise + */ + public boolean isSoneRescueMode() { + return options.getBooleanOption("SoneRescueMode").get(); + } + + /** + * Sets whether the rescue mode is active. + * + * @param soneRescueMode + * {@code true} if the rescue mode is active, {@code false} + * otherwise + * @return This preferences + */ + public Preferences setSoneRescueMode(Boolean soneRescueMode) { + options.getBooleanOption("SoneRescueMode").set(soneRescueMode); + return this; + } + + /** + * Returns whether Sone should clear its settings on the next restart. + * In order to be effective, {@link #isReallyClearOnNextRestart()} needs + * to return {@code true} as well! + * + * @return {@code true} if Sone should clear its settings on the next + * restart, {@code false} otherwise + */ + public boolean isClearOnNextRestart() { + return options.getBooleanOption("ClearOnNextRestart").get(); + } + + /** + * Sets whether Sone will clear its settings on the next restart. + * + * @param clearOnNextRestart + * {@code true} if Sone should clear its settings on the next + * restart, {@code false} otherwise + * @return This preferences + */ + public Preferences setClearOnNextRestart(Boolean clearOnNextRestart) { + options.getBooleanOption("ClearOnNextRestart").set(clearOnNextRestart); + return this; + } + + /** + * Returns whether Sone should really clear its settings on next + * restart. This is a confirmation option that needs to be set in + * addition to {@link #isClearOnNextRestart()} in order to clear Sone’s + * settings on the next restart. + * + * @return {@code true} if Sone should really clear its settings on the + * next restart, {@code false} otherwise + */ + public boolean isReallyClearOnNextRestart() { + return options.getBooleanOption("ReallyClearOnNextRestart").get(); + } + + /** + * Sets whether Sone should really clear its settings on the next + * restart. + * + * @param reallyClearOnNextRestart + * {@code true} if Sone should really clear its settings on + * the next restart, {@code false} otherwise + * @return This preferences + */ + public Preferences setReallyClearOnNextRestart(Boolean reallyClearOnNextRestart) { + options.getBooleanOption("ReallyClearOnNextRestart").set(reallyClearOnNextRestart); + return this; + } + + } + }