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);
}
}