From e20e42682b535c4ae49d7723085c5ca04dfbb0f6 Mon Sep 17 00:00:00 2001
From: Per Eric Trapnes
Date: Fri, 15 May 2026 17:29:38 +0200
Subject: [PATCH] fix handling of csv files such that user selected file is
formated, else a warning with error lines will appear
---
.../AppControllers/SettingsAppController.java | 78 ++++++++++++--
.../gruppe42/Controller/MarketController.java | 12 +--
.../gruppe42/Controller/PopupsController.java | 13 ++-
.../ViewControllers/StartViewController.java | 34 +++---
.../gruppe42/Model/StockFileHandler.java | 4 +-
.../gruppe42/View/Apps/SettingsApp.java | 15 ++-
.../gruppe42/View/Apps/WarningApp.java | 36 ++++---
src/main/resources/css/apps.css | 6 ++
src/main/resources/empty.csv | 0
src/main/resources/scramble.csv | 100 ++++++++++++++++++
src/main/resources/stocks.csv | 4 +-
11 files changed, 241 insertions(+), 61 deletions(-)
create mode 100644 src/main/resources/empty.csv
create mode 100644 src/main/resources/scramble.csv
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 8634cf7..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
@@ -22,24 +22,14 @@ 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 (IOException exception) {
System.out.println("File not found");
- } catch (URISyntaxException exception) {
- System.out.println("Invalid file path");
} 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/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/StockFileHandler.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/StockFileHandler.java
index 8f08e75..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
@@ -18,9 +18,9 @@
* Includes methods for reading and writing from file.
*/
public final class StockFileHandler {
- private static final Pattern SYMBOL_PATTERN = Pattern.compile("^[A-Z]{3}$");
+ 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 static final int EXPECTED_FIELDS = 3;
/**
* Private constructor to prevent instantiation.
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