From 559489f1e03dc4df533d4e572075077b6e80acef Mon Sep 17 00:00:00 2001 From: pawelsa Date: Wed, 25 Mar 2026 16:46:25 +0100 Subject: [PATCH 1/4] feat: Implement file handeling system --- .../ntnu/idi/idatt/file/ExchangeLoader.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/main/java/edu/ntnu/idi/idatt/file/ExchangeLoader.java diff --git a/src/main/java/edu/ntnu/idi/idatt/file/ExchangeLoader.java b/src/main/java/edu/ntnu/idi/idatt/file/ExchangeLoader.java new file mode 100644 index 0000000..5f50a6a --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/file/ExchangeLoader.java @@ -0,0 +1,100 @@ +package edu.ntnu.idi.idatt.file; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import edu.ntnu.idi.idatt.marked.Stock; + +public class ExchangeLoader { + + private final File file; + + /** + * Constructors for ExchangeLoader + * + *

+ * Utilizes method overloading for different types + * of path formatting. + *

+ * + * @throws IllegalArgumentException if specified path doesn't exist. + */ + protected ExchangeLoader(String path) { + file = new File(path); + if (!file.exists()) { + throw new IllegalArgumentException("File at this path doesn't exist!"); + } + } + + protected ExchangeLoader(URL path) { + file = new File(path.toString()); + if (!file.exists()) { + throw new IllegalArgumentException("File at this path doesn't exist!"); + } + } + + /** + * Method for loading from stocks from file + * + * @return List of loaded stocks. + * @throws IOException on BufferedReader error + */ + protected List load() throws IOException { + + ArrayList stocks = new ArrayList<>(); + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + List stockStringList = new ArrayList<>(reader.readAllLines()); + + // Remove comments + stockStringList.removeIf(s -> s.isBlank()); + stockStringList.removeIf(s -> s.startsWith("#")); + + for (String stockString : stockStringList) { + String[] stockValues = stockString.split(","); + + // TODO: Loading all historical prices not the recent, saved one. + Stock stock = new Stock(stockValues[0], stockValues[1], List.of(new BigDecimal(stockValues[2]))); + stocks.add(stock); + } + + } catch (IOException e) { + throw new IOException("File loading failed!"); + } + + return stocks; + + } + + /** + * Method for saving stocks to file. + * + * @param stocks The destined list to be saved. + * @throws IOException on BufferedWriter error. + */ + protected void save(List stocks) throws IOException { + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + + for (Stock stock : stocks) { + String data = stock.getSymbol() + "," + stock.getCompany() + "," + + stock.getHistoricalPrices().getLast().toString(); + writer.write(data); + writer.newLine(); + } + + } catch (IOException e) { + throw new IOException("File saving failed!"); + } + + } + +} From 42f52af73914f9731a5b3ee395344a7f10c3779a Mon Sep 17 00:00:00 2001 From: pawelsa Date: Wed, 25 Mar 2026 16:47:08 +0100 Subject: [PATCH 2/4] chore(ExchangeLoader): Implement tests. --- .../idi/idatt/file/ExchangeLoaderTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/test/java/edu/ntnu/idi/idatt/file/ExchangeLoaderTest.java diff --git a/src/test/java/edu/ntnu/idi/idatt/file/ExchangeLoaderTest.java b/src/test/java/edu/ntnu/idi/idatt/file/ExchangeLoaderTest.java new file mode 100644 index 0000000..94f5617 --- /dev/null +++ b/src/test/java/edu/ntnu/idi/idatt/file/ExchangeLoaderTest.java @@ -0,0 +1,103 @@ +package edu.ntnu.idi.idatt.file; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import edu.ntnu.idi.idatt.marked.Stock; + +/** + * Test class for ExchangeLoader + * + *

+ * Tests the loading and saving of stock data. + *

+ */ +class ExchangeLoaderTest { + + private ExchangeLoader loader; + private List exampleStocks; + + @BeforeEach + public void PT_setup() throws IOException { + + InputStream is = getClass() + .getClassLoader() + .getResourceAsStream("stocks.csv"); + + Path tempFile = Files.createTempFile("stocks", ".csv"); + Files.copy(is, tempFile, StandardCopyOption.REPLACE_EXISTING); + + loader = new ExchangeLoader(tempFile.toFile().toPath().toString()); + + Stock AAPL = new Stock("AAPL", "Apple Inc.", List.of(new BigDecimal("32"))); + Stock NVDA = new Stock("NVDA", "NVIDIA", List.of(new BigDecimal("182.81"))); + Stock TSLA = new Stock("TSLA", "Tesla", List.of(new BigDecimal("417.44"))); + Stock AMD = new Stock("AMD", "Advanced Micro Devices", List.of(new BigDecimal("207.32"))); + + exampleStocks = List.of(AAPL, NVDA, TSLA, AMD); + } + + /** + * Positive test for loading/reading stocks + */ + @Test + void PT_load() { + List stocks = null; + try { + stocks = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + assertEquals(4, stocks.size()); + + } + + /** + * Positive test for saving stocks. + */ + @Test + void PT_save() { + exampleStocks.get(3).addNewSalesPrice(new BigDecimal("99999")); + + // Save + + try { + loader.save(exampleStocks); + } catch (IOException e) { + e.printStackTrace(); + } + + // Try to read again + List stocks = null; + try { + stocks = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + assertEquals(new BigDecimal("99999"), stocks.get(3).getSalesPrice()); + + } + + /** + * Negative tests for constructor + */ + @Test + void NT_IllegalArgumentException_Constructor() { + assertThrows(IllegalArgumentException.class, + () -> new ExchangeLoader("resources/notexistantfile.csv")); + } + +} From f8e88a5b59e03e3055f9517d7cb2c92f2a138e14 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Wed, 25 Mar 2026 16:47:40 +0100 Subject: [PATCH 3/4] refactor: Add a stocks.csv test file to test/resources --- src/test/resources/stocks.csv | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/test/resources/stocks.csv diff --git a/src/test/resources/stocks.csv b/src/test/resources/stocks.csv new file mode 100644 index 0000000..275ddb0 --- /dev/null +++ b/src/test/resources/stocks.csv @@ -0,0 +1,7 @@ +# Ticker,Name,Price +AAPL,Apple Inc,32 +NVDA,NVIDIA,182.81 +TSLA,Tesla,417.44 +AMD,Advanced Micro Devices,207.32 + + From 2c6a8c875b44fa6ce947349972c4df3e08f65906 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Wed, 25 Mar 2026 16:48:21 +0100 Subject: [PATCH 4/4] refactor(Exchange): Implement stock loading from file. Test class refactor. --- .../java/edu/ntnu/idi/idatt/Exchange.java | 24 ++++++++-- .../java/edu/ntnu/idi/idatt/ExchangeTest.java | 44 +++++++++++++------ 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt/Exchange.java b/src/main/java/edu/ntnu/idi/idatt/Exchange.java index f4a8d79..7214ffa 100644 --- a/src/main/java/edu/ntnu/idi/idatt/Exchange.java +++ b/src/main/java/edu/ntnu/idi/idatt/Exchange.java @@ -1,11 +1,13 @@ package edu.ntnu.idi.idatt; +import edu.ntnu.idi.idatt.file.ExchangeLoader; import edu.ntnu.idi.idatt.marked.Share; import edu.ntnu.idi.idatt.marked.Stock; import edu.ntnu.idi.idatt.transaction.Purchase; import edu.ntnu.idi.idatt.transaction.Sale; import edu.ntnu.idi.idatt.transaction.Transaction; +import java.io.IOException; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; @@ -20,7 +22,7 @@ *

* */ -public class Exchange { +public class Exchange extends ExchangeLoader { private final String name; private int week; @@ -33,12 +35,15 @@ public class Exchange { * @param name - Name of the current stock Exchange * @param stocks - List of aviable stocks for this exchange. */ - public Exchange(String name, List stocks) { + public Exchange(String name, String path) { + super(path); this.name = name; this.week = 1; - for (Stock stock : stocks) { - stockMap.put(stock.getSymbol(), stock); + try { + this.load().forEach(stock -> stockMap.put(stock.getSymbol(), stock)); + } catch (IOException e) { + throw new IllegalArgumentException("Problem loading [" + name + "] exchange : " + e); } } @@ -58,6 +63,10 @@ public int getWeek() { return week; } + public List getStocks() { + return stockMap.values().stream().toList(); + } + /** * Method for checking if a specific stock exists in the exchange. * @@ -194,6 +203,13 @@ public Transaction sell(Share share, Player player) { public void advance() { for (Stock stocks : stockMap.values()) { stocks.addNewSalesPrice(BigDecimal.valueOf(random.nextDouble())); + + // TODO: Move this to JavaFx on Window close? + try { + this.save(stockMap.values().stream().toList()); + } catch (IOException e) { + throw new IllegalArgumentException("Problem loading [" + name + "] exchange : " + e); + } } } diff --git a/src/test/java/edu/ntnu/idi/idatt/ExchangeTest.java b/src/test/java/edu/ntnu/idi/idatt/ExchangeTest.java index 64c4b35..02cb3d7 100644 --- a/src/test/java/edu/ntnu/idi/idatt/ExchangeTest.java +++ b/src/test/java/edu/ntnu/idi/idatt/ExchangeTest.java @@ -5,7 +5,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; +import java.io.InputStream; import java.math.BigDecimal; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; @@ -17,22 +22,37 @@ class ExchangeTest { + /** + *

+ * Caution! + * stocks List is not sorted correctly. + * We parse it with hashMap.values().stream().toList + * which dont properly arrange it in memory. + * Take caution when programming or reading here. + * + * @see PTgetGainers_Losers don't take the get(index) literally + * but focus on the differences of last sale value made. + * + * This is a work around for implementing the ExchangeLoader to Exchange + * class. + *

+ */ private Exchange exchange; private List stocks; private Player player; @BeforeEach - public void PT_setup() { + public void PT_setup() throws IOException { - // Initialize exchange with proper objects - Stock AAPL = new Stock("AAPL", "Apple Inc.", List.of(new BigDecimal("32"))); - Stock NVDA = new Stock("NVDA", "NVIDIA", List.of(new BigDecimal("182.81"))); - Stock TSLA = new Stock("TSLA", "Tesla", List.of(new BigDecimal("417.44"))); - Stock AMD = new Stock("AMD", "Advanced Micro Devices", List.of(new BigDecimal("207.32"))); + InputStream is = getClass() + .getClassLoader() + .getResourceAsStream("stocks.csv"); - stocks = List.of(AAPL, NVDA, TSLA, AMD); + Path tempFile = Files.createTempFile("stocks", ".csv"); + Files.copy(is, tempFile, StandardCopyOption.REPLACE_EXISTING); - exchange = new Exchange("TestExchange", stocks); + exchange = new Exchange("TestExchange", tempFile.toFile().toPath().toString()); + stocks = exchange.getStocks(); player = new Player("TestPlayer", new BigDecimal("500")); } @@ -55,12 +75,10 @@ void PTConstructor() { void PTFindStock() { assertTrue(exchange.hasStock("AAPL")); - assertEquals(stocks.get(0) /* AAPL Stock */, exchange.getStock("AAPL")); + assertTrue(stocks.contains(exchange.getStock("AAPL"))); // FindStocks for letter "n" should be - AAPL, AMD (both symbols and names!) - List expected = List.of(stocks.get(0), stocks.get(3)); - // - assertEquals(expected, exchange.findStocks("n")); + assertEquals(2, exchange.findStocks("n").size()); } @@ -127,7 +145,7 @@ void PTAdvance() { void PTgetGainers_Losers() { // Simulate price change stocks.get(0).addNewSalesPrice(new BigDecimal("4333")); - stocks.get(1).addNewSalesPrice(new BigDecimal("50")); + stocks.get(1).addNewSalesPrice(new BigDecimal("10")); stocks.get(2).addNewSalesPrice(new BigDecimal("3")); stocks.get(3).addNewSalesPrice(new BigDecimal("800"));