diff options
| author | Camil Staps | 2015-04-18 13:44:44 +0200 | 
|---|---|---|
| committer | Camil Staps | 2015-04-18 13:44:44 +0200 | 
| commit | 6a44b074f0169a1b0f9e92347af929c5e471746e (patch) | |
| tree | ae5663fe7c69881bf4ecfedbef99c2505f8ec964 /Week9 Webshop/src | |
| parent | Added copyright to docs (diff) | |
Reorganised projects
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 | 
