move project-related classes to own project
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 21 May 2008 18:11:32 +0000 (18:11 +0000)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 21 May 2008 18:11:32 +0000 (18:11 +0000)
git-svn-id: http://trooper/svn/projects/jSite/trunk@924 c3eda9e8-030b-0410-8277-bc7414b0a119

src/net/pterodactylus/jsite/core/Core.java
src/net/pterodactylus/jsite/core/CoreImpl.java
src/net/pterodactylus/jsite/core/Project.java [deleted file]
src/net/pterodactylus/jsite/core/ProjectManager.java [deleted file]
src/net/pterodactylus/jsite/gui/MainWindow.java
src/net/pterodactylus/jsite/gui/ProjectPanel.java
src/net/pterodactylus/jsite/gui/SwingInterface.java
src/net/pterodactylus/jsite/main/Main.java
src/net/pterodactylus/jsite/project/Project.java [new file with mode: 0644]
src/net/pterodactylus/jsite/project/ProjectManager.java [new file with mode: 0644]

index ba0b452..b84c786 100644 (file)
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.net.UnknownHostException;
 import java.util.List;
 
+import net.pterodactylus.jsite.project.Project;
+
 /**
  * Interface for the core.
  * 
index fdf008d..cf5a941 100644 (file)
@@ -24,6 +24,9 @@ import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
 
+import net.pterodactylus.jsite.project.Project;
+import net.pterodactylus.jsite.project.ProjectManager;
+
 /**
  * The core of jSite.
  * 
diff --git a/src/net/pterodactylus/jsite/core/Project.java b/src/net/pterodactylus/jsite/core/Project.java
deleted file mode 100644 (file)
index b93410a..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * jSite2 - Project.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.jsite.core;
-
-import java.beans.PropertyChangeListener;
-
-import net.pterodactylus.util.beans.AbstractBean;
-
-/**
- * Container for project information. A Project is capable of notifying
- * {@link PropertyChangeListener}s if any of the contained properties change.
- * 
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- * @version $Id$
- */
-public class Project extends AbstractBean {
-
-       /** Name of the “name” property. */
-       public static final String PROPERTY_NAME = "name";
-
-       /** Name of the “description” property. */
-       public static final String PROPERTY_DESCRIPTION = "description";
-
-       /** Name of the “public key” property. */
-       public static final String PROPERTY_PUBLIC_KEY = "publicKey";
-
-       /** Name of the “private key” property. */
-       public static final String PROPERTY_PRIVATE_KEY = "privateKey";
-
-       /** Name of the “local path” property. */
-       public static final String PROPERTY_LOCAL_PATH = "localPath";
-
-       /** The name of the project. */
-       private String name;
-
-       /** The description of the project. */
-       private String description;
-
-       /** The public key. */
-       private String publicKey;
-
-       /** The private key. */
-       private String privateKey;
-
-       /** The base path of the project. */
-       private String basePath;
-
-       //
-       // EVENT MANAGEMENT
-       //
-
-       /**
-        * Returns the name of the project.
-        * 
-        * @return The name of the project
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Sets the name of the project.
-        * 
-        * @param name
-        *            The name of the project
-        */
-       public void setName(String name) {
-               String oldName = this.name;
-               this.name = name;
-               if (!equal(oldName, name)) {
-                       firePropertyChange(PROPERTY_NAME, oldName, name);
-               }
-       }
-
-       /**
-        * Returns the description of the project.
-        * 
-        * @return The description of the project
-        */
-       public String getDescription() {
-               return description;
-       }
-
-       /**
-        * Sets the description of the project
-        * 
-        * @param description
-        *            The description of the project
-        */
-       public void setDescription(String description) {
-               String oldDescription = this.description;
-               this.description = description;
-               if (!equal(oldDescription, description)) {
-                       firePropertyChange(PROPERTY_DESCRIPTION, oldDescription, description);
-               }
-       }
-
-       /**
-        * Returns the public key of the project.
-        * 
-        * @return The public key of the project
-        */
-       public String getPublicKey() {
-               return publicKey;
-       }
-
-       /**
-        * Sets the public key of the project.
-        * 
-        * @param publicKey
-        *            The public key of the project
-        */
-       public void setPublicKey(String publicKey) {
-               String oldPublicKey = this.publicKey;
-               this.publicKey = publicKey;
-               if (!equal(oldPublicKey, publicKey)) {
-                       firePropertyChange(PROPERTY_PUBLIC_KEY, oldPublicKey, publicKey);
-               }
-       }
-
-       /**
-        * Returns the private key of the project.
-        * 
-        * @return The private key of the project
-        */
-       public String getPrivateKey() {
-               return privateKey;
-       }
-
-       /**
-        * Sets the private key of the project.
-        * 
-        * @param privateKey
-        *            The private key of the project
-        */
-       public void setPrivateKey(String privateKey) {
-               String oldPrivateKey = this.privateKey;
-               this.privateKey = privateKey;
-               if (!equal(oldPrivateKey, privateKey)) {
-                       firePropertyChange(PROPERTY_PRIVATE_KEY, oldPrivateKey, privateKey);
-               }
-       }
-
-       /**
-        * Returns the base path of the project.
-        * 
-        * @return The base path of the project
-        */
-       public String getBasePath() {
-               return basePath;
-       }
-
-       /**
-        * Sets the base path of the project.
-        * 
-        * @param basePath
-        *            The base path of the project
-        */
-       public void setBasePath(String basePath) {
-               String oldBasePath = this.basePath;
-               this.basePath = basePath;
-               firePropertyChange(PROPERTY_LOCAL_PATH, oldBasePath, basePath);
-       }
-
-}
diff --git a/src/net/pterodactylus/jsite/core/ProjectManager.java b/src/net/pterodactylus/jsite/core/ProjectManager.java
deleted file mode 100644 (file)
index ca07a4d..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * jSite2 - ProjectManager.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.jsite.core;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Properties;
-import java.util.logging.Logger;
-
-import net.pterodactylus.util.io.Closer;
-import net.pterodactylus.util.logging.Logging;
-
-/**
- * Manages projects, taking care of persistence, lifetime statistics, and other
- * things.
- * 
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- * @version $Id$
- */
-public class ProjectManager {
-
-       /** Logger. */
-       private static final Logger logger = Logging.getLogger(ProjectManager.class.getName());
-
-       /** The directory the projects are stored in. */
-       private final String directory;
-
-       /** The node manager. */
-       private NodeManager nodeManager;
-
-       /** All projects. */
-       private final List<Project> projects = Collections.synchronizedList(new ArrayList<Project>());
-
-       /**
-        * Creates a new project manager that saves and restores its state to/from
-        * the given directory.
-        * 
-        * @param directory
-        *            The directory to save and restore states to/from
-        */
-       public ProjectManager(String directory) {
-               this.directory = directory;
-       }
-
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the directory the projects are loaded from and saved to.
-        * 
-        * @return The directory for storing the projects
-        */
-       public String getDirectory() {
-               return directory;
-       }
-
-       /**
-        * Returns a list of all projects.
-        * 
-        * @return A list of all projects
-        */
-       public List<Project> getProjects() {
-               return Collections.unmodifiableList(new ArrayList<Project>(projects));
-       }
-
-       /**
-        * Sets the node manager to use.
-        * 
-        * @param nodeManager
-        *            The node manager to use
-        */
-       public void setNodeManager(NodeManager nodeManager) {
-               this.nodeManager = nodeManager;
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Loads projects and statistics.
-        * 
-        * @throws IOException
-        *             if an I/O error occurs
-        */
-       public void load() throws IOException {
-               File directoryFile = new File(directory);
-               File projectFile = new File(directoryFile, "projects.properties");
-               if (!projectFile.exists() || !projectFile.isFile() || !projectFile.canRead()) {
-                       return;
-               }
-               Properties projectProperties = new Properties();
-               InputStream projectInputStream = null;
-               try {
-                       projectInputStream = new FileInputStream(projectFile);
-                       projectProperties.load(projectInputStream);
-               } finally {
-                       Closer.close(projectInputStream);
-               }
-               int projectIndex = 0;
-               while (projectProperties.containsKey("projects." + projectIndex + ".name")) {
-                       String projectPrefix = "projects." + projectIndex;
-                       String projectName = projectProperties.getProperty(projectPrefix + ".name");
-                       String projectDescription = projectProperties.getProperty(projectPrefix + ".description");
-                       String projectPrivateKey = projectProperties.getProperty(projectPrefix + ".privateKey");
-                       String projectPublicKey = projectProperties.getProperty(projectPrefix + ".publicKey");
-                       Project project = new Project();
-                       project.setName(projectName);
-                       project.setDescription(projectDescription);
-                       project.setPrivateKey(projectPrivateKey);
-                       project.setPublicKey(projectPublicKey);
-                       projects.add(project);
-                       logger.fine("loaded project “" + project.getName() + "”.");
-                       projectIndex++;
-               }
-       }
-
-       /**
-        * Saves projects and statistics.
-        * 
-        * @throws IOException
-        *             if an I/O error occurs
-        */
-       public void save() throws IOException {
-               File directoryFile = new File(directory);
-               if (!directoryFile.exists()) {
-                       if (!directoryFile.mkdirs()) {
-                               throw new IOException("could not create directory: " + directory);
-                       }
-               }
-               Properties projectProperties = new Properties();
-               int projectIndex = 0;
-               for (Project project: projects) {
-                       String projectPrefix = "projects." + projectIndex;
-                       projectProperties.setProperty(projectPrefix + ".name", project.getName());
-                       projectProperties.setProperty(projectPrefix + ".description", project.getDescription());
-                       projectProperties.setProperty(projectPrefix + ".privateKey", project.getPrivateKey());
-                       projectProperties.setProperty(projectPrefix + ".publicKey", project.getPublicKey());
-                       projectIndex++;
-               }
-               File projectFile = new File(directoryFile, "projects.properties");
-               OutputStream projectOutputStream = null;
-               try {
-                       projectOutputStream = new FileOutputStream(projectFile);
-                       projectProperties.store(projectOutputStream, "jSite projects");
-               } finally {
-                       Closer.close(projectOutputStream);
-               }
-       }
-
-       /**
-        * Creates a new project. The returned {@link Project} will contain a newly
-        * generated key pair.
-        * 
-        * @return A newly created project
-        * @throws IOException
-        *             if an I/O error occured communicating with the node
-        * @throws JSiteException
-        *             if there is a problem with the node
-        */
-       public Project createProject() throws IOException, JSiteException {
-               Project project = new Project();
-               String[] keyPair = nodeManager.generateKeyPair();
-               project.setPrivateKey(keyPair[0]);
-               project.setPublicKey(keyPair[1]);
-               projects.add(project);
-               return project;
-       }
-}
index e91a9dc..551cfbb 100644 (file)
@@ -47,12 +47,12 @@ import javax.swing.JToolBar;
 import javax.swing.SwingConstants;
 import javax.swing.border.EmptyBorder;
 
-import net.pterodactylus.jsite.core.Project;
 import net.pterodactylus.jsite.i18n.I18n;
 import net.pterodactylus.jsite.i18n.I18nable;
 import net.pterodactylus.jsite.i18n.gui.I18nAction;
 import net.pterodactylus.jsite.i18n.gui.I18nMenu;
 import net.pterodactylus.jsite.main.Version;
+import net.pterodactylus.jsite.project.Project;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.swing.StatusBar;
 import net.pterodactylus.util.swing.SwingUtils;
index 09060ea..9a2e62b 100644 (file)
@@ -34,9 +34,9 @@ import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 import javax.swing.text.Document;
 
-import net.pterodactylus.jsite.core.Project;
 import net.pterodactylus.jsite.i18n.I18nable;
 import net.pterodactylus.jsite.i18n.gui.I18nLabel;
+import net.pterodactylus.jsite.project.Project;
 import net.pterodactylus.util.logging.Logging;
 
 /**
index 4381ea8..6f97944 100644 (file)
@@ -48,10 +48,10 @@ import net.pterodactylus.jsite.core.Core;
 import net.pterodactylus.jsite.core.CoreListener;
 import net.pterodactylus.jsite.core.JSiteException;
 import net.pterodactylus.jsite.core.Node;
-import net.pterodactylus.jsite.core.Project;
 import net.pterodactylus.jsite.core.Request;
 import net.pterodactylus.jsite.i18n.I18n;
 import net.pterodactylus.jsite.i18n.gui.I18nAction;
+import net.pterodactylus.jsite.project.Project;
 import net.pterodactylus.util.image.IconLoader;
 import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.logging.Logging;
index 151c6b1..8f25230 100644 (file)
@@ -28,9 +28,9 @@ import javax.swing.UIManager.LookAndFeelInfo;
 
 import net.pterodactylus.jsite.core.CoreImpl;
 import net.pterodactylus.jsite.core.NodeManager;
-import net.pterodactylus.jsite.core.ProjectManager;
 import net.pterodactylus.jsite.core.RequestManager;
 import net.pterodactylus.jsite.gui.SwingInterface;
+import net.pterodactylus.jsite.project.ProjectManager;
 import net.pterodactylus.util.logging.Logging;
 
 /**
diff --git a/src/net/pterodactylus/jsite/project/Project.java b/src/net/pterodactylus/jsite/project/Project.java
new file mode 100644 (file)
index 0000000..2c6609f
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * jSite2 - Project.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.jsite.project;
+
+import java.beans.PropertyChangeListener;
+
+import net.pterodactylus.util.beans.AbstractBean;
+
+/**
+ * Container for project information. A Project is capable of notifying
+ * {@link PropertyChangeListener}s if any of the contained properties change.
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class Project extends AbstractBean {
+
+       /** Name of the “name” property. */
+       public static final String PROPERTY_NAME = "name";
+
+       /** Name of the “description” property. */
+       public static final String PROPERTY_DESCRIPTION = "description";
+
+       /** Name of the “public key” property. */
+       public static final String PROPERTY_PUBLIC_KEY = "publicKey";
+
+       /** Name of the “private key” property. */
+       public static final String PROPERTY_PRIVATE_KEY = "privateKey";
+
+       /** Name of the “local path” property. */
+       public static final String PROPERTY_LOCAL_PATH = "localPath";
+
+       /** The name of the project. */
+       private String name;
+
+       /** The description of the project. */
+       private String description;
+
+       /** The public key. */
+       private String publicKey;
+
+       /** The private key. */
+       private String privateKey;
+
+       /** The base path of the project. */
+       private String basePath;
+
+       //
+       // EVENT MANAGEMENT
+       //
+
+       /**
+        * Returns the name of the project.
+        * 
+        * @return The name of the project
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Sets the name of the project.
+        * 
+        * @param name
+        *            The name of the project
+        */
+       public void setName(String name) {
+               String oldName = this.name;
+               this.name = name;
+               if (!equal(oldName, name)) {
+                       firePropertyChange(PROPERTY_NAME, oldName, name);
+               }
+       }
+
+       /**
+        * Returns the description of the project.
+        * 
+        * @return The description of the project
+        */
+       public String getDescription() {
+               return description;
+       }
+
+       /**
+        * Sets the description of the project
+        * 
+        * @param description
+        *            The description of the project
+        */
+       public void setDescription(String description) {
+               String oldDescription = this.description;
+               this.description = description;
+               if (!equal(oldDescription, description)) {
+                       firePropertyChange(PROPERTY_DESCRIPTION, oldDescription, description);
+               }
+       }
+
+       /**
+        * Returns the public key of the project.
+        * 
+        * @return The public key of the project
+        */
+       public String getPublicKey() {
+               return publicKey;
+       }
+
+       /**
+        * Sets the public key of the project.
+        * 
+        * @param publicKey
+        *            The public key of the project
+        */
+       public void setPublicKey(String publicKey) {
+               String oldPublicKey = this.publicKey;
+               this.publicKey = publicKey;
+               if (!equal(oldPublicKey, publicKey)) {
+                       firePropertyChange(PROPERTY_PUBLIC_KEY, oldPublicKey, publicKey);
+               }
+       }
+
+       /**
+        * Returns the private key of the project.
+        * 
+        * @return The private key of the project
+        */
+       public String getPrivateKey() {
+               return privateKey;
+       }
+
+       /**
+        * Sets the private key of the project.
+        * 
+        * @param privateKey
+        *            The private key of the project
+        */
+       public void setPrivateKey(String privateKey) {
+               String oldPrivateKey = this.privateKey;
+               this.privateKey = privateKey;
+               if (!equal(oldPrivateKey, privateKey)) {
+                       firePropertyChange(PROPERTY_PRIVATE_KEY, oldPrivateKey, privateKey);
+               }
+       }
+
+       /**
+        * Returns the base path of the project.
+        * 
+        * @return The base path of the project
+        */
+       public String getBasePath() {
+               return basePath;
+       }
+
+       /**
+        * Sets the base path of the project.
+        * 
+        * @param basePath
+        *            The base path of the project
+        */
+       public void setBasePath(String basePath) {
+               String oldBasePath = this.basePath;
+               this.basePath = basePath;
+               firePropertyChange(PROPERTY_LOCAL_PATH, oldBasePath, basePath);
+       }
+
+}
diff --git a/src/net/pterodactylus/jsite/project/ProjectManager.java b/src/net/pterodactylus/jsite/project/ProjectManager.java
new file mode 100644 (file)
index 0000000..fd62061
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * jSite2 - ProjectManager.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.jsite.project;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import net.pterodactylus.jsite.core.JSiteException;
+import net.pterodactylus.jsite.core.NodeManager;
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.logging.Logging;
+
+/**
+ * Manages projects, taking care of persistence, lifetime statistics, and other
+ * things.
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class ProjectManager {
+
+       /** Logger. */
+       private static final Logger logger = Logging.getLogger(ProjectManager.class.getName());
+
+       /** The directory the projects are stored in. */
+       private final String directory;
+
+       /** The node manager. */
+       private NodeManager nodeManager;
+
+       /** All projects. */
+       private final List<Project> projects = Collections.synchronizedList(new ArrayList<Project>());
+
+       /**
+        * Creates a new project manager that saves and restores its state to/from
+        * the given directory.
+        * 
+        * @param directory
+        *            The directory to save and restore states to/from
+        */
+       public ProjectManager(String directory) {
+               this.directory = directory;
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns the directory the projects are loaded from and saved to.
+        * 
+        * @return The directory for storing the projects
+        */
+       public String getDirectory() {
+               return directory;
+       }
+
+       /**
+        * Returns a list of all projects.
+        * 
+        * @return A list of all projects
+        */
+       public List<Project> getProjects() {
+               return Collections.unmodifiableList(new ArrayList<Project>(projects));
+       }
+
+       /**
+        * Sets the node manager to use.
+        * 
+        * @param nodeManager
+        *            The node manager to use
+        */
+       public void setNodeManager(NodeManager nodeManager) {
+               this.nodeManager = nodeManager;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Loads projects and statistics.
+        * 
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void load() throws IOException {
+               File directoryFile = new File(directory);
+               File projectFile = new File(directoryFile, "projects.properties");
+               if (!projectFile.exists() || !projectFile.isFile() || !projectFile.canRead()) {
+                       return;
+               }
+               Properties projectProperties = new Properties();
+               InputStream projectInputStream = null;
+               try {
+                       projectInputStream = new FileInputStream(projectFile);
+                       projectProperties.load(projectInputStream);
+               } finally {
+                       Closer.close(projectInputStream);
+               }
+               int projectIndex = 0;
+               while (projectProperties.containsKey("projects." + projectIndex + ".name")) {
+                       String projectPrefix = "projects." + projectIndex;
+                       String projectName = projectProperties.getProperty(projectPrefix + ".name");
+                       String projectDescription = projectProperties.getProperty(projectPrefix + ".description");
+                       String projectPrivateKey = projectProperties.getProperty(projectPrefix + ".privateKey");
+                       String projectPublicKey = projectProperties.getProperty(projectPrefix + ".publicKey");
+                       Project project = new Project();
+                       project.setName(projectName);
+                       project.setDescription(projectDescription);
+                       project.setPrivateKey(projectPrivateKey);
+                       project.setPublicKey(projectPublicKey);
+                       projects.add(project);
+                       logger.fine("loaded project “" + project.getName() + "”.");
+                       projectIndex++;
+               }
+       }
+
+       /**
+        * Saves projects and statistics.
+        * 
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void save() throws IOException {
+               File directoryFile = new File(directory);
+               if (!directoryFile.exists()) {
+                       if (!directoryFile.mkdirs()) {
+                               throw new IOException("could not create directory: " + directory);
+                       }
+               }
+               Properties projectProperties = new Properties();
+               int projectIndex = 0;
+               for (Project project: projects) {
+                       String projectPrefix = "projects." + projectIndex;
+                       projectProperties.setProperty(projectPrefix + ".name", project.getName());
+                       projectProperties.setProperty(projectPrefix + ".description", project.getDescription());
+                       projectProperties.setProperty(projectPrefix + ".privateKey", project.getPrivateKey());
+                       projectProperties.setProperty(projectPrefix + ".publicKey", project.getPublicKey());
+                       projectIndex++;
+               }
+               File projectFile = new File(directoryFile, "projects.properties");
+               OutputStream projectOutputStream = null;
+               try {
+                       projectOutputStream = new FileOutputStream(projectFile);
+                       projectProperties.store(projectOutputStream, "jSite projects");
+               } finally {
+                       Closer.close(projectOutputStream);
+               }
+       }
+
+       /**
+        * Creates a new project. The returned {@link Project} will contain a newly
+        * generated key pair.
+        * 
+        * @return A newly created project
+        * @throws IOException
+        *             if an I/O error occured communicating with the node
+        * @throws JSiteException
+        *             if there is a problem with the node
+        */
+       public Project createProject() throws IOException, JSiteException {
+               Project project = new Project();
+               String[] keyPair = nodeManager.generateKeyPair();
+               project.setPrivateKey(keyPair[0]);
+               project.setPublicKey(keyPair[1]);
+               projects.add(project);
+               return project;
+       }
+}