+ /**
+ * Assigns the configured positive trust value for the given target.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void trustSone(Sone origin, Sone target) {
+ setTrust(origin, target, preferences.getPositiveTrust());
+ }
+
+ /**
+ * Assigns the configured negative trust value for the given target.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void distrustSone(Sone origin, Sone target) {
+ setTrust(origin, target, preferences.getNegativeTrust());
+ }
+
+ /**
+ * Removes the trust assignment for the given target.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void untrustSone(Sone origin, Sone target) {
+ removeTrust(origin, target);
+ }
+
+ /**
+ * Updates the stored Sone with the given Sone.
+ *
+ * @param sone
+ * The updated Sone
+ */
+ public void updateSone(Sone sone) {
+ updateSone(sone, false);
+ }
+
+ /**
+ * Updates the stored Sone with the given Sone. If {@code soneRescueMode} is
+ * {@code true}, an older Sone than the current Sone can be given to restore
+ * an old state.
+ *
+ * @param sone
+ * The Sone to update
+ * @param soneRescueMode
+ * {@code true} if the stored Sone should be updated regardless
+ * of the age of the given Sone
+ */
+ public void updateSone(Sone sone, boolean soneRescueMode) {
+ if (hasSone(sone.getId())) {
+ Sone storedSone = getSone(sone.getId());
+ if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) {
+ logger.log(Level.FINE, String.format("Downloaded Sone %s is not newer than stored Sone %s.", sone, storedSone));
+ return;
+ }
+ synchronized (posts) {
+ if (!soneRescueMode) {
+ for (Post post : storedSone.getPosts()) {
+ posts.remove(post.getId());
+ if (!sone.getPosts().contains(post)) {
+ eventBus.post(new PostRemovedEvent(post));
+ }
+ }
+ }
+ List<Post> storedPosts = storedSone.getPosts();
+ synchronized (knownPosts) {
+ for (Post post : sone.getPosts()) {
+ PostBuilder postBuilder = postBuilderFactory.newPostBuilder();
+ postBuilder.copyPost(post).from(storedSone);
+ Post newPost = postBuilder.build().setKnown(knownPosts.contains(post.getId()));
+ if (!storedPosts.contains(newPost)) {
+ if (newPost.getTime() < getSoneFollowingTime(sone)) {
+ knownPosts.add(newPost.getId());
+ newPost.setKnown(true);
+ } else if (!knownPosts.contains(newPost.getId())) {
+ eventBus.post(new NewPostFoundEvent(newPost));
+ }
+ }
+ posts.put(newPost.getId(), newPost);
+ }
+ }
+ }
+ synchronized (replies) {
+ if (!soneRescueMode) {
+ for (PostReply reply : storedSone.getReplies()) {
+ replies.remove(reply.getId());
+ if (!sone.getReplies().contains(reply)) {
+ eventBus.post(new PostReplyRemovedEvent(reply));
+ }
+ }
+ }
+ Set<PostReply> storedReplies = storedSone.getReplies();
+ synchronized (knownReplies) {
+ for (PostReply reply : sone.getReplies()) {
+ reply.setSone(storedSone).setKnown(knownReplies.contains(reply.getId()));
+ if (!storedReplies.contains(reply)) {
+ if (reply.getTime() < getSoneFollowingTime(sone)) {
+ knownReplies.add(reply.getId());
+ reply.setKnown(true);
+ } else if (!knownReplies.contains(reply.getId())) {
+ eventBus.post(new NewPostReplyFoundEvent(reply));
+ }
+ }
+ replies.put(reply.getId(), reply);
+ }
+ }
+ }
+ synchronized (albums) {
+ synchronized (images) {
+ for (Album album : storedSone.getAlbums()) {
+ albums.remove(album.getId());
+ for (Image image : album.getImages()) {
+ images.remove(image.getId());
+ }
+ }
+ for (Album album : sone.getAlbums()) {
+ albums.put(album.getId(), album);
+ for (Image image : album.getImages()) {
+ images.put(image.getId(), image);
+ }
+ }
+ }
+ }
+ synchronized (storedSone) {
+ if (!soneRescueMode || (sone.getTime() > storedSone.getTime())) {
+ storedSone.setTime(sone.getTime());
+ }
+ storedSone.setClient(sone.getClient());
+ storedSone.setProfile(sone.getProfile());
+ if (soneRescueMode) {
+ for (Post post : sone.getPosts()) {
+ storedSone.addPost(post);
+ }
+ for (PostReply reply : sone.getReplies()) {
+ storedSone.addReply(reply);
+ }
+ for (String likedPostId : sone.getLikedPostIds()) {
+ storedSone.addLikedPostId(likedPostId);
+ }
+ for (String likedReplyId : sone.getLikedReplyIds()) {
+ storedSone.addLikedReplyId(likedReplyId);
+ }
+ for (Album album : sone.getAlbums()) {
+ storedSone.addAlbum(album);
+ }
+ } else {
+ storedSone.setPosts(sone.getPosts());
+ storedSone.setReplies(sone.getReplies());
+ storedSone.setLikePostIds(sone.getLikedPostIds());
+ storedSone.setLikeReplyIds(sone.getLikedReplyIds());
+ storedSone.setAlbums(sone.getAlbums());
+ }
+ storedSone.setLatestEdition(sone.getLatestEdition());
+ }
+ }
+ }
+
+ /**
+ * Deletes the given Sone. This will remove the Sone from the
+ * {@link #getLocalSones() local Sones}, stop its {@link SoneInserter} and
+ * remove the context from its identity.
+ *
+ * @param sone
+ * The Sone to delete
+ */
+ public void deleteSone(Sone sone) {
+ if (!(sone.getIdentity() instanceof OwnIdentity)) {
+ logger.log(Level.WARNING, String.format("Tried to delete Sone of non-own identity: %s", sone));
+ return;
+ }
+ synchronized (sones) {
+ if (!getLocalSones().contains(sone)) {
+ logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone));
+ return;
+ }
+ sones.remove(sone.getId());
+ SoneInserter soneInserter = soneInserters.remove(sone);
+ soneInserter.stop();
+ }
+ webOfTrustUpdater.removeContext((OwnIdentity) sone.getIdentity(), "Sone");
+ webOfTrustUpdater.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition");
+ try {
+ configuration.getLongValue("Sone/" + sone.getId() + "/Time").setValue(null);
+ } catch (ConfigurationException ce1) {
+ logger.log(Level.WARNING, "Could not remove Sone from configuration!", ce1);
+ }
+ }
+
+ /**
+ * Marks the given Sone as known. If the Sone was not {@link Post#isKnown()
+ * known} before, a {@link MarkSoneKnownEvent} is fired.
+ *
+ * @param sone
+ * The Sone to mark as known
+ */
+ public void markSoneKnown(Sone sone) {
+ if (!sone.isKnown()) {
+ sone.setKnown(true);
+ synchronized (knownSones) {
+ knownSones.add(sone.getId());
+ }
+ eventBus.post(new MarkSoneKnownEvent(sone));
+ touchConfiguration();
+ }
+ }
+
+ /**
+ * Loads and updates the given Sone from the configuration. If any error is
+ * encountered, loading is aborted and the given Sone is not changed.
+ *
+ * @param sone
+ * The Sone to load and update
+ */
+ public void loadSone(Sone sone) {
+ if (!sone.isLocal()) {
+ logger.log(Level.FINE, String.format("Tried to load non-local Sone: %s", sone));
+ return;
+ }
+
+ /* initialize options. */
+ sone.getOptions().addBooleanOption("AutoFollow", new DefaultOption<Boolean>(false));
+ sone.getOptions().addBooleanOption("EnableSoneInsertNotifications", new DefaultOption<Boolean>(false));
+ sone.getOptions().addBooleanOption("ShowNotification/NewSones", new DefaultOption<Boolean>(true));
+ sone.getOptions().addBooleanOption("ShowNotification/NewPosts", new DefaultOption<Boolean>(true));
+ sone.getOptions().addBooleanOption("ShowNotification/NewReplies", new DefaultOption<Boolean>(true));
+ sone.getOptions().addEnumOption("ShowCustomAvatars", new DefaultOption<ShowCustomAvatars>(ShowCustomAvatars.NEVER));
+
+ /* load Sone. */
+ String sonePrefix = "Sone/" + sone.getId();
+ Long soneTime = configuration.getLongValue(sonePrefix + "/Time").getValue(null);
+ if (soneTime == null) {
+ logger.log(Level.INFO, "Could not load Sone because no Sone has been saved.");
+ return;
+ }
+ String lastInsertFingerprint = configuration.getStringValue(sonePrefix + "/LastInsertFingerprint").getValue("");
+
+ /* load profile. */
+ Profile profile = new Profile(sone);
+ profile.setFirstName(configuration.getStringValue(sonePrefix + "/Profile/FirstName").getValue(null));
+ profile.setMiddleName(configuration.getStringValue(sonePrefix + "/Profile/MiddleName").getValue(null));
+ profile.setLastName(configuration.getStringValue(sonePrefix + "/Profile/LastName").getValue(null));
+ profile.setBirthDay(configuration.getIntValue(sonePrefix + "/Profile/BirthDay").getValue(null));
+ 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. */