diff options
Diffstat (limited to 'Week9 Webshop/src')
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 |