Receipt Calculator Simulation

Good.java package receipts; import java.text.DecimalFormat; // Class representing good in the shop basket. Stores number of this good in the basket, price of one instance of this good. public class Good { // Keywords for determination if good is exempt or not private static final String[] exemptKeyWords = {"food", "book", "medicine", "chocolate", "pill"}; // Format for outputting decimal numbers private static DecimalFormat df = new DecimalFormat("0.00"); // Instance private fields of class private String name; private double price; private int number; private boolean isImported; private boolean isExempt; public Good(String name, double price, int number) throws IllegalArgumentException{ // Throwing exceptions if parameters are illegal if (price <= 0.0) { throw new IllegalArgumentException("Price must be positive"); } if (number <= 0.0) { throw new IllegalArgumentException("Quantity must be positive integer"); } this.name = name; this.price = price; this.number = number; isImported = false; isExempt = false; // Checking if good is imported or exempt String lcName = name.toLowerCase(); if (lcName.contains("imported")) { isImported = true; } // If name of good contains one of keywords, then good is exempt for (String exemptKeyWord : exemptKeyWords) { if (lcName.contains(exemptKeyWord)) { isExempt = true; break; } } } public Good(String name, double price) { this(name, price, 1); } // Getter for name field public String getName() { return name; } // Adding more instances of the same good (increasing number field of the class). // If prices differ, returns false. public boolean addQuantity(Good good) { if (this.price != good.price) { return false; } this.number += good.number; return true; } // Checking if name of the current good matches given name public boolean equals(String name) { return this.name.equals(name); } // Return overall sale tax for 'number' of goods public double getTax() { if (isExempt) { return 0.0; } return number*Math.ceil((price*0.1)/0.05)*0.05; } // Return overall import tax for 'number' of goods public double getImportTax() { if (!isImported) return 0.0; return number*Math.ceil((price*0.05)/0.05)*0.05; } // Calculates overall price for 'number' of goods (including taxes) public double getOverallPrice() { return number*price + getTax() + getImportTax(); } // Returns the string representation of good info. public String toString() { StringBuffer buf = new StringBuffer(); buf.append(name + ": " + df.format(getOverallPrice())); if (number >1) { buf.append(" (" + number + " @ " + df.format(getOverallPrice()/number) + ")"); } return buf.toString(); } } Receipt.java package receipts; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.InputMismatchException; import java.util.List; import java.util.NoSuchElementException; import java.util.Scanner; // Class representing selected set of goods. public class Receipt { // Format for outputting decimal numbers private static DecimalFormat df = new DecimalFormat("0.00"); // List, containing all selected goods private List basket; private double totalPrice; private double taxes; public Receipt() { basket = new ArrayList<>(); taxes = 0; } // Adding good to the basket. If no such kind of good was selected previously, than adding new good instance to the basket list. // Otherwise, increasing number field in corresponding good instance. public void addGood(Good good) throws IllegalArgumentException{ for (Good g : basket) { if (g.equals(good.getName())) { if (!g.addQuantity(good)) { throw new IllegalArgumentException("Two goods with the same name but different data are found."); } return; } } basket.add(good); } // This method outputs all necessary info public void outputAll() { for (Good g : basket) { // Cumulating total overall cost and taxes taxes += (g.getTax() + g.getImportTax()); totalPrice += g.getOverallPrice(); // Printing separate good info System.out.println(g); } System.out.println("Sales taxes: " + df.format(taxes)); System.out.println("Total price: " + df.format(totalPrice)); } // Main method. Reads items from console, until empty string is entered public static void main(String[] args) { Scanner scanner = new Scanner(System.in); Receipt receipt = new Receipt(); // Reading from console while(true) { // If line is empty, then quit String line = scanner.nextLine(); if (line.equals("")) { break; } // Scanning entered line Scanner lineScan = null; try { // Line must contain word "at" int index = line.lastIndexOf("at"); if (index == -1) { throw new IllegalArgumentException("Illegal input format: no \'at\' found."); } // Reading price after word "at". Can throw NumberFormatException String priceLine = line.substring(index+2); double price = Double.parseDouble(priceLine); lineScan = new Scanner(line.substring(0, index)); // Reading item quantity in the beginning of the line. Can throw InputMismatchException int number = lineScan.nextInt(); // Trying to read good name. NoSuchElementException can be thrown if name is empty. String name = lineScan.next(); while (lineScan.hasNext()) { name += (" " + lineScan.next()); } // Creating new item instance and putting it to the basket receipt.addGood(new Good(format(name), price, number)); lineScan.close(); } // Processing errors catch(NumberFormatException nfe) { System.out.println("Can not read price"); } catch (InputMismatchException ime) { System.out.println("Can not read quantity"); lineScan.close(); } catch (NoSuchElementException nsee) { System.out.println("Good item name is empty"); lineScan.close(); } catch(IllegalArgumentException iae) { if (lineScan != null) { lineScan.close(); } System.out.println(iae.getMessage()); } } scanner.close(); // Printing results receipt.outputAll(); } // Formatting good name. First letter of it must be capital. private static String format(String word) { return word.substring(0,1).toUpperCase() + word.substring(1); } }