+ Set<Sone> allSones = new HashSet<Sone>();
+ allSones.addAll(getLocalSones());
+ allSones.addAll(getRemoteSones());
+ return allSones;
+ }
+
+ /**
+ * Returns the Sone with the given ID, regardless whether it’s local or
+ * remote.
+ *
+ * @param id
+ * The ID of the Sone to get
+ * @return The Sone with the given ID, or {@code null} if there is no such
+ * Sone
+ */
+ public Sone getSone(String id) {
+ return getSone(id, true);
+ }
+
+ /**
+ * Returns the Sone with the given ID, regardless whether it’s local or
+ * remote.
+ *
+ * @param id
+ * The ID of the Sone to get
+ * @param create
+ * {@code true} to create a new Sone if none exists,
+ * {@code false} to return {@code null} if a Sone with the given
+ * ID does not exist
+ * @return The Sone with the given ID, or {@code null} if there is no such
+ * Sone
+ */
+ public Sone getSone(String id, boolean create) {
+ if (isLocalSone(id)) {
+ return getLocalSone(id);
+ }
+ return getRemoteSone(id, create);
+ }
+
+ /**
+ * Checks whether the core knows a Sone with the given ID.
+ *
+ * @param id
+ * The ID of the Sone
+ * @return {@code true} if there is a Sone with the given ID, {@code false}
+ * 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);
+ }
+ }
+
+ /**
+ * Returns all local Sones.
+ *
+ * @return All local Sones
+ */
+ public Set<Sone> getLocalSones() {
+ synchronized (localSones) {
+ return new HashSet<Sone>(localSones.values());
+ }
+ }
+
+ /**
+ * Returns the local Sone with the given ID.
+ *
+ * @param id
+ * The ID of the Sone to get
+ * @return The Sone with the given ID
+ */
+ public Sone getLocalSone(String id) {
+ return getLocalSone(id, true);
+ }
+
+ /**
+ * Returns the local Sone with the given ID, optionally creating a new Sone.
+ *
+ * @param id
+ * The ID of the Sone
+ * @param create
+ * {@code true} to create a new Sone if none exists,
+ * {@code false} to return null if none exists
+ * @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;
+ }
+ }
+
+ /**
+ * Returns all remote Sones.
+ *
+ * @return All remote Sones
+ */
+ public Set<Sone> getRemoteSones() {
+ synchronized (remoteSones) {
+ return new HashSet<Sone>(remoteSones.values());
+ }
+ }
+
+ /**
+ * Returns the remote Sone with the given ID.
+ *
+ * @param id
+ * The ID of the remote Sone to get
+ * @return The Sone with the given ID
+ */
+ public Sone getRemoteSone(String id) {
+ return getRemoteSone(id, true);
+ }
+
+ /**
+ * Returns the remote Sone with the given ID.
+ *
+ * @param id
+ * The ID of the remote Sone to get
+ * @param create
+ * {@code true} to always create a Sone, {@code false} to return
+ * {@code null} if no Sone with the given ID exists
+ * @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) {
+ 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);
+ }
+ }
+
+ /**
+ * 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}.
+ *
+ * @param sone
+ * The sone to check for
+ * @return {@code true} if the given Sone is new, false otherwise
+ */
+ public boolean isNewSone(Sone sone) {
+ synchronized (newSones) {
+ boolean isNew = !knownSones.contains(sone.getId()) && newSones.remove(sone.getId());
+ knownSones.add(sone.getId());
+ if (isNew) {
+ coreListenerManager.fireMarkSoneKnown(sone);
+ }
+ return isNew;
+ }
+ }
+
+ /**
+ * Returns whether the given Sone has been modified.
+ *
+ * @param sone
+ * The Sone to check for modifications
+ * @return {@code true} if a modification has been detected in the Sone,
+ * {@code false} otherwise
+ */
+ public boolean isModifiedSone(Sone sone) {
+ return (soneInserters.containsKey(sone)) ? soneInserters.get(sone).isModified() : false;
+ }
+
+ /**
+ * Returns the post with the given ID.
+ *
+ * @param postId
+ * The ID of the post to get
+ * @return The post, or {@code null} if there is no such post
+ */
+ public Post getPost(String postId) {
+ return getPost(postId, true);
+ }
+
+ /**
+ * Returns the post with the given ID, optionally creating a new post.
+ *
+ * @param postId
+ * The ID of the post to get
+ * @param create
+ * {@code true} it create a new post if no post with the given ID
+ * exists, {@code false} to return {@code null}
+ * @return The post, or {@code null} if there is no such post
+ */
+ public Post getPost(String postId, boolean create) {
+ synchronized (posts) {
+ Post post = posts.get(postId);
+ if ((post == null) && create) {
+ post = new Post(postId);
+ posts.put(postId, post);
+ }
+ return post;
+ }
+ }
+
+ /**
+ * Returns whether the given post ID is new. After this method returns it is
+ * marked a known post ID.
+ *
+ * @param postId
+ * The post ID
+ * @return {@code true} if the post is considered to be new, {@code false}
+ * 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;
+ }
+ }
+
+ /**
+ * Returns the reply with the given ID. If there is no reply with the given
+ * ID yet, a new one is created.
+ *
+ * @param replyId
+ * The ID of the reply to get
+ * @return The reply
+ */
+ public Reply getReply(String replyId) {
+ return getReply(replyId, true);
+ }
+
+ /**
+ * Returns the reply with the given ID. If there is no reply with the given
+ * ID yet, a new one is created, unless {@code create} is false in which
+ * case {@code null} is returned.
+ *
+ * @param replyId
+ * The ID of the reply to get
+ * @param create
+ * {@code true} to always return a {@link Reply}, {@code false}
+ * to return {@code null} if no reply can be found
+ * @return The reply, or {@code null} if there is no such reply
+ */
+ public Reply getReply(String replyId, boolean create) {
+ synchronized (replies) {
+ Reply reply = replies.get(replyId);
+ if (create && (reply == null)) {
+ reply = new Reply(replyId);
+ replies.put(replyId, reply);
+ }
+ return reply;
+ }
+ }
+
+ /**
+ * Returns all replies for the given post, order ascending by time.
+ *
+ * @param post
+ * The post to get all replies for
+ * @return All replies for the given post
+ */
+ public List<Reply> getReplies(Post post) {
+ Set<Sone> sones = getSones();
+ List<Reply> replies = new ArrayList<Reply>();
+ for (Sone sone : sones) {
+ for (Reply reply : sone.getReplies()) {
+ if (reply.getPost().equals(post)) {
+ replies.add(reply);
+ }
+ }
+ }
+ Collections.sort(replies, Reply.TIME_COMPARATOR);
+ return replies;
+ }
+
+ /**
+ * Returns whether the reply with the given ID is new.
+ *
+ * @param replyId
+ * The ID of the reply to check
+ * @return {@code true} if the reply is considered to be new, {@code false}
+ * 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;
+ }
+ }
+
+ /**
+ * Returns all Sones that have liked the given post.
+ *
+ * @param post
+ * The post to get the liking Sones for
+ * @return The Sones that like the given post
+ */
+ public Set<Sone> getLikes(Post post) {
+ Set<Sone> sones = new HashSet<Sone>();
+ for (Sone sone : getSones()) {
+ if (sone.getLikedPostIds().contains(post.getId())) {
+ sones.add(sone);
+ }
+ }
+ return sones;
+ }
+
+ /**
+ * Returns all Sones that have liked the given reply.
+ *
+ * @param reply
+ * The reply to get the liking Sones for
+ * @return The Sones that like the given reply
+ */
+ public Set<Sone> getLikes(Reply reply) {
+ Set<Sone> sones = new HashSet<Sone>();
+ for (Sone sone : getSones()) {
+ if (sone.getLikedReplyIds().contains(reply.getId())) {
+ sones.add(sone);
+ }
+ }
+ return sones;