From 94041feaf2a141b8c75725301f06d7e4bc617a12 Mon Sep 17 00:00:00 2001
From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?=
 <bombe@pterodactylus.net>
Date: Fri, 15 Oct 2010 21:40:47 +0200
Subject: [PATCH] Allow Sone, Post, and Reply creation only through the Core
 and its caches.

---
 .../java/net/pterodactylus/sone/core/Core.java     | 65 ++++++++++++++++++++--
 .../pterodactylus/sone/core/SoneDownloader.java    | 29 +++++++++-
 .../java/net/pterodactylus/sone/data/Sone.java     | 43 ++++++++++++++
 .../net/pterodactylus/sone/web/CreatePostPage.java |  2 +-
 4 files changed, 131 insertions(+), 8 deletions(-)

diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java
index 48976b1..459431c 100644
--- a/src/main/java/net/pterodactylus/sone/core/Core.java
+++ b/src/main/java/net/pterodactylus/sone/core/Core.java
@@ -106,7 +106,7 @@ public class Core extends AbstractService {
 	 */
 	public Core freenetInterface(FreenetInterface freenetInterface) {
 		this.freenetInterface = freenetInterface;
-		soneDownloader = new SoneDownloader(freenetInterface);
+		soneDownloader = new SoneDownloader(this, freenetInterface);
 		soneDownloader.start();
 		return this;
 	}
@@ -136,6 +136,34 @@ public class Core extends AbstractService {
 		return soneCache.get(soneId);
 	}
 
+	/**
+	 * Creates a new post.
+	 *
+	 * @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 getPost(UUID.randomUUID().toString()).setSone(sone).setTime(time).setText(text);
+	}
+
 	//
 	// ACTIONS
 	//
@@ -239,6 +267,36 @@ public class Core extends AbstractService {
 		localSones.remove(sone);
 	}
 
+	/**
+	 * Returns the post with the given ID. If no post exists yet with the given
+	 * ID, a new post is returned.
+	 *
+	 * @param postId
+	 *            The ID of the post
+	 * @return The post
+	 */
+	public Post getPost(String postId) {
+		if (!postCache.containsKey(postId)) {
+			postCache.put(postId, new Post(postId));
+		}
+		return postCache.get(postId);
+	}
+
+	/**
+	 * Returns the reply with the given ID. If no reply exists yet with the
+	 * given ID, a new reply is returned.
+	 *
+	 * @param replyId
+	 *            The ID of the reply
+	 * @return The reply
+	 */
+	public Reply getReply(String replyId) {
+		if (!replyCache.containsKey(replyId)) {
+			replyCache.put(replyId, new Reply(replyId));
+		}
+		return replyCache.get(replyId);
+	}
+
 	//
 	// SERVICE METHODS
 	//
@@ -307,8 +365,7 @@ public class Core extends AbstractService {
 					}
 					long time = configuration.getLongValue(postPrefix + "/Time").getValue(null);
 					String text = configuration.getStringValue(postPrefix + "/Text").getValue(null);
-					Post post = new Post(id, sone, time, text);
-					postCache.put(id, post);
+					Post post = getPost(id).setSone(sone).setTime(time).setText(text);
 					sone.addPost(post);
 				} while (true);
 				int replyCounter = 0;
@@ -325,7 +382,7 @@ public class Core extends AbstractService {
 					Post replyPost = postCache.get(configuration.getStringValue(replyPrefix + "/Post").getValue(null));
 					long replyTime = configuration.getLongValue(replyPrefix + "/Time").getValue(null);
 					String replyText = configuration.getStringValue(replyPrefix + "/Text").getValue(null);
-					Reply reply = new Reply(replyId, replySone, replyPost, replyTime, replyText);
+					Reply reply = getReply(replyId).setSone(replySone).setPost(replyPost).setTime(replyTime).setText(replyText);
 					replyCache.put(replyId, reply);
 				} while (true);
 
diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java
index 47bd940..4dd634c 100644
--- a/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java
+++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloader.java
@@ -49,6 +49,9 @@ public class SoneDownloader extends AbstractService {
 	/** The logger. */
 	private static final Logger logger = Logging.getLogger(SoneDownloader.class);
 
+	/** The core. */
+	private final Core core;
+
 	/** The Freenet interface. */
 	private final FreenetInterface freenetInterface;
 
@@ -58,11 +61,14 @@ public class SoneDownloader extends AbstractService {
 	/**
 	 * Creates a new Sone downloader.
 	 *
+	 * @param core
+	 *            The core
 	 * @param freenetInterface
 	 *            The Freenet interface
 	 */
-	public SoneDownloader(FreenetInterface freenetInterface) {
+	public SoneDownloader(Core core, FreenetInterface freenetInterface) {
 		super("Sone Downloader");
+		this.core = core;
 		this.freenetInterface = freenetInterface;
 	}
 
@@ -180,7 +186,7 @@ public class SoneDownloader extends AbstractService {
 					return;
 				}
 				try {
-					posts.add(new Post(postId, sone, Long.parseLong(postTime), postText));
+					posts.add(core.getPost(postId).setSone(sone).setTime(Long.parseLong(postTime)).setText(postText));
 				} catch (NumberFormatException nfe1) {
 					/* TODO - mark Sone as bad. */
 					logger.log(Level.WARNING, "Downloaded post for Sone %s with invalid time: %s", new Object[] { sone, postTime });
@@ -202,7 +208,24 @@ public class SoneDownloader extends AbstractService {
 				String replyPostId = replyXml.getValue("post-id", null);
 				String replyTime = replyXml.getValue("time", null);
 				String replyText = replyXml.getValue("text", null);
-				/* TODO - finish! */
+				if ((replyId == null) || (replyPostId == null) || (replyTime == null) || (replyText == null)) {
+					/* TODO - mark Sone as bad. */
+					logger.log(Level.WARNING, "Downloaded reply for Sone %s with missing data! ID: %s, Post: %s, Time: %s, Text: %s", new Object[] { sone, replyId, replyPostId, replyTime, replyText });
+					return;
+				}
+				try {
+					replies.add(core.getReply(replyId).setSone(sone).setPost(core.getPost(replyPostId)).setTime(Long.parseLong(replyTime)).setText(replyText));
+				} catch (NumberFormatException nfe1) {
+					/* TODO - mark Sone as bad. */
+					logger.log(Level.WARNING, "Downloaded reply for Sone %s with invalid time: %s", new Object[] { sone, replyTime });
+					return;
+				}
+			}
+
+			/* okay, apparently everything was parsed correctly. Now import. */
+			/* atomic setter operation on the Sone. */
+			synchronized (sone) {
+				sone.setProfile(profile);
 			}
 		} catch (IOException ioe1) {
 			logger.log(Level.WARNING, "Could not read XML file from " + sone + "!", ioe1);
diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java
index 4d50527..9219e99 100644
--- a/src/main/java/net/pterodactylus/sone/data/Sone.java
+++ b/src/main/java/net/pterodactylus/sone/data/Sone.java
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.data;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
@@ -191,6 +192,20 @@ public class Sone {
 	}
 
 	/**
+	 * Sets all friends of this Sone at once.
+	 *
+	 * @param friends
+	 *            The new (and only) friends of this Sone
+	 * @return This Sone (for method chaining)
+	 */
+	public synchronized Sone setFriends(Collection<Sone> friends) {
+		friendSones.clear();
+		friendSones.addAll(friends);
+		modificationCounter++;
+		return this;
+	}
+
+	/**
 	 * Returns whether this Sone has the given Sone as a friend Sone.
 	 *
 	 * @param friendSone
@@ -249,6 +264,20 @@ public class Sone {
 	}
 
 	/**
+	 * Sets all posts of this Sone at once.
+	 *
+	 * @param posts
+	 *            The new (and only) posts of this Sone
+	 * @return This Sone (for method chaining)
+	 */
+	public synchronized Sone setPosts(Collection<Post> posts) {
+		this.posts.clear();
+		this.posts.addAll(posts);
+		modificationCounter++;
+		return this;
+	}
+
+	/**
 	 * Adds the given post to this Sone. The post will not be added if its
 	 * {@link Post#getSone() Sone} is not this Sone.
 	 *
@@ -285,6 +314,20 @@ public class Sone {
 	}
 
 	/**
+	 * Sets all replies of this Sone at once.
+	 *
+	 * @param replies
+	 *            The new (and only) replies of this Sone
+	 * @return This Sone (for method chaining)
+	 */
+	public synchronized Sone setReplies(Collection<Reply> replies) {
+		this.replies.clear();
+		this.replies.addAll(replies);
+		modificationCounter++;
+		return this;
+	}
+
+	/**
 	 * Adds a reply to this Sone. If the given reply was not made by this Sone,
 	 * nothing is added to this Sone.
 	 *
diff --git a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java
index d44ee4f..58fd74e 100644
--- a/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java
+++ b/src/main/java/net/pterodactylus/sone/web/CreatePostPage.java
@@ -53,7 +53,7 @@ public class CreatePostPage extends SoneTemplatePage {
 		String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
 		if (text.length() != 0) {
 			Sone currentSone = getCurrentSone(request.getToadletContext());
-			Post post = new Post(currentSone, System.currentTimeMillis(), text);
+			Post post = webInterface.core().createPost(currentSone, System.currentTimeMillis(), text);
 			currentSone.addPost(post);
 			throw new RedirectException("index.html");
 		}
-- 
2.7.4