diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java index 0ef6962..10ae9b9 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java @@ -86,7 +86,7 @@ public void start(final Stage stage) throws Exception { ViewManager viewManager = new ViewManager(stage, eventManager); List stocksInFile; - StockFileManager parser1 = new StockFileManager("/sp500.csv"); + StockFileManager parser1 = new StockFileManager("src/main/resources/sp500.csv"); StockFileParser converter1 = new StockFileParser(); stocksInFile = converter1.getStocksFromStrings(parser1.readFile()); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java index b1a782d..ac5cb7c 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java @@ -1,6 +1,10 @@ package edu.ntnu.idi.idatt2003.g40.mappe.service; -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -72,7 +76,7 @@ private enum ParserRuleSet { try { new BigDecimal(s); return true; - } catch (NumberFormatException e) { + } catch (NumberFormatException _) { return false; } }), @@ -109,6 +113,9 @@ public StockFileManager(final String pathName) { /** * Reads the file and returns a list element of all valid stocks as strings. * + *

If file is not found, + * falls back to default file in resources folder.

+ * *

Uses {@link BufferedReader} for opening a file stream.

* * @return {@link List} object of all valid stock strings in file. @@ -117,17 +124,20 @@ public StockFileManager(final String pathName) { * * @see Path * */ - public List readFile() throws IOException { - try (InputStream inputStream = getClass().getResourceAsStream(pathName); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + Path path = Paths.get(pathName); + + if (!Files.exists(path)) { + extractResourceFallback(path); + } - List allLines = bufferedReader.readAllLines(); - List readableLines = - allLines.stream() - .filter(ParserRuleSet.VALID_FORMAT.rule).toList(); + try (BufferedReader bufferedReader = + Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + List allLines = bufferedReader.lines().toList(); + List readableLines = allLines.stream() + .filter(ParserRuleSet.VALID_FORMAT.rule) + .toList(); - // Valid lines (following the correct regular expressions) return readableLines.stream().filter(s -> { String[] parts = s.trim().split(","); @@ -135,20 +145,45 @@ public List readFile() throws IOException { return false; } - boolean validCode = ParserRuleSet - .VALID_CODE.rule.test(parts[0].trim()); - - boolean validName = ParserRuleSet - .VALID_NAME.rule.test(parts[1].trim()); - - boolean validPrice = ParserRuleSet - .VALID_PRICE.rule.test(parts[2].trim()); + boolean validCode = + ParserRuleSet.VALID_CODE.rule.test(parts[0].trim()); + boolean validName = + ParserRuleSet.VALID_NAME.rule.test(parts[1].trim()); + boolean validPrice = + ParserRuleSet.VALID_PRICE.rule.test(parts[2].trim()); return validCode && validName && validPrice; }).toList(); } catch (IOException e) { - throw new IOException("File parser could not parse file!"); + throw new IOException("File parser could not parse file!", e); + } + } + + /** + * Extracts the fallback template file from the application resources to + * the local file system. + * + * @param targetPath path to send the fallback file. + * + * @throws IOException if resource or input stream is not found or null. + */ + private void extractResourceFallback(final Path targetPath) + throws IOException { + String resourceName = "/sp500.csv"; + + try (InputStream inputStream = getClass().getResourceAsStream(resourceName)) { + if (inputStream == null) { + throw new FileNotFoundException( + "Resource file not found in JAR: " + resourceName + ); + } + + if (targetPath.getParent() != null) { + Files.createDirectories(targetPath.getParent()); + } + + Files.copy(inputStream, targetPath); } } diff --git a/src/main/resources/testStockData.txt b/src/main/resources/testStockData.txt deleted file mode 100644 index b349d0e..0000000 --- a/src/main/resources/testStockData.txt +++ /dev/null @@ -1,11 +0,0 @@ -#THIS IS A COMMENT. - -AAPL, Apple Inc., 276.43 -NVID, Nvidida Corporation, 241.591 - -#Above me are some valid formats. -#Below me are some invalid formats - -COOLI, This is a cool name, 252.2 - -COOL, This is a cool name, 252.2a \ No newline at end of file diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java index 9706b5c..6630d44 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java @@ -1,60 +1,66 @@ package edu.ntnu.idi.idatt2003.g40.mappe.service; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; class StockFileManagerTest { - private final String testStockDataPath = "/testStockData.txt"; - - private final String absoluteTestStockDataPath = "src/main/resources/testStockData.txt"; - StockFileManager stockFileManager; - - private final String validStockFromFile = "NVID, Nvidida Corporation, 241.591"; - - private final String invalidStockFromFile = "COOLI, This is a cool name, 252.2"; + /** + * Path used to test file system. + * */ + @TempDir + private Path tempDir; - private final String commentFromFile = "#Above me are some valid formats."; - - private List allLines = new ArrayList<>(); + @Test + void readFileParsesValidStocksAndFiltersOutCommentsAndInvalidData() + throws IOException { + Path tempFile = tempDir.resolve("testStockData.txt"); + List mockFileData = List.of( + "# This is a comment header line and should be skipped", + "NVID, Nvidida Corporation, 241.591", + "AAPL, Apple Inc, 175.50", + "", + "INVALID_ROW_MISSING_COLUMNS", + "COOLI, This is a cool name but missing price token" + ); + Files.write(tempFile, mockFileData); - private List validStocks = new ArrayList<>(); + StockFileManager stockFileManager = + new StockFileManager(tempFile.toString()); + List parsingResults = stockFileManager.readFile(); - @BeforeEach - void setUp() throws Exception { - stockFileManager = new StockFileManager(testStockDataPath); - Path path = Paths.get(absoluteTestStockDataPath); - allLines = Files.readAllLines(path); - try { - validStocks = stockFileManager.readFile(); - } catch (Exception _) { - throw new Exception("Test failed"); - } + assertEquals(2, parsingResults.size()); + assertTrue(parsingResults.contains("NVID, Nvidida Corporation, 241.591")); + assertTrue(parsingResults.contains("AAPL, Apple Inc, 175.50")); + assertFalse(parsingResults.stream().anyMatch(line -> line.startsWith("#"))); } @Test - void parser_gets_valid_stock_from_file() { - assertTrue(allLines.contains(validStockFromFile)); - assertTrue(validStocks.contains(validStockFromFile)); - } + void readFileHandlesEmptyFileGracefully() throws IOException { + Path emptyFile = tempDir.resolve("emptyStockData.txt"); + Files.createFile(emptyFile); - @Test - void parser_skips_comments_from_file() { - assertTrue(allLines.contains(commentFromFile)); - assertFalse(validStocks.contains(commentFromFile)); + StockFileManager stockFileManager = new StockFileManager(emptyFile.toString()); + List results = stockFileManager.readFile(); + + assertTrue(results.isEmpty()); } @Test - void parser_skips_invalid_stock_from_file() { - assertTrue(allLines.contains(invalidStockFromFile)); - assertFalse(validStocks.contains(invalidStockFromFile)); + void constructorOrReadFileDoesNotThrowExceptionOnMissingFile() { + StockFileManager stockFileManager = + new StockFileManager("non_existent_directory/missing_file.txt"); + + assertDoesNotThrow(stockFileManager::readFile); } -} \ No newline at end of file +}