aboutsummaryrefslogtreecommitdiff
path: root/Week9 Webshop/src
diff options
context:
space:
mode:
Diffstat (limited to 'Week9 Webshop/src')
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Article.java89
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/CLIInteraction.java94
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Cart.java67
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Category.java28
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Command.java25
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Database.java393
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/DatabaseItem.java15
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/DuplicateEntryException.java18
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/InputRequiredException.java13
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/ItemNotFoundException.java14
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Order.java67
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Shell.java476
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/Shop.java23
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/User.java143
-rw-r--r--Week9 Webshop/src/com/camilstaps/shop/UserInteraction.java198
15 files changed, 1663 insertions, 0 deletions
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Article.java b/Week9 Webshop/src/com/camilstaps/shop/Article.java
new file mode 100644
index 0000000..d22a323
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Article.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.io.File;
+
+/**
+ * An Article in the webshop
+ * @author Camil Staps, s4498062
+ */
+public class Article extends DatabaseItem {
+
+ /**
+ * Basic data about the article
+ */
+ private final String name;
+ private String description;
+ private final Category category;
+ private File multimedia;
+ private final float price;
+
+ /**
+ * The owner who added the article
+ */
+ private final User owner;
+
+ /**
+ * Straightforwardly creating a new article
+ * @param user
+ * @param name
+ * @param category
+ * @param price
+ */
+ public Article(User user, String name, Category category, float price) {
+ this.owner = user;
+ this.name = name;
+ this.category = category;
+ this.price = price;
+ }
+
+ public User getOwner() {
+ return owner;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Category getCategory() {
+ return category;
+ }
+
+ public File getMultimedia() {
+ return multimedia;
+ }
+
+ public float getPrice() {
+ return price;
+ }
+
+ /**
+ * Set a new description
+ * @param description
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Set multimedia
+ * @param multimedia
+ */
+ public void setMultimedia(File multimedia) {
+ this.multimedia = multimedia;
+ }
+
+ @Override
+ public String toString() {
+ return name + " (" + category.getName() + "): " + Float.toString(price);
+ }
+
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/CLIInteraction.java b/Week9 Webshop/src/com/camilstaps/shop/CLIInteraction.java
new file mode 100644
index 0000000..cbf59ab
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/CLIInteraction.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Command Line Interface Interaction
+ * @author Camil Staps, s4498062
+ */
+public class CLIInteraction extends UserInteraction {
+
+ private final Scanner in;
+ private final PrintStream out;
+
+ /**
+ * Default is stdin and stdout
+ */
+ public CLIInteraction() {
+ this(System.in, System.out);
+ }
+
+ /**
+ * CLI interaction with custom input and outputstream
+ * @param is
+ * @param ps
+ */
+ public CLIInteraction(InputStream is, PrintStream ps) {
+ in = new Scanner(is);
+ out = ps;
+ }
+
+ @Override
+ String getString() {
+ return in.nextLine();
+ }
+
+ @Override
+ public int getChoice(String question, String[] options) {
+ out.println(question);
+ int i = 1;
+ for (String option : options) {
+ out.println(" " + (i++) + " : " + option);
+ }
+ int selection = 0;
+ boolean read = false;
+ do {
+ if (read) {
+ out.println("Invalid option. Try again:");
+ }
+ try {
+ selection = in.nextInt();
+ } catch (InputMismatchException ex) {
+ }
+ in.nextLine();
+ read = true;
+ } while (selection < 1 || selection > options.length);
+ return selection - 1;
+ }
+
+ @Override
+ void putString(String string) {
+ out.print(string);
+ }
+
+ @Override
+ Command getCommand() {
+ putString("► ");
+ return new Command(getString());
+ }
+
+ @Override
+ float getFloat() {
+ float result = in.nextFloat();
+ in.nextLine();
+ return result;
+ }
+
+ @Override
+ boolean getBoolean() {
+ putString(" (yes/no) ");
+ String result;
+ do {
+ result = in.nextLine();
+ } while (!result.equalsIgnoreCase("yes") && !result.equalsIgnoreCase("no"));
+ return result.equalsIgnoreCase("yes");
+ }
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Cart.java b/Week9 Webshop/src/com/camilstaps/shop/Cart.java
new file mode 100644
index 0000000..74f6ccd
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Cart.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A Cart holds the articles a User is planning to buy.
+ * @author Camil Staps, s4498062
+ */
+public class Cart implements Serializable {
+
+ private final Set<Article> articles = new HashSet<>();
+
+ public Set<Article> getArticles() {
+ return articles;
+ }
+
+ /**
+ * Get the total price of all articles
+ * @return
+ */
+ public float getTotalAmount() {
+ float result = 0;
+ for (Article a : articles) {
+ result += a.getPrice();
+ }
+ return result;
+ }
+
+ /**
+ * Add a new article
+ * @param article
+ */
+ public void add(Article article) {
+ Database.getInstance().removeItem(article);
+ articles.add(article);
+ }
+
+ /**
+ * Remove an article (and put it back in the database)
+ * @param article
+ */
+ public void remove(Article article) {
+ articles.remove(article);
+ try {
+ Database.getInstance().addItem(article);
+ } catch (DuplicateEntryException ex) {
+ }
+ }
+
+ /**
+ * Remove all articles in the manner of remove()
+ * @see self#remove
+ */
+ public void reset() {
+ for (Article a : articles) {
+ remove(a);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Category.java b/Week9 Webshop/src/com/camilstaps/shop/Category.java
new file mode 100644
index 0000000..05c247c
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Category.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+/**
+ * A Category for Articles
+ * @author Camil Staps, s4498062
+ */
+public class Category extends DatabaseItem {
+
+ private final String name;
+
+ /**
+ * Create a new Category
+ * @param name
+ */
+ public Category(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Command.java b/Week9 Webshop/src/com/camilstaps/shop/Command.java
new file mode 100644
index 0000000..b4694c0
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Command.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+/**
+ * A Command is something that can be executed by a normal visitor, a User or an Administrator.
+ * The current version only holds one string as command, but later versions could include arguments.
+ * @author Camil Staps, s4498062
+ */
+public class Command {
+
+ private final String command;
+
+ public Command(String command) {
+ this.command = command;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Database.java b/Week9 Webshop/src/com/camilstaps/shop/Database.java
new file mode 100644
index 0000000..8344bca
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Database.java
@@ -0,0 +1,393 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * The Shop database
+ * @author Camil Staps, s4498062
+ */
+public class Database {
+
+ /**
+ * This is a singleton
+ */
+ private static Database instance;
+
+ /**
+ * Keep the database in working memory during runtime
+ */
+ private Set<User> users;
+ private Set<Article> articles;
+ private Set<Category> categories;
+ private Set<Order> orders;
+
+ /**
+ * Files to store the database
+ * Considering backups, it's nicer to have different objects stored in
+ * different files, so that if the Orders file breaks, we still have the
+ * Users, etc.
+ */
+ private final File FILE_DB = new File("./db");
+ private final File FILE_USERS = new File("./db/users.db");
+ private final File FILE_ARTICLES = new File("./db/articles.db");
+ private final File FILE_CATEGORIES = new File("./db/categories.db");
+ private final File FILE_ORDERS = new File("./db/orders.db");
+
+ /**
+ * Don't use this constructor. This is a singleton: use getInstance() instead.
+ * @see self#getInstance()
+ */
+ public Database() {
+ if (!FILE_DB.exists())
+ FILE_DB.mkdir();
+
+ readUsers();
+ readArticles();
+ readCategories();
+ readOrders();
+ }
+
+ /**
+ * Get an instance of the database. This is a singleton.
+ * @return the database
+ */
+ public static Database getInstance() {
+ if (instance == null) {
+ instance = new Database();
+ }
+ return instance;
+ }
+
+ /**
+ * Add an item to the database
+ * @param item the item
+ * @throws com.camilstaps.shop.DuplicateEntryException
+ * @throws ClassCastException if the item to remove is not a DatabaseItem
+ */
+ public void addItem(DatabaseItem item) throws DuplicateEntryException {
+ if (item instanceof User) {
+ if (isUserExists(((User) item).getNr())) {
+ throw new DuplicateEntryException();
+ }
+ users.add((User) item);
+ } else if (item instanceof Article) {
+ if (isArticleExists(((Article) item).getName())) {
+ throw new DuplicateEntryException();
+ }
+ articles.add((Article) item);
+ } else if (item instanceof Category) {
+ if (isCategoryExists(((Category) item).getName())) {
+ throw new DuplicateEntryException();
+ }
+ categories.add((Category) item);
+ } else if (item instanceof Order) {
+ orders.add((Order) item);
+ } else {
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * Remove an item from the database
+ * @param item the item to remove
+ * @throws ClassCastException if the item to remove is not a DatabaseItem
+ */
+ public void removeItem(DatabaseItem item) {
+ if (item instanceof User) {
+ users.remove((User) item);
+ } else if (item instanceof Article) {
+ articles.remove((Article) item);
+ } else if (item instanceof Category) {
+ categories.remove((Category) item);
+ } else if (item instanceof Order) {
+ orders.remove((Order) item);
+ } else {
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * Save the database to the filesystem.
+ * @return true on success, false on failure
+ */
+ public boolean write() {
+ return writeUsers() && writeArticles() && writeCategories() && writeOrders();
+ }
+
+ /**
+ * Check if there exists a user with a certain U/S-number
+ * @param nr
+ * @return
+ */
+ public boolean isUserExists(String nr) {
+ for (User user : users) {
+ if (user.getNr().equals(nr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if an article with a name exists
+ * @param name
+ * @return
+ */
+ public boolean isArticleExists(String name) {
+ for (Article article : articles) {
+ if (article.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a category with a name exists
+ * @param name
+ * @return
+ */
+ public boolean isCategoryExists(String name) {
+ for (Category category : categories) {
+ if (category.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the set of users
+ * @return
+ */
+ public Set<User> getUsers() {
+ return users;
+ }
+
+ /**
+ * Get the set of articles
+ * @return
+ */
+ public Set<Article> getArticles() {
+ return articles;
+ }
+
+ /**
+ * Get the set of categories
+ * @return
+ */
+ public Set<Category> getCategories() {
+ return categories;
+ }
+
+ /**
+ * Get an array of the names of the categories
+ * @return
+ */
+ public String[] getCategoryNames() {
+ String[] categoryNames = new String[categories.size()];
+ int i = 0;
+ for (Category c : categories) {
+ categoryNames[i++] = c.getName();
+ }
+ return categoryNames;
+ }
+
+ /**
+ * Get the set of orders
+ * @return
+ */
+ public Set<Order> getOrders() {
+ return orders;
+ }
+
+ /**
+ * Get the set of articles of which the name or description matches a regular expression
+ * @param p the regular expression
+ * @return
+ */
+ public Set<Article> searchArticle(Pattern p) {
+ Set<Article> result = new HashSet<>();
+ for (Article a : articles) {
+ if (p.matcher(a.getName()).find() || p.matcher(a.getDescription()).find()) {
+ result.add(a);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get a user by his number
+ * @param number
+ * @return
+ * @throws com.camilstaps.shop.ItemNotFoundException
+ */
+ public User getUser(String number) throws ItemNotFoundException {
+ for (User u : users) {
+ if (u.getNr().equals(number)) {
+ return u;
+ }
+ }
+ throw new ItemNotFoundException();
+ }
+
+ /**
+ * Get an article by its name
+ * @param name
+ * @return
+ * @throws ItemNotFoundException
+ */
+ public Article getArticle(String name) throws ItemNotFoundException {
+ for (Article a : articles) {
+ if (a.getName().equals(name)) {
+ return a;
+ }
+ }
+ throw new ItemNotFoundException();
+ }
+
+ /**
+ * Get a category by a name
+ * @param name
+ * @return
+ */
+ public Category getCategory(String name) throws ItemNotFoundException {
+ for (Category c : categories) {
+ if (c.getName().equals(name)) {
+ return c;
+ }
+ }
+ throw new ItemNotFoundException();
+ }
+
+ /**
+ * Read the users from the database into RAM
+ */
+ private void readUsers() {
+ users = new HashSet<>();
+ try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(FILE_USERS))) {
+ users = (Set) in.readObject();
+ in.close();
+ } catch (FileNotFoundException ex) {
+ } catch (IOException | ClassNotFoundException ex) {
+ }
+ }
+
+ /**
+ * Read the articles from the database into RAM
+ */
+ private void readArticles() {
+ articles = new HashSet<>();
+ try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(FILE_ARTICLES))) {
+ articles = (Set) in.readObject();
+ in.close();
+ } catch (FileNotFoundException ex) {
+ } catch (IOException | ClassNotFoundException ex) {
+ }
+ }
+
+ /**
+ * Read the categories from the database into RAM
+ */
+ private void readCategories() {
+ categories = new HashSet<>();
+ try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(FILE_CATEGORIES))) {
+ categories = (Set) in.readObject();
+ in.close();
+ } catch (FileNotFoundException ex) {
+ } catch (IOException | ClassNotFoundException ex) {
+ }
+ }
+
+ /**
+ * Read the orders from the database into RAM
+ */
+ private void readOrders() {
+ orders = new HashSet<>();
+ try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(FILE_ORDERS))) {
+ orders = (Set) in.readObject();
+ in.close();
+ } catch (FileNotFoundException ex) {
+ } catch (IOException | ClassNotFoundException ex) {
+ }
+ }
+
+ /**
+ * Write the users from RAM to the database
+ */
+ private boolean writeUsers() {
+ System.err.println("Saving users...");
+
+ try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE_USERS))) {
+ out.writeObject(users);
+ out.close();
+ return true;
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Write the articles from RAM to the database
+ */
+ private boolean writeArticles() {
+ System.err.println("Saving articles...");
+
+ try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE_ARTICLES))) {
+ out.writeObject(articles);
+ out.close();
+ return true;
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Write the categories from RAM to the database
+ */
+ private boolean writeCategories() {
+ System.err.println("Saving categories...");
+
+ try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE_CATEGORIES))) {
+ out.writeObject(categories);
+ out.close();
+ return true;
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Write the orders from RAM to the database
+ */
+ private boolean writeOrders() {
+ System.err.println("Saving orders...");
+
+ try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE_ORDERS))) {
+ out.writeObject(orders);
+ out.close();
+ return true;
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/DatabaseItem.java b/Week9 Webshop/src/com/camilstaps/shop/DatabaseItem.java
new file mode 100644
index 0000000..0cbe661
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/DatabaseItem.java
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.io.Serializable;
+
+/**
+ * DatabaseItem is a general class for anything that is stored in the Database
+ * @author Camil Staps, s4498062
+ */
+public class DatabaseItem implements Serializable {
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/DuplicateEntryException.java b/Week9 Webshop/src/com/camilstaps/shop/DuplicateEntryException.java
new file mode 100644
index 0000000..a91d7c7
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/DuplicateEntryException.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+/**
+ * This Exception is thrown when an attempt is made to add a DatabaseItem to the
+ * Database which gives a uniqueness conflict, such as:
+ * * An article with the same name exists
+ * * A category with the same name exists
+ * * A user with the same number exists
+ *
+ * @author Camil Staps, s4498062
+ */
+public class DuplicateEntryException extends Exception {
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/InputRequiredException.java b/Week9 Webshop/src/com/camilstaps/shop/InputRequiredException.java
new file mode 100644
index 0000000..ca0110d
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/InputRequiredException.java
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+/**
+ * This Exception is thrown when required input was omitted by the user.
+ * @author Camil Staps, s4498062
+ */
+public class InputRequiredException extends Exception {
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/ItemNotFoundException.java b/Week9 Webshop/src/com/camilstaps/shop/ItemNotFoundException.java
new file mode 100644
index 0000000..5122d79
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/ItemNotFoundException.java
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+/**
+ * This error is thrown when an attempt is made to instantiate an object with a
+ * non-existing name, number, etc.
+ * @author Camil Staps, s4498062
+ */
+public class ItemNotFoundException extends Exception {
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Order.java b/Week9 Webshop/src/com/camilstaps/shop/Order.java
new file mode 100644
index 0000000..2b0c5a7
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Order.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An order is a set of articles, purchased by a user
+ * @author Camil Staps, s4498062
+ */
+public class Order extends DatabaseItem {
+
+ private final Set<Article> articles;
+ private final User user;
+ private boolean paid = false;
+
+ /**
+ * This constructor takes the articles from the Cart of the User, and clears
+ * that Cart afterwards.
+ * @param user
+ */
+ public Order(User user) {
+ this.user = user;
+ articles = new HashSet<>();
+ for (Article a : user.getCart().getArticles()) {
+ articles.add(a);
+ }
+ user.getCart().getArticles().clear();
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public Set<Article> getArticles() {
+ return articles;
+ }
+
+ public void setPaid(boolean set) {
+ paid = set;
+ }
+
+ /**
+ * See whether payment has been received for this article already
+ * @return
+ */
+ public boolean isPaid() {
+ return paid;
+ }
+
+ /**
+ * Get the total price of all articles
+ * @return
+ */
+ public float getTotalAmount() {
+ float result = 0;
+ for (Article a : articles) {
+ result += a.getPrice();
+ }
+ return result;
+ }
+
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Shell.java b/Week9 Webshop/src/com/camilstaps/shop/Shell.java
new file mode 100644
index 0000000..7939963
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Shell.java
@@ -0,0 +1,476 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * Executing commands and providing output, using a UserInteraction and acting
+ * on a Database.
+ * @author Camil Staps, s4498062
+ */
+public class Shell {
+
+ /**
+ * The Database to work with
+ */
+ private final Database database;
+ /**
+ * The UserInteraction to use
+ */
+ private final UserInteraction ui;
+
+ /**
+ * The current User, if logged in
+ */
+ private User user;
+
+ public Shell(UserInteraction userInteraction) {
+ this.database = Database.getInstance();
+ this.ui = userInteraction;
+ }
+
+ /**
+ * Request commands, execute them, and show the results
+ */
+ public void run() {
+ while (true) {
+ Command command = ui.getCommand();
+ try {
+ Method method = getClass().getMethod("exec" + command.getCommand().substring(0,1).toUpperCase() + command.getCommand().substring(1));
+ method.invoke(this);
+
+ if (command.getCommand().equalsIgnoreCase("exit")) {
+ return;
+ }
+ } catch (NoSuchMethodException ex) {
+ ui.putStringln("Failure: no such command");
+ } catch (InvocationTargetException ex) {
+ ui.putStringln("Failure: " + ex.getCause().toString());
+ } catch (SecurityException | IllegalAccessException | IllegalArgumentException ex) {
+ ui.putStringln("Failure: unknown error");
+ }
+ }
+ }
+
+ /**
+ * Require that the visitor logs in
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException if the visitor fails to login
+ * @throws ItemNotFoundException if the visitor tries to login with a non-existing User number
+ */
+ public void requireLogin() throws LoginRequiredException, ItemNotFoundException {
+ if (user == null) {
+ ui.putStringln("You must login first.");
+
+ try {
+ execLogin();
+ } catch (InputRequiredException ex) {
+ }
+
+ if (user == null) {
+ throw new LoginRequiredException();
+ }
+ }
+ }
+
+ /**
+ * Require that an administrator is logged in
+ * @throws com.camilstaps.shop.Shell.AdminRequiredException if the visitor fails to login, or is not an administrator
+ * @throws ItemNotFoundException if the visitor tries to login with a non-existing User number
+ */
+ public void requireAdmin() throws AdminRequiredException, ItemNotFoundException {
+ if (user == null || !user.isAdmin()) {
+ ui.putStringln("You must login as an administrator first.");
+
+ try {
+ execLogin();
+ } catch (InputRequiredException ex) {
+ }
+
+ if (user == null || !user.isAdmin()) {
+ throw new AdminRequiredException();
+ }
+ }
+ }
+
+ /**
+ * Command: add an article
+ * @throws DuplicateEntryException
+ * @throws InputRequiredException
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execAddArticle() throws DuplicateEntryException, InputRequiredException, LoginRequiredException, ItemNotFoundException {
+ requireLogin();
+
+ String name = ui.getRequiredString("Name: ");
+ Category category = ui.getCategory();
+ float price = ui.getFloat("Price: ");
+
+ Article a = new Article(user, name, category, price);
+ database.addItem(a);
+ }
+
+ /**
+ * Command: set the article description
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ * @throws InputRequiredException
+ */
+ public void execSetArticleDescription() throws LoginRequiredException, ItemNotFoundException, InputRequiredException {
+ requireLogin();
+
+ Article a;
+ if (user.isAdmin()) {
+ a = ui.getArticle();
+ } else {
+ a = ui.getArticle(user);
+ }
+
+ a.setDescription(ui.getRequiredString("Description: "));
+ }
+
+ /**
+ * Command: set the multimedia linked to an article
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ * @throws InputRequiredException
+ */
+ public void execSetArticleMultimedia() throws LoginRequiredException, ItemNotFoundException, InputRequiredException {
+ requireLogin();
+
+ Article a;
+ if (user.isAdmin()) {
+ a = ui.getArticle();
+ } else {
+ a = ui.getArticle(user);
+ }
+
+ a.setMultimedia(new File(ui.getRequiredString("Multimedia: ")));
+ }
+
+ /**
+ * Command: remove an article
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ * @throws InputRequiredException
+ */
+ public void execRemoveArticle() throws LoginRequiredException, ItemNotFoundException, InputRequiredException {
+ requireLogin();
+
+ Article a;
+ if (user.isAdmin()) {
+ a = ui.getArticle();
+ } else {
+ a = ui.getArticle(user);
+ }
+
+ database.removeItem(a);
+ }
+
+ /**
+ * Command: show a list of articles
+ */
+ public void execListArticles() {
+ for (Article a : database.getArticles()) {
+ ui.putStringln(a.toString());
+ }
+ }
+
+ /**
+ * Command: search for an article
+ */
+ public void execSearchArticle() {
+ String regex = ui.getString("Keywords: ");
+ for (Article a : database.searchArticle(Pattern.compile(regex))) {
+ ui.putStringln(a.toString());
+ }
+ }
+
+ /**
+ * Command: show detailed data about an article
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execShowArticle() throws InputRequiredException, ItemNotFoundException {
+ Article a = ui.getArticle();
+ ui.putStringln(a.toString());
+ if (a.getDescription() != null) {
+ ui.putStringln(a.getDescription());
+ }
+ File multimedia = a.getMultimedia();
+ if (multimedia != null) {
+ ui.putStringln("Multimedia: " + multimedia.getPath());
+ }
+ if (user != null && user.isAdmin()) {
+ ui.putStringln("User: " + a.getOwner().toString(true));
+ }
+ }
+
+ /**
+ * Command: add a category
+ * @throws DuplicateEntryException
+ * @throws InputRequiredException
+ * @throws com.camilstaps.shop.Shell.AdminRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execAddCategory() throws DuplicateEntryException, InputRequiredException, AdminRequiredException, ItemNotFoundException {
+ requireAdmin();
+
+ String name = ui.getRequiredString("Name: ");
+ database.addItem(new Category(name));
+ }
+
+ /**
+ * Command: list categories
+ */
+ public void execListCategories() {
+ for (String c : database.getCategoryNames()) {
+ ui.putStringln(c);
+ }
+ }
+
+ /**
+ * Command: list users
+ * Administrators see a more detailed list
+ */
+ public void execListUsers() {
+ for (User u : database.getUsers()) {
+ ui.putStringln(u.toString(user != null && user.isAdmin()));
+ }
+ }
+
+ /**
+ * Command: register a new user
+ * @throws DuplicateEntryException
+ * @throws InputRequiredException
+ */
+ public void execRegister() throws DuplicateEntryException, InputRequiredException {
+ boolean addAsAdmin = false;
+
+ if (database.getUsers().isEmpty()) {
+ addAsAdmin = true;
+ ui.putStringln("This is the first user and will therefore be added as administrator.");
+ } else if (user != null && user.isAdmin()) {
+ addAsAdmin = ui.getBoolean("Add user as administrator");
+ }
+
+ String nr = ui.getRequiredString("Number: ");
+ String email = ui.getRequiredString("Email: ");
+
+ User u = new User(nr, email, addAsAdmin);
+
+ String password = u.setRandomPassword();
+ ui.putStringln("Password: " + password);
+
+ database.addItem(u);
+ }
+
+ /**
+ * Command: login
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execLogin() throws InputRequiredException, ItemNotFoundException {
+ if (user != null) {
+ ui.putStringln("You are already logged in.");
+ return;
+ }
+
+ User u = ui.getUser();
+ String pw = ui.getRequiredString("Password: ");
+
+ if (!u.verify(pw)) {
+ ui.putStringln("Failed to login.");
+ return;
+ }
+
+ if (u.isBlocked()) {
+ ui.putStringln("You are blocked.");
+ return;
+ }
+
+ user = u;
+ }
+
+ /**
+ * Command: logout
+ */
+ public void execLogout() {
+ user = null;
+ }
+
+ /**
+ * Command: add an article to the cart
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ * @throws InputRequiredException
+ */
+ public void execAddToCart() throws LoginRequiredException, ItemNotFoundException, InputRequiredException {
+ requireLogin();
+
+ user.getCart().add(ui.getArticle());
+ }
+
+ /**
+ * Command: remove an article from the cart
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execRemoveFromCart() throws LoginRequiredException, ItemNotFoundException {
+ requireLogin();
+
+ user.getCart().remove(ui.getArticle(user.getCart().getArticles()));
+ }
+
+ /**
+ * Command: list articles in the cart
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execListCart() throws LoginRequiredException, ItemNotFoundException {
+ requireLogin();
+
+ for (Article a : user.getCart().getArticles()) {
+ ui.putStringln(a.toString());
+ }
+
+ ui.putStringln("Total value: " + user.getCart().getTotalAmount());
+ }
+
+ /**
+ * Command: remove all articles from the cart
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ */
+ public void execClearCart() throws LoginRequiredException, ItemNotFoundException {
+ requireLogin();
+
+ if (ui.getBoolean("Are you sure?")) user.getCart().reset();
+ }
+
+ /**
+ * Command: checkout (create an Order using the current Cart)
+ * @throws ItemNotFoundException
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws DuplicateEntryException
+ */
+ public void execCheckout() throws ItemNotFoundException, LoginRequiredException, DuplicateEntryException {
+ requireLogin();
+
+ if (user.getCart().getArticles().isEmpty()) {
+ throw new ItemNotFoundException();
+ }
+
+ ui.putStringln("By checking out, you agree to pay the total amount.");
+ if (!ui.getBoolean("Do you agree?"))
+ return;
+
+ Order order = new Order(user);
+ database.addItem(order);
+
+ ui.putStringln("Your order has been added as " + order.toString());
+ }
+
+ /**
+ * Command: list orders
+ * @throws ItemNotFoundException
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ */
+ public void execListOrders() throws ItemNotFoundException, LoginRequiredException {
+ requireLogin();
+
+ Set<Order> orders;
+ if (user.isAdmin()) {
+ orders = database.getOrders();
+ } else {
+ orders = user.getOrders();
+ }
+ for (Order o : orders) {
+ ui.putStringln(o.toString());
+ }
+ }
+
+ /**
+ * Command: show detailed information about an order
+ * @throws com.camilstaps.shop.Shell.LoginRequiredException
+ * @throws ItemNotFoundException
+ * @throws InputRequiredException
+ */
+ public void execShowOrder() throws LoginRequiredException, ItemNotFoundException, InputRequiredException {
+ requireLogin();
+
+ Order o;
+ if (user.isAdmin()) {
+ o = ui.getOrder();
+ } else {
+ o = ui.getOrder(user);
+ }
+ ui.putStringln(o.toString());
+ for (Article a : o.getArticles()) {
+ ui.putStringln(" " + a.toString());
+ }
+ ui.putStringln("Total amount: " + o.getTotalAmount());
+ ui.putStringln("Paid: " + (o.isPaid() ? "yes" : "no"));
+ }
+
+ /**
+ * Command: set the paid status of an Order to true
+ * @throws com.camilstaps.shop.Shell.AdminRequiredException
+ * @throws ItemNotFoundException
+ * @throws InputRequiredException
+ */
+ public void execSetOrderPaid() throws AdminRequiredException, ItemNotFoundException, InputRequiredException {
+ requireAdmin();
+ ui.getOrder().setPaid(true);
+ }
+
+ /**
+ * Command: block a user
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ * @throws com.camilstaps.shop.Shell.AdminRequiredException
+ */
+ public void execBlockUser() throws InputRequiredException, ItemNotFoundException, AdminRequiredException {
+ requireAdmin();
+ ui.getUser().setBlocked(true);
+ }
+
+ /**
+ * Command: unblock a user
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ * @throws com.camilstaps.shop.Shell.AdminRequiredException
+ */
+ public void execUnblockUser() throws InputRequiredException, ItemNotFoundException, AdminRequiredException {
+ requireAdmin();
+ ui.getUser().setBlocked(false);
+ }
+
+ /**
+ * Command: save the database
+ */
+ public void execExit() {
+ database.write();
+ }
+
+ /**
+ * This Exception is thrown when the visitor is required to login, but fails
+ */
+ private class LoginRequiredException extends Exception {
+ }
+
+ /**
+ * This Exception is thrown when the visitor is required to login as an administrator, but fails
+ */
+ private class AdminRequiredException extends Exception {
+ }
+
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/Shop.java b/Week9 Webshop/src/com/camilstaps/shop/Shop.java
new file mode 100644
index 0000000..cab828a
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/Shop.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+/**
+ * The webshop
+ * @author Camil Staps, s4498062
+ */
+public class Shop {
+
+ /**
+ * Create a shell and run
+ * @param args
+ */
+ public static void main(String[] args) {
+ Shell sh = new Shell(new CLIInteraction());
+ sh.run();
+ }
+
+}
diff --git a/Week9 Webshop/src/com/camilstaps/shop/User.java b/Week9 Webshop/src/com/camilstaps/shop/User.java
new file mode 100644
index 0000000..12cfd4d
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/User.java
@@ -0,0 +1,143 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * A User is a person with a U/S-number, email and password.
+ * He may be blocked / be an admin, and has a Cart.
+ * @author Camil Staps, s4498062
+ */
+public class User extends DatabaseItem {
+
+ private final String nr;
+ private final String email;
+ private String hash;
+ private final boolean isAdmin;
+ private final Cart cart = new Cart();
+ private boolean isBlocked = false;
+
+ public User (String nr, String email) {
+ this.nr = nr;
+ this.email = email;
+ this.isAdmin = false;
+ }
+
+ public User (String nr, String email, boolean isAdmin) {
+ this.nr = nr;
+ this.email = email;
+ this.isAdmin = isAdmin;
+ }
+
+ public String getNr() {
+ return nr;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public boolean isAdmin() {
+ return isAdmin;
+ }
+
+ public Cart getCart() {
+ return cart;
+ }
+
+ public void setBlocked(boolean set) {
+ isBlocked = set;
+ }
+
+ public boolean isBlocked() {
+ return isBlocked;
+ }
+
+ public Set<Article> getArticles() {
+ Set<Article> articles = Database.getInstance().getArticles();
+ Set<Article> result = new HashSet<>();
+ for (Article a : articles) {
+ if (a.getOwner().getNr().equals(nr)) {
+ result.add(a);
+ }
+ }
+ return result;
+ }
+
+ public Set<Order> getOrders() {
+ Set<Order> orders = Database.getInstance().getOrders();
+ Set<Order> result = new HashSet<>();
+ for (Order o : orders) {
+ if (o.getUser().getNr().equals(nr)) {
+ result.add(o);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Set a random new password for the user
+ * @return the new password
+ */
+ public String setRandomPassword() {
+ String pw = generatePassword();
+ hash = hash(pw);
+ return pw;
+ }
+
+ /**
+ * Hash a password. Currently, this is just the identity function.
+ * @param password
+ * @return
+ */
+ public static String hash(String password) {
+ return password;
+ }
+
+ /**
+ * Generate a random password
+ * @return
+ */
+ private static String generatePassword() {
+ // Only characters that cannot easily be confused
+ final String drawFrom = "123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
+ final Random r = new Random();
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 12; i++) {
+ sb.append(drawFrom.charAt(r.nextInt(drawFrom.length())));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Verify if a password matches the user's password
+ * @param password the password to check
+ * @return
+ */
+ public boolean verify(String password) {
+ return hash(password).equals(hash);
+ }
+
+ @Override
+ public String toString() {
+ return (isAdmin ? "++ " : "") + nr + " (" + email + ")" + (isBlocked ? " -!-" : "");
+ }
+
+ /**
+ * This User as a String, with a detailed and a quick view
+ * @param showSensitive whether to show sensitive data (email, isAdmin, isBlocked) or not
+ * @return
+ */
+ public String toString(boolean showSensitive) {
+ return showSensitive ? toString() : nr;
+ }
+
+} \ No newline at end of file
diff --git a/Week9 Webshop/src/com/camilstaps/shop/UserInteraction.java b/Week9 Webshop/src/com/camilstaps/shop/UserInteraction.java
new file mode 100644
index 0000000..1140089
--- /dev/null
+++ b/Week9 Webshop/src/com/camilstaps/shop/UserInteraction.java
@@ -0,0 +1,198 @@
+/**
+ * Copyright (c) 2015 Camil Staps <info@camilstaps.nl>
+ * See the LICENSE file for copying permission.
+ */
+
+package com.camilstaps.shop;
+
+import java.util.Set;
+
+/**
+ * Interact with the user: provide and request information.
+ * @author Camil Staps, s4498062
+ */
+public abstract class UserInteraction {
+
+ /**
+ * Show a String
+ * @param string
+ */
+ abstract void putString(String string);
+
+ /**
+ * Show a String with a linefeed
+ * @param string
+ */
+ void putStringln(String string) {
+ putString(string + System.lineSeparator());
+ }
+
+ /**
+ * Get a String as input
+ * @return
+ */
+ abstract String getString();
+
+ /**
+ * Get a String as input, after outputting a question
+ * @param question
+ * @return
+ */
+ String getString(String question) {
+ putString(question);
+ return getString();
+ }
+
+ /**
+ * Get a String as input, and throw an Exception when it's empty
+ * @return
+ * @throws InputRequiredException
+ */
+ String getRequiredString() throws InputRequiredException {
+ String result = getString();
+ if (result.isEmpty()) throw new InputRequiredException();
+ return result;
+ }
+
+ /**
+ * Get a String as input, after outputting a question, and throw an
+ * Exception when the input is empty
+ * @param question
+ * @return
+ * @throws InputRequiredException
+ */
+ String getRequiredString(String question) throws InputRequiredException {
+ putString(question);
+ return getRequiredString();
+ }
+
+ /**
+ * Get a float as input
+ * @return
+ */
+ abstract float getFloat();
+
+ /**
+ * Get a float as input, after outputting a question
+ * @param question
+ * @return
+ */
+ float getFloat(String question) {
+ putString(question);
+ return getFloat();
+ }
+
+ /**
+ * Get a boolean as input
+ * @return
+ */
+ abstract boolean getBoolean();
+
+ /**
+ * Get a boolean as input, after outputting a question
+ * @param question
+ * @return
+ */
+ boolean getBoolean(String question) {
+ putString(question);
+ return getBoolean();
+ }
+
+ /**
+ * Get a Command as input
+ * @return
+ */
+ abstract Command getCommand();
+
+ /**
+ * Let the user choose from an array of options, after outputting a question
+ * @param question
+ * @param options
+ * @return the index of the choice in the options array
+ */
+ abstract int getChoice(String question, String[] options);
+
+ /**
+ * Let the user choose from a Set of Articles
+ * @param set
+ * @return
+ */
+ Article getArticle(Set<Article> set) {
+ String[] articleNames = new String[set.size()];
+ Article[] articles = new Article[set.size()];
+ int i = 0;
+ for (Article a : set) {
+ articleNames[i] = a.toString();
+ articles[i++] = a;
+ }
+ return articles[getChoice("Article: ", articleNames)];
+ }
+
+ /**
+ * Let the user choose an Article
+ * @return
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ */
+ Article getArticle() throws InputRequiredException, ItemNotFoundException {
+ String name = getRequiredString("Name article: ");
+ return Database.getInstance().getArticle(name);
+ }
+
+ /**
+ * Let the user choose an Article from the Articles of a specific User
+ * @param user
+ * @return
+ */
+ Article getArticle(User user) {
+ return getArticle(user.getArticles());
+ }
+
+ /**
+ * Let the user choose a category
+ * @return
+ * @throws ItemNotFoundException
+ */
+ Category getCategory() throws ItemNotFoundException {
+ String[] categories = Database.getInstance().getCategoryNames();
+ return Database.getInstance().getCategory(categories[getChoice("Category: ", categories)]);
+ }
+
+ /**
+ * Let the user choose a User
+ * @return
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ */
+ User getUser() throws InputRequiredException, ItemNotFoundException {
+ return Database.getInstance().getUser(getRequiredString("Number user: "));
+ }
+
+ /**
+ * Let the user choose an order
+ * @return
+ * @throws InputRequiredException
+ * @throws ItemNotFoundException
+ */
+ Order getOrder() throws InputRequiredException, ItemNotFoundException {
+ return getOrder(getUser());
+ }
+
+ /**
+ * Let the user choose an order, from the orders of a specific User
+ * @param user
+ * @return
+ */
+ Order getOrder(User user) {
+ Set<Order> orders = user.getOrders();
+ String[] orderStrings = new String[orders.size()];
+ Order[] orderObjects = new Order[orders.size()];
+ int i = 0;
+ for (Order o : orders) {
+ orderStrings[i] = o.toString();
+ orderObjects[i++] = o;
+ }
+ return orderObjects[getChoice("Order: ", orderStrings)];
+ }
+
+} \ No newline at end of file