From: David ‘Bombe’ Roden Date: Wed, 21 May 2008 18:11:32 +0000 (+0000) Subject: move project-related classes to own project X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=10b165ebaa51eccec487500b32f0c7b3106923af;p=jSite2.git move project-related classes to own project git-svn-id: http://trooper/svn/projects/jSite/trunk@924 c3eda9e8-030b-0410-8277-bc7414b0a119 --- diff --git a/src/net/pterodactylus/jsite/core/Core.java b/src/net/pterodactylus/jsite/core/Core.java index ba0b452..b84c786 100644 --- a/src/net/pterodactylus/jsite/core/Core.java +++ b/src/net/pterodactylus/jsite/core/Core.java @@ -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. * diff --git a/src/net/pterodactylus/jsite/core/CoreImpl.java b/src/net/pterodactylus/jsite/core/CoreImpl.java index fdf008d..cf5a941 100644 --- a/src/net/pterodactylus/jsite/core/CoreImpl.java +++ b/src/net/pterodactylus/jsite/core/CoreImpl.java @@ -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 index b93410a..0000000 --- a/src/net/pterodactylus/jsite/core/Project.java +++ /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 <bombe@freenetproject.org> - * @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 index ca07a4d..0000000 --- a/src/net/pterodactylus/jsite/core/ProjectManager.java +++ /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 <bombe@freenetproject.org> - * @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 projects = Collections.synchronizedList(new ArrayList()); - - /** - * 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 getProjects() { - return Collections.unmodifiableList(new ArrayList(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; - } -} diff --git a/src/net/pterodactylus/jsite/gui/MainWindow.java b/src/net/pterodactylus/jsite/gui/MainWindow.java index e91a9dc..551cfbb 100644 --- a/src/net/pterodactylus/jsite/gui/MainWindow.java +++ b/src/net/pterodactylus/jsite/gui/MainWindow.java @@ -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; diff --git a/src/net/pterodactylus/jsite/gui/ProjectPanel.java b/src/net/pterodactylus/jsite/gui/ProjectPanel.java index 09060ea..9a2e62b 100644 --- a/src/net/pterodactylus/jsite/gui/ProjectPanel.java +++ b/src/net/pterodactylus/jsite/gui/ProjectPanel.java @@ -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; /** diff --git a/src/net/pterodactylus/jsite/gui/SwingInterface.java b/src/net/pterodactylus/jsite/gui/SwingInterface.java index 4381ea8..6f97944 100644 --- a/src/net/pterodactylus/jsite/gui/SwingInterface.java +++ b/src/net/pterodactylus/jsite/gui/SwingInterface.java @@ -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; diff --git a/src/net/pterodactylus/jsite/main/Main.java b/src/net/pterodactylus/jsite/main/Main.java index 151c6b1..8f25230 100644 --- a/src/net/pterodactylus/jsite/main/Main.java +++ b/src/net/pterodactylus/jsite/main/Main.java @@ -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 index 0000000..2c6609f --- /dev/null +++ b/src/net/pterodactylus/jsite/project/Project.java @@ -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 <bombe@freenetproject.org> + * @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 index 0000000..fd62061 --- /dev/null +++ b/src/net/pterodactylus/jsite/project/ProjectManager.java @@ -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 <bombe@freenetproject.org> + * @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 projects = Collections.synchronizedList(new ArrayList()); + + /** + * 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 getProjects() { + return Collections.unmodifiableList(new ArrayList(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; + } +}