<repo_name>tcampanella/bravonext-test<file_sep>/src/main/java/com/tcampanella/bravonext/iface/Item.java
package com.tcampanella.bravonext.iface;

import java.math.BigDecimal;

/**
 * @author <NAME>
 *
 * General interface for an Item 
 * 
 */
public interface Item {
	
	
	public String getName();
	
	public BigDecimal getPrice();
	
	public int getAmount();
	
	public boolean isImported();
	
	public boolean isExempted();
	
}
<file_sep>/src/main/java/com/tcampanella/bravonext/iface/IList.java
package com.tcampanella.bravonext.iface;

import java.util.List;

/**
 * @author <NAME>
 *
 *	The following interface (generic) let the class that implements it
 *  expose two methods to access a private list of Items
 *
 */
public interface IList<T> {
	
	public List<T> getItems();
	
	public T getItem(int index);

}
<file_sep>/src/test/java/com/tcampanella/bravonext/test/main/ShoppingListTest.java
package com.tcampanella.bravonext.test.main;

import org.junit.Test;

import com.tcampanella.bravonext.main.ShoppingList;

import static org.junit.Assert.*;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * @author <NAME>
 * 
 *  JUnit test case for the class ShoppingList
 *
 */
public class ShoppingListTest {
	
	@Test
	public void testShoppingList() {
		
		/**
		 * Create a new ShoppingList object
		 */
		
		ShoppingList shoppingList = null;
		
		try {
			
			/**
			 * Standard "file.txt" is used
			 */
			
			shoppingList = new ShoppingList();
			
		} catch (IOException e) {
			
			// Should not happen
			e.printStackTrace();
			
		}
		
		/**
		 * Check that the shopping list has been correctly
		 * populated
		 */
		
		assertTrue(shoppingList.getItems().size() > 0);
		assertTrue(shoppingList.getItem(0).getItems().size() > 0);
		
		/**
		 * Check that, in case a wrong file name is provided,
		 * a FileNotFound exception is thrown
		 */
		FileNotFoundException fileNotFoundException = null;
		
		try {
			shoppingList = new ShoppingList("wrong file name");
		} catch (UnsupportedEncodingException e) {
			//should not happen
			e.printStackTrace();
		} catch (FileNotFoundException e) {

			fileNotFoundException = e;
		
		} catch (IOException e) {
			//should not happen
			e.printStackTrace();
		}
		
		assertTrue(fileNotFoundException != null);
		
	}
	
}
<file_sep>/README.md
# Bravonext test
The actual repository is to provide a solution to the test proposed by Bravonext.

The project can be manually built by executing the following maven command:

mvn test assembly:single

A uber jar file will be then generated, by the name bravonextTest.jar. Such jar can be run by executing the following command:

java -jar bravonextTest.jar

N.B.: At the same path where the jar is located, another file should exist, by the name shoppingList.txt

The project has been conceived as follows:

Two main entities exist, ShoppingList and ReceiptList, which contain respectively a list of ShoppingBaskets (a series of items to be bought) and Receipts (a series of items just bought, together with the total cost and amount of taxes). A ReceiptList is created out of an Existing ShoppingList, which must be created (instantiated) before it (the same concept is applied to Receipts-ShoppingBaskets and ReceiptItems-ShoppingItems). 
A ShoppingBasket contains a variable number of ShoppingItems, while a Receipt contains a variable number of ReceiptItems. Each ReceiptItem, Receipt and ReceiptList will always have access to the corresponding entity (ShoppingItem, ShoppingBasket, ShoppingList).

No additional library/framework has been used in this project
<file_sep>/src/main/java/com/tcampanella/bravonext/iface/IReference.java
package com.tcampanella.bravonext.iface;

/**
 * @author <NAME>
 *
 * Generic interface to obtain a reference
 * to a related entity
 */
public interface IReference<T> {
	
	public T getReference();

}
<file_sep>/src/main/java/com/tcampanella/bravonext/main/ShoppingBasket.java
package com.tcampanella.bravonext.main;

import java.util.ArrayList;
import java.util.List;

import com.tcampanella.bravonext.iface.IShoppingBasket;
import com.tcampanella.bravonext.iface.Item;

/**
 * @author <NAME>
 * 
 * Class representing a Shopping Basket
 *
 */
public class ShoppingBasket implements IShoppingBasket {
	
	/**
	 * List of ShoppingItems
	 */
	private final List<Item> shoppingIems = new ArrayList<Item>();
	
