+ /**
+ * Creates a new Sone for the given own identity.
+ *
+ * @param ownIdentity
+ * The own identity to create a Sone for
+ * @return The created Sone
+ */
+ public Sone createSone(OwnIdentity ownIdentity) {
+ try {
+ ownIdentity.addContext("Sone");
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.SEVERE, "Could not add “Sone” context to own identity: " + ownIdentity, wote1);
+ return null;
+ }
+ Sone sone = addLocalSone(ownIdentity);
+ return sone;
+ }
+
+ /**
+ * Adds the Sone of the given identity.
+ *
+ * @param identity
+ * The identity whose Sone to add
+ * @return The added or already existing Sone
+ */
+ public Sone addRemoteSone(Identity identity) {
+ if (identity == null) {
+ logger.log(Level.WARNING, "Given Identity is null!");
+ return null;
+ }
+ synchronized (remoteSones) {
+ final Sone sone = getRemoteSone(identity.getId()).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 (newSones) {
+ newSone = !knownSones.contains(sone.getId());
+ if (newSone) {
+ newSones.add(sone.getId());
+ }
+ }
+ if (newSone) {
+ coreListenerManager.fireNewSoneFound(sone);
+ }
+ }
+ remoteSones.put(identity.getId(), sone);
+ soneDownloader.addSone(sone);
+ setSoneStatus(sone, SoneStatus.unknown);
+ new Thread(new Runnable() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run() {
+ soneDownloader.fetchSone(sone);
+ }
+
+ }, "Sone Downloader").start();
+ return sone;
+ }
+ }
+
+ /**
+ * 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
+ * The target of the trust
+ * @return The trust relationship
+ */
+ public Trust getTrust(Sone origin, Sone target) {
+ if (!isLocalSone(origin)) {
+ logger.log(Level.WARNING, "Tried to get trust from remote Sone: %s", origin);
+ return null;
+ }
+ return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity());
+ }
+
+ /**
+ * Sets the trust value of the given origin Sone for the target Sone.
+ *
+ * @param origin
+ * The origin Sone
+ * @param target
+ * The target Sone
+ * @param trustValue
+ * The trust value (from {@code -100} to {@code 100})
+ */
+ public void setTrust(Sone origin, Sone target, int trustValue) {
+ Validation.begin().isNotNull("Trust Origin", origin).check().isInstanceOf("Trust Origin", origin.getIdentity(), OwnIdentity.class).isNotNull("Trust Target", target).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check();
+ try {
+ ((OwnIdentity) origin.getIdentity()).setTrust(target.getIdentity(), trustValue, options.getStringOption("TrustComment").get());
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not set trust for Sone: " + target, wote1);
+ }
+ }
+
+ /**
+ * Removes any trust assignment for the given target Sone.
+ *
+ * @param origin
+ * The trust origin
+ * @param target
+ * The trust target
+ */
+ public void removeTrust(Sone origin, Sone target) {
+ Validation.begin().isNotNull("Trust Origin", origin).isNotNull("Trust Target", target).check().isInstanceOf("Trust Origin Identity", origin.getIdentity(), OwnIdentity.class).check();
+ try {
+ ((OwnIdentity) origin.getIdentity()).removeTrust(target.getIdentity());
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not remove trust for Sone: " + target, wote1);
+ }
+ }
+
+ /**
+ * 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, options.getIntegerOption("PositiveTrust").get());
+ }
+
+ /**
+ * 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, options.getIntegerOption("NegativeTrust").get());
+ }
+
+ /**
+ * 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 stores Sone with the given Sone.
+ *
+ * @param sone
+ * The updated Sone
+ */
+ public void updateSone(Sone sone) {
+ if (hasSone(sone.getId())) {
+ boolean soneRescueMode = isLocalSone(sone) && 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 });
+ return;
+ }
+ synchronized (posts) {
+ if (!soneRescueMode) {
+ for (Post post : storedSone.getPosts()) {
+ posts.remove(post.getId());
+ if (!sone.getPosts().contains(post)) {
+ coreListenerManager.firePostRemoved(post);
+ }
+ }
+ }
+ synchronized (newPosts) {
+ for (Post post : sone.getPosts()) {
+ post.setSone(getSone(post.getSone().getId()));
+ if (!storedSone.getPosts().contains(post) && !knownPosts.contains(post.getId())) {
+ newPosts.add(post.getId());
+ coreListenerManager.fireNewPostFound(post);
+ }
+ posts.put(post.getId(), post);
+ }
+ }
+ }
+ synchronized (replies) {
+ if (!soneRescueMode) {
+ for (Reply reply : storedSone.getReplies()) {
+ replies.remove(reply.getId());
+ if (!sone.getReplies().contains(reply)) {
+ coreListenerManager.fireReplyRemoved(reply);
+ }
+ }
+ }
+ synchronized (newReplies) {
+ for (Reply reply : sone.getReplies()) {
+ reply.setSone(getSone(reply.getSone().getId()));
+ if (!storedSone.getReplies().contains(reply) && !knownReplies.contains(reply.getId())) {
+ newReplies.add(reply.getId());
+ coreListenerManager.fireNewReplyFound(reply);
+ }
+ replies.put(reply.getId(), reply);
+ }
+ }
+ }
+ 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 (Reply reply : sone.getReplies()) {
+ storedSone.addReply(reply);
+ }
+ for (String likedPostId : sone.getLikedPostIds()) {
+ storedSone.addLikedPostId(likedPostId);
+ }
+ for (String likedReplyId : sone.getLikedReplyIds()) {
+ storedSone.addLikedReplyId(likedReplyId);
+ }
+ } else {
+ storedSone.setPosts(sone.getPosts());
+ storedSone.setReplies(sone.getReplies());
+ storedSone.setLikePostIds(sone.getLikedPostIds());
+ storedSone.setLikeReplyIds(sone.getLikedReplyIds());
+ }
+ storedSone.setLatestEdition(sone.getLatestEdition());
+ }
+ }
+ }
+
+ /**
+ * Deletes the given Sone. This will remove the Sone from the
+ * {@link #getLocalSone(String) local Sones}, stops its {@link SoneInserter}
+ * and remove the context from its identity.
+ *
+ * @param sone
+ * The Sone to delete
+ */
+ public void deleteSone(Sone sone) {
+ if (!(sone.getIdentity() instanceof OwnIdentity)) {
+ logger.log(Level.WARNING, "Tried to delete Sone of non-own identity: %s", sone);
+ return;
+ }
+ synchronized (localSones) {
+ if (!localSones.containsKey(sone.getId())) {
+ logger.log(Level.WARNING, "Tried to delete non-local Sone: %s", sone);
+ return;
+ }
+ localSones.remove(sone.getId());
+ soneInserters.remove(sone).stop();
+ }
+ try {
+ ((OwnIdentity) sone.getIdentity()).removeContext("Sone");
+ ((OwnIdentity) sone.getIdentity()).removeProperty("Sone.LatestEdition");
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not remove context and properties from Sone: " + sone, wote1);
+ }
+ 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 {@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())) {
+ knownPosts.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.
+ *
+ * @param sone
+ * The Sone to load and update
+ */
+ public void loadSone(Sone sone) {
+ if (!isLocalSone(sone)) {
+ logger.log(Level.FINE, "Tried to load non-local Sone: %s", sone);
+ return;
+ }
+
+ /* 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();
+ 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;