diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/SettingsAppController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/SettingsAppController.java index 74a4a52..a3e6c04 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/SettingsAppController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/SettingsAppController.java @@ -1,36 +1,92 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Exchange; +import edu.ntnu.idi.idatt2003.gruppe42.Model.StockFileHandler; import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.SettingsApp; import java.nio.file.Path; +import java.util.Objects; public class SettingsAppController implements AppController { - private SettingsApp settingsApp; private Path userSelectedPath; private Runnable onUserSelection; + private Runnable onSelectedFileFailed; + private String errorMessage; - public SettingsAppController(SettingsApp settingsApp) { + private final SettingsApp settingsApp; + + public SettingsAppController(final SettingsApp settingsApp) { this.settingsApp = settingsApp; + userSelectedPath = loadDefaultPath(); + refreshView(); if (!settingsApp.isLoggedIn()) { - settingsApp.getOpenFileChooserButton().setOnAction(event -> { - userSelectedPath = settingsApp.getFileChooser().showOpenDialog(settingsApp.getRoot().getScene().getWindow()).toPath(); - System.out.println(userSelectedPath.toString()); - onUserSelection.run(); - }); + settingsApp.getOpenFileChooserButton().setOnAction(event -> openFileChooser()); } } + private void openFileChooser() { + var file = settingsApp.getFileChooser() + .showOpenDialog(settingsApp.getRoot().getScene().getWindow()); + + if (file == null) { + return; // user cancelled — keep current path + } + + Path selected = file.toPath(); + + if (isValidStockFile(selected)) { + userSelectedPath = selected; + onUserSelection.run(); + } else { + onSelectedFileFailed.run(); + userSelectedPath = loadDefaultPath(); + } + + refreshView(); + } + + private boolean isValidStockFile(final Path path) { + try { + new Exchange(StockFileHandler.readFromFile(path)); + return true; + } catch (Exception e) { + errorMessage = e.getMessage(); + return false; + } + } + + private Path loadDefaultPath() { + try { + return Path.of(Objects.requireNonNull( + getClass().getClassLoader().getResource("stocks.csv")).toURI()); + } catch (Exception e) { + System.err.println("Failed to load default stocks.csv: " + e.getMessage()); + return null; + } + } + + private void refreshView() { + settingsApp.setPath(userSelectedPath); + settingsApp.updateContent(); + } + public Path getUserSelectedPath() { return userSelectedPath; } - public void setOnUserPathSelected(Runnable callback) { + public void setOnUserPathSelected(final Runnable callback) { onUserSelection = callback; } - @Override - public void nextTick() { + public void setOnSelectedFileFailed(final Runnable callback) { + onSelectedFileFailed = callback; + } + public String getErrorMessage() { + return errorMessage; } -} + + @Override + public void nextTick() {} +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java index d4a7159..d738a83 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java @@ -1,9 +1,12 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions.StockFileParseException; import edu.ntnu.idi.idatt2003.gruppe42.Model.Exchange; import edu.ntnu.idi.idatt2003.gruppe42.Model.Stock; import edu.ntnu.idi.idatt2003.gruppe42.Model.StockFileHandler; import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.StockApp; +import java.io.IOException; +import java.net.URISyntaxException; import java.nio.file.Path; import java.util.List; import java.util.Objects; @@ -19,23 +22,16 @@ public final class MarketController { /** * Constructs a new MarketController and starts the market. */ - public MarketController(Path userSelectedPath) { + public MarketController(Path path) { this.stockResolution = 120; - System.out.println(userSelectedPath); try { - Path path = Path.of(Objects.requireNonNull(getClass().getClassLoader() - .getResource("stocks.csv")).toURI()); - if (userSelectedPath != null) { - path = userSelectedPath; - System.out.println(path); - } exchange = new Exchange(StockFileHandler.readFromFile(path)); - startMarket(); System.out.println("Market loaded"); - - } catch (Exception exception) { + } catch (IOException exception) { System.out.println("File not found"); + } catch (StockFileParseException exception) { + System.out.println("Invalid stock file format"); } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java index 4aa95d6..a11f1db 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java @@ -26,7 +26,7 @@ public PopupsController(List popups) { private void initPopup(Popup popup) { makeDraggable(popup); - popup.getCloseButton().setOnAction(event -> hide(popup)); + popup.getCloseButton().setOnAction(event -> hide(popup.getType())); } public boolean show(App type) { @@ -45,11 +45,16 @@ public boolean show(App type) { return true; } - public boolean hide(Popup popup) { - if (popup == null) { + public boolean hide(App type) { + if (type == null) { return false; } - popup.getRoot().setVisible(false); + popups.stream() + .filter(popup -> popup.getType().equals(type)) + .findFirst() + .ifPresent(popup -> { + popup.getRoot().setVisible(false); + }); audioManager.playSFX(Audio.CLOSE_APP); return true; } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java index c24bbe3..a4ff879 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java @@ -1,7 +1,5 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller.ViewControllers; -import edu.ntnu.idi.idatt2003.gruppe42.Audio.Audio; -import edu.ntnu.idi.idatt2003.gruppe42.Audio.AudioManager; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.AppStoreController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.BankAppController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.SettingsAppController; @@ -11,7 +9,7 @@ import edu.ntnu.idi.idatt2003.gruppe42.Controller.PopupsController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.TimeAndWeatherController; import edu.ntnu.idi.idatt2003.gruppe42.Model.App; -import edu.ntnu.idi.idatt2003.gruppe42.Model.Day; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions.StockFileParseException; import edu.ntnu.idi.idatt2003.gruppe42.Model.Player; import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Purchase; import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Sale; @@ -64,6 +62,7 @@ public DesktopViewController(final Player player, final GameController gameContr this.marketController = new MarketController(userSelectedPath); + AppStoreApp appStoreApp = new AppStoreApp(); gameController.addAppController(new AppStoreController(appStoreApp)); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java index ac27a27..940f7ff 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java @@ -13,6 +13,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; /** @@ -29,6 +30,7 @@ public class StartViewController { private final PopupsController popupsController; private Path userSelectedPath; private SettingsAppController settingsAppController; + private WarningApp warningApp; private static final List TATE_NAMES = List.of( "Top G", @@ -58,34 +60,31 @@ public StartViewController(final Millions application, final StartView startView SettingsApp settingsApp = new SettingsApp(false); settingsAppController = new SettingsAppController(settingsApp); - WarningApp warningApp = new WarningApp(); + warningApp = new WarningApp(); warningApp.centerInParent(); startView.getRoot().getChildren().addAll(settingsApp.getRoot(), warningApp.getRoot()); settingsAppController.setOnUserPathSelected(this::updateUserSelectedPath); - - warningApp.configure( - "⚠", - "Difficulty Required", - "Choose how much starting money you want before jumping in.", - "Got it" - ); + settingsAppController.setOnSelectedFileFailed(this::showFileWarning); popups.add(settingsApp); popups.add(warningApp); popupsController = new PopupsController(popups); - warningApp.getPrimaryButton().setOnAction(e -> { - popupsController.hide(warningApp); - }); - startView.getUsernameField().textProperty().addListener( (obs, old, newValue) -> username = newValue ); startView.getLoginButton().setOnAction(event -> { if (startView.getSelectedMode() == null) { + warningApp.configure( + "⚠", + "Difficulty Required", + "Choose how much starting money you want before jumping in.", + "Got it" + ); + warningApp.getPrimaryButton().setOnAction(e -> popupsController.hide(App.WARNING)); popupsController.show(App.WARNING); return; } @@ -124,6 +123,17 @@ private boolean isValidName(final String value) { return value.matches("[a-zA-Z0-9]+"); } + private void showFileWarning(){ + warningApp.configure( + "⚠", + "Invalid File", + settingsAppController.getErrorMessage(), + "Revert to default file" + ); + warningApp.getPrimaryButton().setOnAction(e -> popupsController.hide(App.WARNING)); + popupsController.show(App.WARNING); + } + private void updateUserSelectedPath() { userSelectedPath = settingsAppController.getUserSelectedPath(); } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Exceptions/StockFileParseException.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Exceptions/StockFileParseException.java new file mode 100644 index 0000000..8fd4e91 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Exceptions/StockFileParseException.java @@ -0,0 +1,15 @@ +package edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions; + +public class StockFileParseException extends Exception { + private final int lineNumber; + private final String lineContent; + + public StockFileParseException(int lineNumber, String lineContent, String reason) { + super(String.format("Parse error on line %d: %s%n Content : \"%s\"", lineNumber, reason, lineContent)); + this.lineNumber = lineNumber; + this.lineContent = lineContent; + } + + public int getLineNumber() { return lineNumber; } + public String getLineContent() { return lineContent; } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandler.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandler.java index 19dd290..6a062cf 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandler.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandler.java @@ -1,20 +1,27 @@ package edu.ntnu.idi.idatt2003.gruppe42.Model; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions.StockFileParseException; import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileReader; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; /** * Utility class for handling file data. * Includes methods for reading and writing from file. */ public final class StockFileHandler { + private static final Pattern SYMBOL_PATTERN = Pattern.compile("^[A-Z.]{1,5}$"); + private static final Pattern PRICE_PATTERN = Pattern.compile("^\\d+\\.\\d{2}$"); + private static final int EXPECTED_FIELDS = 3; + /** * Private constructor to prevent instantiation. */ @@ -28,62 +35,85 @@ private StockFileHandler() { * @return list of stock objects * @throws IOException if file not found or error occurs */ - public static List readFromFile(final Path path) throws IOException { + + public static List readFromFile(final Path path) + throws IOException, StockFileParseException { + + if (!Files.exists(path)) { + throw new IOException("File not found: " + path); + } + if (!Files.isReadable(path)) { + throw new IOException("File is not readable: " + path); + } List stocks = new ArrayList<>(); + int lineNumber = 0; + + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8))) { - try (BufferedReader bufferedReader = new BufferedReader( - new FileReader(path.toFile()))) { String line; - while ((line = bufferedReader.readLine()) != null) { - line = line.trim(); + while ((line = reader.readLine()) != null) { + lineNumber++; + String trimmed = line.trim(); - if (line.isEmpty() || line.startsWith("#")) { + // Skip blank lines and comments + if (trimmed.isEmpty() || trimmed.startsWith("#")) { continue; } - String[] parts = line.split(","); + String[] parts = trimmed.split(","); + if (parts.length != EXPECTED_FIELDS) { + throw new StockFileParseException(lineNumber, trimmed, + String.format("Expected %d comma-separated fields but found %d. " + + "Hint: decimal prices must use '.' not ','", + EXPECTED_FIELDS, parts.length)); + } + + String symbol = parts[0].trim(); + if (symbol.isEmpty()) { + throw new StockFileParseException(lineNumber, trimmed, + "Ticker symbol is empty"); + } + if (!SYMBOL_PATTERN.matcher(symbol).matches()) { + throw new StockFileParseException(lineNumber, trimmed, + String.format("Ticker symbol \"%s\" is invalid — must be exactly 3 uppercase letters (A-Z)", symbol)); + } + + String company = parts[1].trim(); + if (company.isEmpty()) { + throw new StockFileParseException(lineNumber, trimmed, + "Company name is empty"); + } + + String rawPrice = parts[2].trim(); + if (!PRICE_PATTERN.matcher(rawPrice).matches()) { + throw new StockFileParseException(lineNumber, trimmed, + String.format("Price \"%s\" is invalid — must be a positive number with exactly 2 decimal places using '.' (e.g. 191.27)", rawPrice)); + } - final int symbolIndex = 0; - final int companyIndex = 1; - final int priceIndex = 2; + BigDecimal price; + try { + price = new BigDecimal(rawPrice); + } catch (NumberFormatException e) { + throw new StockFileParseException(lineNumber, trimmed, + String.format("Price \"%s\" could not be parsed as a number", rawPrice)); + } - String symbol = parts[symbolIndex]; - String company = parts[companyIndex]; - BigDecimal salePrice = new BigDecimal(parts[priceIndex].trim()); + if (price.compareTo(BigDecimal.ZERO) <= 0) { + throw new StockFileParseException(lineNumber, trimmed, + String.format("Price \"%s\" must be greater than zero", rawPrice)); + } - stocks.add(new Stock(symbol, company, salePrice)); + stocks.add(new Stock(symbol, company, price)); } } - return stocks; - } - /** - * Method for writing stock information to file. - * - * @param path the path of the file to write to - * @param stocks list of stock objects to be transcribed - * @throws IOException if error occurs - */ - public static void writeToFile( - final Path path, - final List stocks - ) throws IOException { - - try (BufferedWriter writer = Files.newBufferedWriter(path)) { - writer.write("# S&P 500 Companies by Market Cap"); - writer.write("\n# Ticker,Name,Price"); - writer.newLine(); - - for (Stock stock : stocks) { - String line = String.format("%s,%s,%s", - stock.getSymbol(), - stock.getCompany(), - stock.getSalesPrice()); - - writer.write(line); - writer.newLine(); - } + if (stocks.isEmpty()) { + throw new StockFileParseException(lineNumber, "", + "File contained no valid stock entries"); } + + return stocks; } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/SettingsApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/SettingsApp.java index cd84a00..78be2d0 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/SettingsApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/SettingsApp.java @@ -2,7 +2,9 @@ import edu.ntnu.idi.idatt2003.gruppe42.Model.App; import edu.ntnu.idi.idatt2003.gruppe42.View.Popup; +import java.nio.file.Path; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.stage.FileChooser; public class SettingsApp extends Popup { @@ -10,10 +12,13 @@ public class SettingsApp extends Popup { private Button openFileChooserButton; private FileChooser fileChooser; private boolean isLoggedIn; + private Path userSelectedPath; public SettingsApp(boolean isLoggedIn) { super(720, 480, 80, 60, App.SETTINGS); this.isLoggedIn = isLoggedIn; + openFileChooserButton = new Button("Import stock file"); + fileChooser = new FileChooser(); updateContent(); } @@ -36,16 +41,18 @@ public void setLoggedIn(boolean status) { public void updateContent() { if (!isLoggedIn) { - openFileChooserButton = new Button("Import stock file"); - fileChooser = new FileChooser(); fileChooser.setTitle("Select .csv stock file"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("CSV files","*.csv")); - - content.getChildren().setAll(openFileChooserButton); + Label currentPathLabel = new Label("Current path: " + userSelectedPath); + content.getChildren().setAll(openFileChooserButton, currentPathLabel); } else { //TODO: settings in desktopView content.getChildren().setAll(); } } + public void setPath(Path path) { + userSelectedPath = path; + updateContent(); + } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java index 422e298..153930c 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java @@ -6,14 +6,13 @@ import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; /** * Compact, reusable warning popup used across the entire game. * - * configuring, then show the root and overlay via the desktop controller.

- * *

The close button is always re-wired by the configure methods so it * mirrors the appropriate dismissal action.

*/ @@ -28,7 +27,7 @@ public final class WarningApp extends Popup { private final HBox buttons; public WarningApp() { - super(370, 230, 0, 0, App.WARNING); + super(370, 270, 0, 0, App.WARNING); iconLabel.setStyle("-fx-font-size: 30px;"); iconLabel.setAlignment(Pos.CENTER); @@ -41,6 +40,14 @@ public WarningApp() { messageLabel.setWrapText(true); messageLabel.setAlignment(Pos.CENTER); + ScrollPane messageScroll = new ScrollPane(messageLabel); + messageScroll.setFitToWidth(true); + messageScroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + messageScroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); + messageScroll.setPrefHeight(200); + messageScroll.getStyleClass().add("warning-scroll"); + messageScroll.setStyle("-fx-background-color: transparent; -fx-background: transparent;"); + primaryButton.getStyleClass().add("primary-button"); primaryButton.setPrefWidth(150); @@ -50,7 +57,7 @@ public WarningApp() { buttons = new HBox(12, secondaryButton, primaryButton); buttons.setAlignment(Pos.CENTER); - VBox inner = new VBox(14, iconLabel, titleLabel, messageLabel, buttons); + VBox inner = new VBox(10, iconLabel, titleLabel, messageScroll, buttons); inner.setAlignment(Pos.CENTER); content.setPadding(new Insets(24, 28, 24, 28)); @@ -62,9 +69,9 @@ public WarningApp() { * Configures for a single-button (dismiss-only) scenario. * The secondary button is hidden. The close button mirrors the primary. * - * @param icon emoji icon shown at the top - * @param title bold heading inside the card - * @param message body text below the heading + * @param icon emoji icon shown at the top + * @param title bold heading inside the card + * @param message body text below the heading * @param primaryLabel label on the single action button */ public void configure( @@ -81,7 +88,6 @@ public void configure( secondaryButton.setVisible(false); secondaryButton.setManaged(false); - // Clear old handlers so callers can set fresh ones after configure primaryButton.setOnAction(null); secondaryButton.setOnAction(null); getCloseButton().setOnAction(event -> primaryButton.fire()); @@ -91,11 +97,11 @@ public void configure( * Configures for a two-button (choice) scenario. * The secondary button is the "safe" cancel option; the close button mirrors it. * - * @param icon emoji icon shown at the top - * @param title bold heading inside the card - * @param message body text below the heading - * @param secondaryLabel label on the left / cancel button - * @param primaryLabel label on the right / action button + * @param icon emoji icon shown at the top + * @param title bold heading inside the card + * @param message body text below the heading + * @param secondaryLabel label on the left / cancel button + * @param primaryLabel label on the right / action button */ public void configure( final String icon, @@ -118,7 +124,7 @@ public void configure( getCloseButton().setOnAction(event -> secondaryButton.fire()); } - public void show(){ + public void show() { getRoot().setVisible(true); getRoot().toFront(); } @@ -132,4 +138,4 @@ public Button getPrimaryButton() { public Button getSecondaryButton() { return secondaryButton; } -} +} \ No newline at end of file diff --git a/src/main/resources/css/apps.css b/src/main/resources/css/apps.css index 2201e8c..8a54649 100644 --- a/src/main/resources/css/apps.css +++ b/src/main/resources/css/apps.css @@ -373,6 +373,12 @@ -fx-line-spacing: 2; } +.warning-scroll, +.warning-scroll .viewport, +.warning-scroll .scroll-pane { + -fx-background-color: transparent; + -fx-border-color: transparent; +} .game-over-title { -fx-font-weight: bold; -fx-font-size: 36px; diff --git a/src/main/resources/empty.csv b/src/main/resources/empty.csv new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/scramble.csv b/src/main/resources/scramble.csv new file mode 100644 index 0000000..fdaab8f --- /dev/null +++ b/src/main/resources/scramble.csv @@ -0,0 +1,100 @@ +03w8ruwr3jweer3wrø3w3wr' +3wr + +3w +r +3w +r3w +r3w +rar +fws +rws +r +3wr +w4strf +e4f +esrv +ybu +5rvtr +q2 +ecrey +5r + +y3w +c3e +3w +tg +r5y + + e4 + c4 + 2ws + h + t6 + vy + tw3 + + rr + d + hy + e4 + 5r + + e + y + rd + vy + 5r + vy + htr + f + hyv + r5 + tv + e4 + vtg + + r + dt + g + a + 3e24 + + 6 + + 6 + 5 + + 6 + + + + 4e + + 4 + 5 + re + g + + f + d gv + d + fgh + + tf + h + rd + g + rd + cres + rr + d + tv + rd + vt + rd + vt + rd + cg + + d \ No newline at end of file diff --git a/src/main/resources/stocks.csv b/src/main/resources/stocks.csv index 36eefea..a771bb5 100644 --- a/src/main/resources/stocks.csv +++ b/src/main/resources/stocks.csv @@ -18,10 +18,10 @@ MON,Monsters Inc.,329.54 AIR,Plane Inc.,240.70 ATB,Bus Inc.,539.52 BAT,Battery Inc.,243.43 -KWI,Kiwi Inc.,102,24 +KWI,Kiwi Inc.,102.24 VAC,Vacine Inc.,156.76 JZZ,Jazz Inc.,230.40 -MET,Metal Inc.,302,89 +MET,Metal Inc.,302.89 FRM,Farm Org.,260.76 COS,Cosmetic Inc.,403.30 TXI,Taxi Org.,239.70 diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandlerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandlerTest.java index cd6d20b..26d3711 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandlerTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandlerTest.java @@ -16,25 +16,25 @@ public class StockFileHandlerTest { void readFromFileTest() throws IOException { Path testFile = Files.createTempFile("stocks", "csv"); Files.writeString(testFile, "AAPL,Apple Inc.,276.43\nMSFT,Microsoft,404.68"); - List stocks = StockFileHandler.readFromFile(testFile); - assertEquals(2, stocks.size()); - assertEquals("AAPL", stocks.get(0).getSymbol()); + try { + List stocks = StockFileHandler.readFromFile(testFile); + assertEquals(2, stocks.size()); + assertEquals("AAPL", stocks.get(0).getSymbol()); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Test void readFromFileWithCommentsAndEmptyLines() throws IOException { Path testFile = Files.createTempFile("stocks", "csv"); Files.writeString(testFile, "#Comment\n\nAAPL,Apple Inc.,276.43"); - List stocks = StockFileHandler.readFromFile(testFile); - assertEquals(1, stocks.size()); - } - @Test - void writeToFileTest() throws IOException { - Path testFile = Files.createTempFile("stocks", "csv"); - List stocks = List.of(new Stock("AAPL", "Apple Inc.", new BigDecimal("276.43"))); - StockFileHandler.writeToFile(testFile, stocks); - List lines = Files.readAllLines(testFile); - assertTrue(lines.get(2).startsWith("AAPL,Apple Inc.,276.43")); + try { + List stocks = StockFileHandler.readFromFile(testFile); + assertEquals(1, stocks.size()); + } catch (Exception e) { + throw new RuntimeException(e); + } } }