	/**
	 * 
	 * A ShoppingBasket is created out of a list of ShoppingItems
	 * 
	 * @param shoppingIems
	 */
	public ShoppingBasket(List<Item> shoppingIems) {
	
		this.shoppingIems.addAll(shoppingIems);
	}
	
	/* 
	 * @see com.tcampanella.last_minute.main.IList#getItems()
	 */
	public List<Item> getItems() {
		
		return shoppingIems;
	}
	
	/* 
	 * @see com.tcampanella.last_minute.main.IList#getItem(int index)
	 */
	public Item getItem(int index) {
		
		return this.shoppingIems.get(index);
	}

}
<file_sep>/src/main/java/com/tcampanella/bravonext/main/Receipt.java
package com.tcampanella.bravonext.main;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import com.tcampanella.bravonext.iface.IReceipt;
import com.tcampanella.bravonext.iface.IReference;
import com.tcampanella.bravonext.iface.IShoppingBasket;
import com.tcampanella.bravonext.iface.Item;

/**
 * @author <NAME>
 * 
 * Class representing a Receipt
 *
 */
public class Receipt implements IReference<ShoppingBasket>, IReceipt {
	
	/**
	 * List of ReceiptItem
	 */
	private final List<Item> receiptItems = new ArrayList<Item>();	
	
	/**
	 * Total cost of the Receipt (including taxes)
	 */
	private BigDecimal total_cost = new BigDecimal("0.0");
	
	/**
	 * Total taxes of the Receipt
	 */
	private BigDecimal total_taxes = new BigDecimal("0.0");
	
	/**
	 * Reference to the ShoppingBasket a Receipt
	 * is created from
	 */
	private final IShoppingBasket shoppingBasket;
	
	
	/**
	 * 
	 * @param shoppingBasket
	 */
	public Receipt(ShoppingBasket shoppingBasket) {
		
		this.shoppingBasket = shoppingBasket;
		generateReceipt();
		
	}
	
	/**
	 * Method to populate a Receipt from a ShoppingBasket
	 */
	private void generateReceipt() {
		
		for(Item shoppingItem : shoppingBasket.getItems()) {
			
			Item receiptItem = new ReceiptItem(shoppingItem);
			total_cost = 
					total_cost.add(receiptItem.getPrice().multiply(new BigDecimal(""+receiptItem.getAmount())));
			total_taxes = 
					total_taxes.add(receiptItem.getPrice().subtract(((ReceiptItem)receiptItem).getReference().getPrice()));

			receiptItems.add(receiptItem);
			
		}
		
	}
	
	public BigDecimal getTotal_cost() {
		return total_cost;
	}

	/**
	 * @return the total_taxes
	 */
	public BigDecimal getTotal_taxes() {
		return total_taxes;
	}
	
	/* 
	 * @see com.tcampanella.last_minute.main.IReference#getReference()
	 */
	public ShoppingBasket getReference() {

		return (ShoppingBasket) this.shoppingBasket;
	}
	
	/**
	 * The following method will return a String formatted as follows:
	 * 
	 	1 book : 12.49
		1 music CD: 16.49
		1 chocolate bar: 0.85
		
		Sales Taxes: 1.50 Total: 29.83
		
	 */
	@Override
	public String toString() {
	
		StringBuilder string = new StringBuilder();
        
		for(Item item : receiptItems)
			string.append(""+item.getAmount() + " "+item.getName() + ": " + item.getPrice() +"\n"); 

		string.append("\nSales Taxes: " + this.getTotal_taxes() + " Total: " + this.getTotal_cost()+"\n\n");
		
		return string.toString();
		
	}
	
	/* 
	 * @see com.tcampanella.last_minute.main.IList#getItems()
	 */
	public List<Item> getItems() {
		
		return receiptItems;
	}
	
	/* 
	 * @see com.tcampanella.last_minute.main.IList#getItem(int index)
	 */
	public Item getItem(int index) {
		
		return receiptItems.get(index);
	}

}
<file_sep>/src/main/java/com/tcampanella/bravonext/main/ReceiptItem.java
package com.tcampanella.bravonext.main;

import java.math.BigDecimal;

import com.tcampanella.bravonext.iface.IReference;
import com.tcampanella.bravonext.iface.Item;
import com.tcampanella.bravonext.util.Util;

/**
 * 
 * @author <NAME>
 *
 * Class representing a single Item in a Receipt
 */
public class ReceiptItem implements Item, IReference<Item> {

	/**
	 *  Item name
	 */
	private String name;
	
