diff --git a/src/main/java/millions/App.java b/src/main/java/millions/App.java index 9b459b9..cb1b7d2 100644 --- a/src/main/java/millions/App.java +++ b/src/main/java/millions/App.java @@ -1,18 +1,22 @@ package millions; import java.math.BigDecimal; +import java.util.logging.Level; +import java.util.logging.Logger; + import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.stage.Stage; import millions.controller.GameController; +import millions.controller.fileIO.CSV.CSVStockFileWriter; import millions.controller.fileIO.InvalidFormatException; import millions.view.GameView; import millions.view.StartView; /** Main JavaFX application entry point for the Millions stock trading game. */ public class App extends Application { - + private static final Logger logger = Logger.getLogger(App.class.getName()); @Override public void start(Stage stage) { GameController controller = new GameController(); @@ -36,15 +40,16 @@ public void start(Stage stage) { Scene gameScene = new Scene(gameView, 1920, 1080); stage.setScene(gameScene); } catch (InvalidFormatException e) { + logger.log(Level.WARNING, "InvalidFormatException: " + e.getMessage()); Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("Error"); alert.setHeaderText("Error with selected file"); - alert.setContentText("Please control the format of the selected file"); + alert.setContentText(e.getMessage() + "\nPlease control the format of the selected file"); alert.showAndWait(); } catch (RuntimeException ex) { - System.err.println(ex); + logger.log(Level.SEVERE, ex.getMessage()); System.exit(0); } }); diff --git a/src/main/java/millions/controller/fileIO/CSV/CSVFileHandler.java b/src/main/java/millions/controller/fileIO/CSV/CSVFileHandler.java index 5a7d9b8..bcd783d 100644 --- a/src/main/java/millions/controller/fileIO/CSV/CSVFileHandler.java +++ b/src/main/java/millions/controller/fileIO/CSV/CSVFileHandler.java @@ -7,6 +7,9 @@ import java.nio.file.Path; import java.util.List; +/** + *

Bundles StockFileReader and CSVStockFileParser together to reduce boilerplate code when reading stocks from a csv file

+ */ public class CSVFileHandler { StockFileReader reader; CSVStockFileParser parser; @@ -16,6 +19,12 @@ public CSVFileHandler() { this.parser = new CSVStockFileParser(); } + /** + * Reads and parses stocks from a csv file + * @param filePath Path to stock file + * @return list of stock object created from parsed file + * @throws InvalidFormatException Throws an InvalidFormatException received from parser + */ public List getStocksFromFile(Path filePath) { try { StockFileReader reader = new StockFileReader(); diff --git a/src/main/java/millions/controller/fileIO/CSV/CSVStockFileParser.java b/src/main/java/millions/controller/fileIO/CSV/CSVStockFileParser.java index 2eab5ab..9208bbc 100644 --- a/src/main/java/millions/controller/fileIO/CSV/CSVStockFileParser.java +++ b/src/main/java/millions/controller/fileIO/CSV/CSVStockFileParser.java @@ -12,13 +12,31 @@ public class CSVStockFileParser { public CSVStockFileParser() {} - // returns true if all entries have exactly 3 data points + /** + * Verifies the amount of data fields present in supplied CSV file. + * + * @param lines Lines from csv file + * @return Boolean: True if file satisfies expected format (3 fields) + */ public boolean verifyCSV(List lines) { return lines.stream() .filter(l -> !(l.startsWith("#") || l.isBlank())) .noneMatch(l -> l.split(",").length != 3); } + /** + * Parses the supplied lines if they satisfy the correct format expectations + * + * @param lines

lines to be parsed.
+ * Each line must contain three data fields: String,String,BigDecimal
+ * (Fields cannot be blank)
+ * blank lines or lines beginning with '#' are ignored + *

+ * @return List of stock objects created from the supplied lines + * @throws InvalidFormatException If one or more lines contain too many or too few data fields + * @throws InvalidFormatException If the BigDecimal field on one or more lines are not compatible + * @throws InvalidFormatException Upon recieving an IllegalArgumentException (Either Symbol or Company name is blank) + */ public List parse(List lines) { List stocks = new ArrayList<>(); if (verifyCSV(lines)) { @@ -26,14 +44,20 @@ public List parse(List lines) { .filter(l -> !((l.startsWith("#") || l.isBlank()))) .forEach( l -> { - String[] split = l.split(","); - String symbol = split[0]; - String company = split[1]; - BigDecimal price = new BigDecimal(split[2]); - stocks.add(new Stock(symbol, company, price)); + try { + String[] split = l.split(","); + String symbol = split[0]; + String company = split[1]; + BigDecimal price = new BigDecimal(split[2]); + stocks.add(new Stock(symbol, company, price)); + } catch (NumberFormatException e) { + throw new InvalidFormatException("Error with number conversion on line: " + l + "\n" + "Last field must be a number"); + } catch (IllegalArgumentException e) { + throw new InvalidFormatException("Illegal argument on line: " + l + "\n" + e.getMessage()); + } }); } else { - throw new InvalidFormatException("Incorrect format for CSV File"); + throw new InvalidFormatException("Incorrect format for CSV File: incorrect amount of data fields detected on one or more lines"); } return stocks; } diff --git a/src/main/java/millions/controller/fileIO/CSV/CSVStockFileWriter.java b/src/main/java/millions/controller/fileIO/CSV/CSVStockFileWriter.java index 0233a7c..fdce35d 100644 --- a/src/main/java/millions/controller/fileIO/CSV/CSVStockFileWriter.java +++ b/src/main/java/millions/controller/fileIO/CSV/CSVStockFileWriter.java @@ -1,27 +1,30 @@ package millions.controller.fileIO.CSV; -import millions.controller.fileIO.StockFileWriter; -import millions.model.Stock; - -import java.io.BufferedWriter; import java.io.*; +import java.io.BufferedWriter; import java.nio.file.Path; import java.util.List; +import java.util.logging.Logger; + +import millions.controller.fileIO.StockFileWriter; +import millions.model.Stock; -//TODO: Validation of data before writing /** - * Writes stock data to a CSV file. + * Implements StockFileWriter.
+ * Converts a list of stock objects into a writeable string with a CSV format. + * */ public class CSVStockFileWriter implements StockFileWriter { - private final List stocks; + private static final Logger logger = Logger.getLogger(CSVStockFileWriter.class.getName()); private String finalString; - public CSVStockFileWriter(List stocks) { - this.stocks = stocks; - } + public CSVStockFileWriter() {} + /** + * Formats given string to CSV format to prepare for writing to file + */ @Override - public void formatString() { + public String formatString(List stocks) { StringBuilder builder = new StringBuilder(); stocks.forEach(stock -> { builder.append(stock.getSymbol()); @@ -31,16 +34,22 @@ public void formatString() { builder.append(stock.getSalesPrice().toString()); builder.append("\n"); }); - this.finalString = builder.toString(); + return builder.toString(); } + /** + * Writes the saved string to a file + * @param stocks List of stock objects to write + * @param path Path to desired file + * @return Boolean for success + */ @Override - public boolean write(Path path){ + public boolean write(List stocks, Path path){ try (FileWriter fw = new FileWriter(path.toString()); BufferedWriter writer = new BufferedWriter(fw);) { - this.formatString(); + this.formatString(stocks); writer.write(finalString); } catch (IOException e) { - e.printStackTrace(); + logger.severe(e.getMessage()); } return false; } diff --git a/src/main/java/millions/controller/fileIO/InvalidFormatException.java b/src/main/java/millions/controller/fileIO/InvalidFormatException.java index 6883256..a1e2136 100644 --- a/src/main/java/millions/controller/fileIO/InvalidFormatException.java +++ b/src/main/java/millions/controller/fileIO/InvalidFormatException.java @@ -1,5 +1,8 @@ package millions.controller.fileIO; +/** + * Exception to be thrown when verifying the format of files + */ public class InvalidFormatException extends RuntimeException { public InvalidFormatException(String message) { super(message); diff --git a/src/main/java/millions/controller/fileIO/StockFileReader.java b/src/main/java/millions/controller/fileIO/StockFileReader.java index 33956b1..f873896 100644 --- a/src/main/java/millions/controller/fileIO/StockFileReader.java +++ b/src/main/java/millions/controller/fileIO/StockFileReader.java @@ -17,6 +17,11 @@ public class StockFileReader { public StockFileReader() {} + /** + * Reads the file found at the specified path + * @param path Path to the desired file + * @return List of each line in the file as a string + */ public List readFile(Path path) { File file = new File(path.toString()); List lines = new ArrayList<>(); diff --git a/src/main/java/millions/controller/fileIO/StockFileWriter.java b/src/main/java/millions/controller/fileIO/StockFileWriter.java index cfd1baf..a85f363 100644 --- a/src/main/java/millions/controller/fileIO/StockFileWriter.java +++ b/src/main/java/millions/controller/fileIO/StockFileWriter.java @@ -1,11 +1,14 @@ package millions.controller.fileIO; +import millions.model.Stock; + import java.nio.file.Path; +import java.util.List; /** * Interface for writing stock data to a file. */ public interface StockFileWriter { - public void formatString(); - public boolean write(Path path); + String formatString(List stocks); + boolean write(List stocks, Path path); } diff --git a/src/test/java/millions/CSVStockFileParserTest.java b/src/test/java/millions/CSVStockFileParserTest.java index df753aa..8266d35 100644 --- a/src/test/java/millions/CSVStockFileParserTest.java +++ b/src/test/java/millions/CSVStockFileParserTest.java @@ -1,33 +1,75 @@ package millions; import millions.controller.fileIO.CSV.CSVStockFileParser; +import millions.controller.fileIO.InvalidFormatException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; + +import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class CSVStockFileParserTest { static String exampleString; + final CSVStockFileParser parser = new CSVStockFileParser(); @BeforeAll - public static void setUpTestFile() throws Exception { + public static void setUpTestString() { exampleString = "# Top 500 US Stocks by Market Cap\n"; exampleString += "# Ticker,Name,Price\n"; exampleString += "\n"; exampleString += "NVDA,Nvidia,191.27\n"; exampleString += "AAPL,Apple Inc.,276.43\n"; exampleString += "MSFT,Microsoft,404.68\n"; + } @Test public void parseStockFileTest(){ List testList = List.of(exampleString.split("\n")); - - CSVStockFileParser parser = new CSVStockFileParser(); assertEquals(3, parser.parse(testList).size()); } + + @Test + public void InvalidFormatExceptionTest() { + exampleString += "Line with incorrect amount of data"; + List testList = List.of(exampleString.split("\n")); + Exception e = assertThrows(InvalidFormatException.class, () -> {parser.parse(testList);}); + String expectedMessage = "Incorrect format for CSV File: incorrect amount of data fields detected on one or more lines"; + assertEquals(expectedMessage, e.getMessage()); + } + + @Test + public void NumberConversionExceptionTest() { + exampleString += "Company, Company Inc., NotANumber"; + List testList = List.of(exampleString.split("\n")); + Exception e = assertThrows(InvalidFormatException.class, () -> {parser.parse(testList);}); + String expectedMessage = "Error with number conversion on line: Company, Company Inc., NotANumber\n" + + "Last field must be a number"; + assertEquals(expectedMessage, e.getMessage()); + } + + @Test + public void EmptySymbolFieldExceptionTest() { + exampleString += ",test,1"; + List testList = List.of(exampleString.split("\n")); + Exception e = assertThrows(InvalidFormatException.class, () -> {parser.parse(testList);}); + String expectedMessage = "Illegal argument on line: ,test,1\n" + + "Symbol cannot be null or blank"; + assertEquals(expectedMessage, e.getMessage()); + } + @Test + public void EmptyCompanyNameFieldExceptionTest() { + exampleString += "test,,1"; + List testList = List.of(exampleString.split("\n")); + Exception e = assertThrows(InvalidFormatException.class, () -> {parser.parse(testList);}); + String expectedMessage = "Illegal argument on line: test,,1\n" + + "Company cannot be null or blank"; + assertEquals(expectedMessage, e.getMessage()); + } } diff --git a/src/test/java/millions/CSVStockFileWriterTest.java b/src/test/java/millions/CSVStockFileWriterTest.java deleted file mode 100644 index c6fe15a..0000000 --- a/src/test/java/millions/CSVStockFileWriterTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package millions; - -import millions.controller.fileIO.CSV.CSVStockFileWriter; -import millions.controller.fileIO.StockFileReader; -import millions.model.Stock; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.math.BigDecimal; -import java.nio.file.Path; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class CSVStockFileWriterTest { - @TempDir - static Path tempDir; - - List stocks; - - @BeforeEach - void setup() { - Stock s1 = new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300)); - Stock s2 = new Stock("DOGL", "DOOGLE Inc.", BigDecimal.valueOf(200.00)); - Stock s3 = new Stock("MSFT", "EpsteinSoft Inc.", BigDecimal.valueOf(0.02)); - - this.stocks = List.of(s1, s2, s3); - } - - @Test - public void testWrite() { - for(Stock stock : this.stocks) { - System.out.println(stock.toString()); - } - CSVStockFileWriter csvStockFileWriter = new CSVStockFileWriter(stocks); - csvStockFileWriter.write(tempDir.resolve("stocks.csv")); - - StockFileReader stockFileReader = new StockFileReader(); - assertEquals(3, stockFileReader.readFile(tempDir.resolve("stocks.csv")).size()); - } -}