	/**
	 * Item price (with taxes)
	 */
	private BigDecimal price;
	
	/**
	 * Number of items
	 */
	private int amount;

	/**
	 * Good exempted from basic sales taxes
	 */
	boolean exempted;
	
	/**
	 * Good imported
	 */
	private boolean imported;
	
	/**
	 * A reference to the related sshoppingItem
	 */
	private final Item shoppingItem;
	
	/**
	 * Utility class
	 */
	private static final Util util = new Util();
	
	/*
	 * 
	 * @param shoppingItem
	 * 
	 * A ReceiptItem is created out of an existing SshoppingItem
	 */
	public ReceiptItem(Item shoppingItem) {
		
		this.shoppingItem = shoppingItem;
		this.name = shoppingItem.getName();
		this.amount = shoppingItem.getAmount();
		this.exempted = shoppingItem.isExempted();
		this.imported = shoppingItem.isImported();
		
		/**
		 * The price is calculated including the necessary taxes
		 */
		this.price = util.calculateTaxes(shoppingItem).add(shoppingItem.getPrice()); 
	}
	
	/* 
	 * @see com.tcampanella.last_minute.main.Item#getName()
	 */
	public String getName() {
		
		return this.name;
	}

	/* 
	 * @see com.tcampanella.last_minute.main.Item#getPrice()
	 */
	public BigDecimal getPrice() {
		
		return this.price;
	}

	/* 
	 * @see com.tcampanella.last_minute.main.Item#getAmount()
	 */
	public int getAmount() {

		return this.amount;
	}
	
	public boolean isExempted() {

		return this.exempted;
	}
	/*
	 * @see com.tcampanella.last_minute.main.Item#isImported()
	 */
	public boolean isImported() {

		return this.imported;
	}

	/* 
	 * @see com.tcampanella.last_minute.main.IReference#getReference()
	 */
	public Item getReference() {

		return this.shoppingItem;
	}

}
<file_sep>/src/main/java/com/tcampanella/bravonext/main/ShoppingList.java
package com.tcampanella.bravonext.main;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import com.tcampanella.bravonext.iface.IShoppingList;
import com.tcampanella.bravonext.util.Util;

/**
 * @author <NAME>
 *
 */
public class ShoppingList implements IShoppingList {
	
	private List<ShoppingBasket> shoppingBaskets = new ArrayList<ShoppingBasket>();
	private final Util util = new Util();
	private final static String DEFAULT_FILE_NAME = "shoppingList.txt";
	
	
	public ShoppingList() throws UnsupportedEncodingException, FileNotFoundException, IOException {
		
		this(DEFAULT_FILE_NAME);
		
	}
	
	public ShoppingList(String fileName) throws UnsupportedEncodingException, FileNotFoundException, IOException {
		
		
		shoppingBaskets.addAll(util.readShoppingList(new FileInputStream("./" + fileName)));
			
	}

	public List<ShoppingBasket> getItems(){
		
		return this.shoppingBaskets;
	}
	
	public ShoppingBasket getItem(int index) {
		
		return shoppingBaskets.get(index);
	}

}
<file_sep>/src/test/java/com/tcampanella/bravonext/test/util/UtilTest.java
package com.tcampanella.bravonext.test.util;

import static org.junit.Assert.*;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.tcampanella.bravonext.iface.Item;
import com.tcampanella.bravonext.main.ShoppingBasket;
import com.tcampanella.bravonext.main.ShoppingItem;
import com.tcampanella.bravonext.util.Util;

/**
 * 
 * @author <NAME>
 * 
 *  JUnit test case for the class Util
 *
 */
public class UtilTest {
	
	@Test
	public void testisExempt() {
		
		/**
		 * Create a new Util class
		 */
		
		Util util = new Util();
		
		/**
		 * Check the correct behaviour of isExempt(String name)
		 * 
		 * exemption list --> { "chocolate","book","headache pills"}
		 */
		
		assertTrue(util.isExempt("book"));
		assertTrue(util.isExempt("Book"));
		assertFalse(util.isExempt("music CD"));
		assertTrue(util.isExempt("headache pills"));
		
	}
	
	@Test
	public void testCalculateTaxes() {
		
		/**
		 * Create a new Util class
		 */
		
		Util util = new Util();
		
		/**
		 * Check the correct behaviour of calculateTaxes(Item item)
		 */
		
		Item item1 = new ShoppingItem("name", new BigDecimal("1.51"), 1, true, true);
		
		assertTrue(util.calculateTaxes(item1).compareTo(new BigDecimal("0.10")) == 0);
		
		Item item2 = new ShoppingItem("name", new BigDecimal("1.51"), 1, false, true);
		
		assertTrue(util.calculateTaxes(item2).compareTo(new BigDecimal("0.25")) == 0);
		
		Item item3 = new ShoppingItem("name", new BigDecimal("1.51"), 1, false, false);
		
		assertTrue(util.calculateTaxes(item3).compareTo(new BigDecimal("0.20")) == 0);
		
	}
	
	@Test
	public void testRoundNumber() {
		
		/**
		 * Create a new Util class
		 */
		
		Util util = new Util();
		
		/**
		 * Check the correct behaviour of roundNumber(BigDecimal numberToBeRounded)
		 */
		
		assertTrue(util.roundNumber(new BigDecimal("0.013")).compareTo(new BigDecimal("0.05")) == 0);
		
		assertTrue(util.roundNumber(new BigDecimal("7.123")).compareTo(new BigDecimal("7.15")) == 0);
		
		assertTrue(util.roundNumber(new BigDecimal("7.120001")).compareTo(new BigDecimal("7.15")) == 0);
		
	}
	
	@Test
	public void testReadShoppingList() {
		
		/**
		 * Create a new Util class
		 */
		
		Util util = new Util();
		
		/**
		 * Create a new list of ShoppingBasket
		 */
		
		List<ShoppingBasket> shoppingBaskets = new ArrayList<ShoppingBasket>();
		
		try {
			/**
			 * Check the correct behaviour of readShoppingList(FileInputStream inputStream)
			 */
			shoppingBaskets = util.readShoppingList(new FileInputStream("./" + "shoppingList.txt"));

		} catch (IOException e) {
			// should not be reached
			
			e.printStackTrace();
		}
		
		/**
		 * Check that the list has been
		 * correctly populated
		 */
		
		assertTrue(shoppingBaskets.size() == 6);
		assertTrue(shoppingBaskets.get(0).getItem(0).getName().equals("book"));
		
		/**
		 * Check that readShoppingList(FileInputStream inputStream), if invoked
		 * with a not existing file name, it would throw a FileNotFoundException
		 */
		
		FileNotFoundException fileNotFoundException = null;
		
		try {
			
			shoppingBaskets = util.readShoppingList(new FileInputStream("./" + "file_not_existing.txt"));
		
		} catch (FileNotFoundException e) {
			// should not be reached
		
			fileNotFoundException = e;
	
		} catch (IOException e) {
			// should not be reached
		
			e.printStackTrace();
		}
		
		assertTrue(fileNotFoundException != null);
		
	}
	
}

<file_sep>/src/test/java/com/tcampanella/bravonext/test/main/ReceiptTest.java
package com.tcampanella.bravonext.test.main;

import static org.junit.Assert.assertTrue;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.tcampanella.bravonext.iface.Item;
import com.tcampanella.bravonext.main.Receipt;
import com.tcampanella.bravonext.main.ShoppingBasket;
import com.tcampanella.bravonext.main.ShoppingItem;

/**
 * @author <NAME>
 *
 * JUnit test case for the class Receipt
 */
public class ReceiptTest {
	
	@Test
	public void test() {
		
		/**
		 * Create a new list of Items
		 */
		
		List<Item> shoppingItems = new ArrayList<Item>();
		
		for(int i = 0; i< 10; i++)
			shoppingItems.add(new ShoppingItem("name "+ i, new BigDecimal("1.10"), 1, false, false));
		
		/**
		 * Create a new shopping basket with 
		 * the list of items (shoppingItems)
		 */
		
		ShoppingBasket shoppingBasket = new ShoppingBasket(shoppingItems);
		
		/**
		 * Create a new Receipt
		 */
		
		Receipt receipt = new Receipt(shoppingBasket);
		
		/**
		 * Check that the receipt has been 
		 * correctly populated
		 */
		
		assertTrue(receipt.getItems().size() == 10);
		assertTrue(receipt.getItem(1).getName().equals("name 1"));
		
		/**
		 * Check that the getReference returns the correct ShoppingBasket
		 */
		
		assertTrue(receipt.getReference().equals(shoppingBasket));
		
		/**
		 * Check that the total cost and total amount of taxes
		 * have been correctly calculated given the list of items (shoppingItems)
		 */
		
		assertTrue(receipt.getTotal_cost().equals(new BigDecimal("12.50")));
		assertTrue(receipt.getTotal_taxes().equals(new BigDecimal("1.50")));		
			
	}

}
