diff --git a/millions/src/main/java/no/ntnu/gruppe53/App.java b/millions/src/main/java/no/ntnu/gruppe53/App.java index b3c3b06..3170ab6 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/App.java +++ b/millions/src/main/java/no/ntnu/gruppe53/App.java @@ -8,28 +8,34 @@ /** * Represents the main app of the application. + * *
Sets up necessary components and shows the first view * of the application: {@link StartView}.
*/ public class App extends Application { - @Override - public void start(Stage stage) { - ViewController vm = new ViewController(stage); + @Override + public void start(Stage stage) { + ViewController vm = new ViewController(stage); - StartView startView = new StartView(vm); + StartView startView = new StartView(vm); - new StartViewController(startView, vm); + new StartViewController(startView, vm); - vm.addView("start", startView); + vm.addView("start", startView); - vm.switchView("start"); + vm.switchView("start"); - stage.setTitle("Millions - the Stock Game"); - stage.show(); - } + stage.setTitle("Millions - the Stock Game"); + stage.show(); + } - public static void main(String[] args) { - launch(); - } + /** + * Launches the program. + * + * @param args arguments + */ + static void main(String[] args) { + launch(); + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java b/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java index d8ea407..0514822 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java +++ b/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java @@ -3,336 +3,375 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; - import javafx.application.Platform; import javafx.scene.control.Alert; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.stage.Stage; import javafx.util.Subscription; -import no.ntnu.gruppe53.model.*; +import no.ntnu.gruppe53.model.Exchange; +import no.ntnu.gruppe53.model.Player; +import no.ntnu.gruppe53.model.PurchaseCalculator; +import no.ntnu.gruppe53.model.SaleCalculator; +import no.ntnu.gruppe53.model.Share; +import no.ntnu.gruppe53.model.Stock; import no.ntnu.gruppe53.service.LanguageManager; import no.ntnu.gruppe53.service.StockLineChartService; -import no.ntnu.gruppe53.view.*; +import no.ntnu.gruppe53.view.EndView; +import no.ntnu.gruppe53.view.FooterBar; +import no.ntnu.gruppe53.view.MarketView; +import no.ntnu.gruppe53.view.NavigationBar; +import no.ntnu.gruppe53.view.PortfolioView; +import no.ntnu.gruppe53.view.TransactionArchiveView; /** * Represents the main controller for the views of the stock game. + * *Uses a {@link LanguageManager} for methods to change text dynamically.
*/ public class GameController { - private Player player; - private Exchange exchange; - private ListCreates the player and exchange to be used by the game from + * data from the {@link GameSetupController}.
+ */ + public void startNewGame() { + Stage primaryStage = vm.getStage(); + + GameSetupController setupController = new GameSetupController(primaryStage); + GameSetupController.SetupResult setupResult = setupController.setupNewGame(); + + if (setupResult == null) { + return; } - /** - * Starts a new game by initializing all main views and navigation. - *Creates the player and exchange to be used by the game from - * data from the {@link GameSetupController}.
- */ - public void startNewGame() { - Stage primaryStage = vm.getStage(); - - GameSetupController setupController = new GameSetupController(primaryStage); - GameSetupController.SetupResult setupResult = setupController.setupNewGame(); - if (setupResult == null) return; + player = setupResult.getPlayer(); + ListMarketView lets the player perform {@link no.ntnu.gruppe53.model.Purchase} transactions.
+ */ + public void showMarketView() { + if (marketView != null) { + vm.switchView("market"); } - - /** - * Switches the view to the {@link MarketView}. - *MarketView lets the player perform {@link Purchase} transactions.
- */ - public void showMarketView() { - if (marketView != null) { - vm.switchView("market"); - } + } + + /** + * Switches the view to the {@link PortfolioView}. + * + *Contains the data from a player's {@link no.ntnu.gruppe53.model.Portfolio}.
+ * + *PortfolioView lets theEndView player perform + * {@link no.ntnu.gruppe53.model.Sale} transactions.
+ */ + public void showPortfolio() { + if (portfolioView != null) { + vm.switchView("portfolio"); } - - /** - * Switches the view to the {@link PortfolioView}. - *Contains the data from a player's {@link Portfolio}.
- *PortfolioView lets theEndView player perform {@link Sale} transactions.
- */ - public void showPortfolio() { - if (portfolioView != null) vm.switchView("portfolio"); + } + + /** + * Switches the view to the {@link TransactionArchiveView}. + * + *TransactionArchiveView contains data from + * {@link no.ntnu.gruppe53.model.TransactionArchive}
+ */ + public void showTransactionHistory() { + if (transactionArchiveView != null) { + vm.switchView("history"); } - - /** - * Switches the view to the {@link TransactionArchiveView}. - *TransactionArchiveView contains data from {@link TransactionArchive}
- */ - public void showTransactionHistory() { - if (transactionArchiveView != null) vm.switchView("history"); - } - - /** - * Initializes the {@link NavigationBar} and {@link FooterBar} for the controller. - *Binds functionality to exposed buttons and lists in views.
- *Sets subscriptions for observable values for views.
- *Sets the method for setting the selected language locale for the footer.
- * @param nav the NavigationBar view - * @param footer the FooterBar view - */ - public void initialize(NavigationBar nav, FooterBar footer) { - nav.onMarketButtonClick(() -> vm.switchView("market")); - nav.onPortfolioButtonClick(() -> vm.switchView("portfolio")); - nav.onHistoryButtonClick(() -> vm.switchView("history")); - nav.onEndGameButtonClick(() -> endGame()); - - footer.onLanguageChange(selected -> { - lm.setLocale(selected.locale()); + } + + /** + * Initializes the {@link NavigationBar} and {@link FooterBar} for the controller. + * + *Binds functionality to exposed buttons and lists in views.
+ * + *Sets subscriptions for observable values for views.
+ * + *Sets the method for setting the selected language locale for the footer.
+ * + * @param nav the NavigationBar view + * @param footer the FooterBar view + */ + public void initialize(NavigationBar nav, FooterBar footer) { + nav.onMarketButtonClick(() -> vm.switchView("market")); + nav.onPortfolioButtonClick(() -> vm.switchView("portfolio")); + nav.onHistoryButtonClick(() -> vm.switchView("history")); + nav.onEndGameButtonClick(this::endGame); + + footer.onLanguageChange(selected -> lm.setLocale(selected.locale())); + + footer.onAdvanceButtonClick(() -> { + if (exchange != null) { + exchange.advance(); + } + }); + + marketView.onStockSelection(stock -> { + if (selectedStockPriceSubscription != null) { + selectedStockPriceSubscription.unsubscribe(); + selectedStockPriceSubscription = null; + } + + if (stock != null) { + marketView.setSelectedStockLabel(stock.getSymbol() + " - " + stock.getCompany()); + marketView.setupPriceSubscription(stock, currentStock -> { + var series = StockLineChartService.buildSeries(currentStock, exchange); + marketView.updateChart(series, currentStock.getCompany()); }); - footer.onAdvanceButtonClick(() -> { - if (exchange != null) { - exchange.advance(); - } - }); - - marketView.onStockSelection(stock -> { - if (selectedStockPriceSubscription != null) { - selectedStockPriceSubscription.unsubscribe(); - selectedStockPriceSubscription = null; - } - - if (stock != null) { - marketView.setSelectedStockLabel(stock.getSymbol() + " - " + stock.getCompany()); - marketView.setupPriceSubscription(stock, currentStock -> { - var series = StockLineChartService.buildSeries(currentStock, exchange); - marketView.updateChart(series, currentStock.getCompany()); - }); - marketView.setPurchaseCalculator(calculatePurchase(marketView.getSelectedStock(), - marketView.getQuantityPurchase())); - - selectedStockPriceSubscription = stock.salesPriceProperty().subscribe(newPrice -> { - if (marketView.getSelectedStock() != null) { - marketView.setPurchaseCalculator(calculatePurchase( - marketView.getSelectedStock(), - marketView.getQuantityPurchase() - )); - } - }); + marketView.setPurchaseCalculator(calculatePurchase( + marketView.getSelectedStock(), marketView.getQuantityPurchase())); - } else { - marketView.setSelectedStockLabel(""); - marketView.clearChart(); - } + selectedStockPriceSubscription = stock.salesPriceProperty().subscribe(newPrice -> { + if (marketView.getSelectedStock() != null) { + marketView.setPurchaseCalculator(calculatePurchase( + marketView.getSelectedStock(), + marketView.getQuantityPurchase() + )); + } }); - marketView.onPurchaseButton(() -> { - Stock stock = marketView.getSelectedStock(); - if (stock != null) purchaseStock(stock.getSymbol(), marketView.getQuantityPurchase()); - }); - marketView.onQuantitySelect(() -> { - if (marketView.getSelectedStock() != null) { - marketView.setPurchaseCalculator(calculatePurchase(marketView.getSelectedStock(), - marketView.getQuantityPurchase())); - } - }); - - portfolioView.onShareSelection(share -> { - if (selectedSharePriceSubscription != null) { - selectedSharePriceSubscription.unsubscribe(); - selectedSharePriceSubscription = null; - } - if (share != null) { - portfolioView.setSelectedShareLabel(share.getStock().getSymbol() + " - " + share.getStock().getCompany()); - portfolioView.setSaleCalculator(calculateSale(portfolioView.getSelectedShare())); - - selectedSharePriceSubscription = share.getStock().salesPriceProperty().subscribe(newPrice -> { - if (portfolioView.getSelectedShare() != null) { - portfolioView.setSaleCalculator(calculateSale(portfolioView.getSelectedShare())); - } + } else { + marketView.setSelectedStockLabel(""); + marketView.clearChart(); + } + }); + + marketView.onPurchaseButton(() -> { + Stock stock = marketView.getSelectedStock(); + if (stock != null) { + purchaseStock(stock.getSymbol(), marketView.getQuantityPurchase()); + } + }); + + marketView.onQuantitySelect(() -> { + if (marketView.getSelectedStock() != null) { + marketView.setPurchaseCalculator(calculatePurchase( + marketView.getSelectedStock(), marketView.getQuantityPurchase())); + } + }); + + portfolioView.onShareSelection(share -> { + if (selectedSharePriceSubscription != null) { + selectedSharePriceSubscription.unsubscribe(); + selectedSharePriceSubscription = null; + } + + if (share != null) { + portfolioView.setSelectedShareLabel( + share.getStock().getSymbol() + " - " + share.getStock().getCompany()); + portfolioView.setSaleCalculator(calculateSale(portfolioView.getSelectedShare())); + + selectedSharePriceSubscription = + share.getStock().salesPriceProperty().subscribe(newPrice -> { + + if (portfolioView.getSelectedShare() != null) { + portfolioView.setSaleCalculator( + calculateSale(portfolioView.getSelectedShare())); + } }); - } else { - portfolioView.setSelectedShareLabel(""); - } - }); - - portfolioView.onSellButton(this::handleSell); - - endView.onQuitButton(Platform::exit); - - endView.onNewGameButton(() -> { - startNewGame(); - }); + } else { + portfolioView.setSelectedShareLabel(""); + } + }); + + portfolioView.onSellButton(this::handleSell); + + endView.onQuitButton(Platform::exit); + + endView.onNewGameButton(this::startNewGame); + } + + /** + * Lets a player purchase {@link Share}s in a stock. + * + *Utilizes the buy method from {@link Exchange}.
+ * + * @param stockSymbol the symbol/ticker of the stock to purchase shares for + * @param quantity the quantity of shares to be purchased + */ + private void purchaseStock(String stockSymbol, int quantity) { + if (exchange == null || player == null || stockSymbol == null || quantity <= 0) { + return; } - /** - * Lets a player purchase {@link Share}s in a stock. - *Utilizes the buy method from {@link Exchange}.
- * @param stockSymbol the symbol/ticker of the stock to purchase shares for - * @param quantity the quantity of shares to be purchased - */ - private void purchaseStock(String stockSymbol, int quantity) { - if (exchange == null || player == null || stockSymbol == null || quantity <= 0) return; - try { - exchange.buy(stockSymbol, BigDecimal.valueOf(quantity), player); - } catch (IllegalStateException | IllegalArgumentException ex) { - showErrorAlert("Purchase Failed", ex.getMessage()); - } + try { + exchange.buy(stockSymbol, BigDecimal.valueOf(quantity), player); + } catch (IllegalStateException | IllegalArgumentException ex) { + showErrorAlert("Purchase Failed", ex.getMessage()); } - - /** - * Handles sale of the selected share. - */ - private void handleSell() { - Share selectedShare = portfolioView.getSelectedShare(); - if (selectedShare != null) sellShare(selectedShare); + } + + /** + * Handles sale of the selected share. + */ + private void handleSell() { + Share selectedShare = portfolioView.getSelectedShare(); + if (selectedShare != null) { + sellShare(selectedShare); } - - /** - * Lets a player sell a share from their portfolio. - * @param selectedShare the currently selected share - */ - private void sellShare(Share selectedShare) { - if (exchange == null || player == null || selectedShare == null) return; - try { - exchange.sell(selectedShare, player); - } catch (IllegalStateException | IllegalArgumentException ex) { - showErrorAlert("Sale Failed", ex.getMessage()); - } + } + + /** + * Lets a player sell a share from their portfolio. + * + * @param selectedShare the currently selected share + */ + private void sellShare(Share selectedShare) { + if (exchange == null || player == null || selectedShare == null) { + return; } - - - /** - * Creates a {@link PurchaseCalculator} that can be used for the - * setPurchaseCalculator() function in the {@link MarketView}. - * @param stock the currently selected stock - * @param quantity the currently selected quantity - * @return {@link PurchaseCalculator} - */ - private PurchaseCalculator calculatePurchase(Stock stock, int quantity) { - stock = marketView.getSelectedStock(); - quantity = marketView.getQuantityPurchase(); - - Share share = new Share(stock, BigDecimal.valueOf(quantity), stock.getSalesPrice()); - - - - return purchaseCalculator = new PurchaseCalculator(share); - } - - /** - * Creates a {@link SaleCalculator} that can be used for the - * setSaleCalculator() function in the {@link PortfolioView}. - * @param share the share to use the calculator on - * @return {@link SaleCalculator} - */ - private SaleCalculator calculateSale(Share share) { - - share = portfolioView.getSelectedShare(); - - return saleCalculator = new SaleCalculator(share); - - } - - - /** - * Creates an alert where the Player can confirm if they want to end the game. - * If yes, then sells entire portfolio and displays the EndView. - */ - private void endGame() { - Alert alert = new Alert(Alert.AlertType.CONFIRMATION); - alert.titleProperty().bind(lm.bindString("endGameAlertTitle")); - alert.headerTextProperty().bind(lm.bindString("endGameAlertHeaderText")); - alert.contentTextProperty().bind(lm.bindString("endGameAlertContentText")); - - ButtonType yesButton = new ButtonType(lm.getString("yes"), ButtonBar.ButtonData.YES); - ButtonType noButton = new ButtonType(lm.getString("no"), ButtonBar.ButtonData.NO); - - - alert.getButtonTypes().setAll(yesButton, noButton); - - alert.showAndWait().ifPresent(button -> { - if (button == yesButton) { - sellPortfolio(); - vm.setGlobalPanels(null, null); - vm.switchView("endGame"); - - endView.getStatusLabel().setText(player.getStatus(exchange)); - endView.getMoneyLabel().setText(player.getMoney().setScale(2, RoundingMode.HALF_EVEN).toString()); - - - - } - }); - - } - - private void sellPortfolio() { - ListLets the {@link Player} set their name, starting money * and provide a .csv file of {@link Stock} data to populate * the {@link Exchange}.
*/ public class GameSetupController { + /** + * Nested class to pass the player and stocks values over to the + * {@link GameController}. + */ + public static class SetupResult { + private final Player player; + private final ListUses default values if the player does not enter their desired values: - *
Uses default values if the player does not enter their desired values: + *
Contains three choices: *
Uses a {@link LanguageManager} to change the UI language.
*/ public class StartViewController { - /** - * Handles the events when "New Game" and "Quit" is pressed. - * - * @param view the StartView to be displayed - * @param vm the {@link ViewController} to handle switching of views - */ - public StartViewController(StartView view, ViewController vm) { - view.setOnNewGame(() -> { - GameController gameController = new GameController(vm); - gameController.startNewGame(); - }); - - view.setOnLanguage(() -> { - LanguageManager lm = LanguageManager.getInstance(); - ListUses a {@link HashMap} as an archive for registered views that * are possible to switch between.
*/ public class ViewController { - private final Stage stage; - private final Scene scene; - private final MapUses a {@link BorderPane} layout for views.
- */ - public ViewController(Stage stage) { - this.stage = stage; - this.mainLayout = new BorderPane(); - this.scene = new Scene(mainLayout, 1200, 900); - this.stage.setScene(scene); - } + /** + * Initializes a ViewController using the {@link Stage} as the first default view. + * + *Uses a {@link BorderPane} layout for views.
+ */ + public ViewController(Stage stage) { + this.stage = stage; + this.mainLayout = new BorderPane(); + Scene scene = new Scene(mainLayout, 1200, 900); + this.stage.setScene(scene); + } - /** - * Returns the stage of the ViewController. - * - * @return the stage of the ViewController. - */ - public Stage getStage() { - return this.stage; - } + /** + * Returns the stage of the ViewController. + * + * @return the stage of the ViewController. + */ + public Stage getStage() { + return this.stage; + } - /** - * Sets the top and bottom part of the BorderPane. - *Uses {@link NavigationBar} as the top bar, - * and {@link FooterBar} as the bottom bar.
- */ - public void setGlobalPanels(NavigationBar navigationBar, FooterBar footerBar) { - this.mainLayout.setTop(navigationBar); - this.mainLayout.setBottom(footerBar); - } + /** + * Sets the top and bottom part of the BorderPane. + * + *Uses {@link NavigationBar} as the top bar, + * and {@link FooterBar} as the bottom bar.
+ */ + public void setGlobalPanels(NavigationBar navigationBar, FooterBar footerBar) { + this.mainLayout.setTop(navigationBar); + this.mainLayout.setBottom(footerBar); + } - /** - * Adds a view to the stored archive of possible views. - * - * @param name the name/key to refer to the view when switching view - * @param view the view to be added - */ - public void addView(String name, Parent view) { - views.put(name, view); - } + /** + * Adds a view to the stored archive of possible views. + * + * @param name the name/key to refer to the view when switching view + * @param view the view to be added + */ + public void addView(String name, Parent view) { + views.put(name, view); + } - /** - * Switches the to the given view based on the string key - * stored in the view archive. - *Only switches the center pane.
- * - * @param name the name/key of the view to switch to - */ - public void switchView(String name) { - Parent view = views.get(name); + /** + * Switches the to the given view based on the string key + * stored in the view archive. + * + *Only switches the center pane.
+ * + * @param name the name/key of the view to switch to + */ + public void switchView(String name) { + Parent view = views.get(name); - if (view == null) { - throw new IllegalArgumentException("No views found matching: " + name); - } - - mainLayout.setCenter(view); + if (view == null) { + throw new IllegalArgumentException("No views found matching: " + name); } + + mainLayout.setCenter(view); + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/model/Exchange.java b/millions/src/main/java/no/ntnu/gruppe53/model/Exchange.java index f20919c..86fef14 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/model/Exchange.java +++ b/millions/src/main/java/no/ntnu/gruppe53/model/Exchange.java @@ -1,252 +1,284 @@ package no.ntnu.gruppe53.model; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.Collectors; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.*; -import java.util.stream.Collectors; - /** - * Represents an exchange/market containing {@link Stock}s and allows a {@link Player} to buy {@link Share}s - * of said stocks. - *Contains method to advance the week of the market, finding stocks, gainers, and losers on the exchange.
+ * Represents an exchange/market containing {@link Stock}s and allows + * a {@link Player} to buy {@link Share}s of said stocks. + * + *Contains method to advance the week of the market, finding stocks, + * gainers, and losers on the exchange.
*/ public class Exchange { - private final String name; - private int week; - private final MapValidates that the values are proper before creating the exchange.
- *Initializes factories for creating a {@link Transaction}.
- * - * @param name the name of the exchange - * @param stocks the list of stocks to be tradeable on the exchange - * @throws IllegalArgumentException if exchange is null or blank, if list of stocks is null, or if - * the list of stocks contains a null or duplicate symbol stock - */ - public Exchange(String name, ListValidates that the values are proper before creating the exchange.
+ * + *Initializes factories for creating a {@link Transaction}.
+ * + * @param name the name of the exchange + * @param stocks the list of stocks to be tradeable on the exchange + * @throws IllegalArgumentException if exchange is null or blank, if list of stocks is null, or if + * the list of stocks contains a null or duplicate symbol stock + */ + public Exchange(String name, ListReturns {@code true} if it does,v{@code false} otherwise.
- * - * @param symbol the symbol/ticker of the stock - * @return {@code true} if the exchange contains the stock, otherwise {@code false} - */ - public boolean hasStock(String symbol) { - return stockMap.containsKey(symbol); - } + for (Stock stock : stocks) { + if (stock == null) { + throw new IllegalArgumentException("Stock cannot be null"); + } - /** - * Returns the stock object matching the given symbol/ticker. - * - * @param symbol the stock symbol/ticker - * @return the stock object matching the symbol/ticker - * @throws IllegalArgumentException if search symbol is null or symbol is not in stockMap - */ - public Stock getStock(String symbol) { - if (symbol == null || !stockMap.containsKey(symbol)) { - throw new IllegalArgumentException("Stock not found: " + symbol); - } - return stockMap.get(symbol); + if (stockMap.containsKey(stock.getSymbol())) { + throw new IllegalArgumentException("Duplicate stock symbol: " + stock.getSymbol()); + } + + stockMap.put(stock.getSymbol(), stock); + this.stocks.add(stock); } - /** - * Returns an {@link ObservableList} of the list of stocks for JavaFX to observe. - * - * @return an observable list of the stocks in the exchange - */ - public ObservableListReturns {@code true} if it does,v{@code false} otherwise.
+ * + * @param symbol the symbol/ticker of the stock + * @return {@code true} if the exchange contains the stock, otherwise {@code false} + */ + public boolean hasStock(String symbol) { + return stockMap.containsKey(symbol); + } + + /** + * Returns the stock object matching the given symbol/ticker. + * + * @param symbol the stock symbol/ticker + * @return the stock object matching the symbol/ticker + * @throws IllegalArgumentException if search symbol is null or symbol is not in stockMap + */ + public Stock getStock(String symbol) { + if (symbol == null || !stockMap.containsKey(symbol)) { + throw new IllegalArgumentException("Stock not found: " + symbol); + } + return stockMap.get(symbol); + } + + /** + * Returns an {@link ObservableList} of the list of stocks for JavaFX to observe. + * + * @return an observable list of the stocks in the exchange + */ + public ObservableListCreates a {@link Purchase} transaction for the + * purchase.
+ * + * @param symbol the symbol/ticker of the stock + * @param quantity the quantity of shares to buy for said stock + * @param player the player making the purchase + * @return a purchase transaction containing all details of the purchase + * @throws IllegalArgumentException if player or quantity is null + */ + public Transaction buy(String symbol, BigDecimal quantity, Player player) { + if (player == null) { + throw new IllegalArgumentException("Player cannot be null"); } - /** - * Represents a player buying a share on the exchange. - *Creates a {@link Purchase} transaction for the - * purchase.
- * - * @param symbol the symbol/ticker of the stock - * @param quantity the quantity of shares to buy for said stock - * @param player the player making the purchase - * @return a purchase transaction containing all details of the purchase - * @throws IllegalArgumentException if player or quantity is null - */ - public Transaction buy(String symbol, BigDecimal quantity, Player player) { - if (player == null) throw new IllegalArgumentException("Player cannot be null"); - if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) - throw new IllegalArgumentException("Quantity must be positive"); - - Stock stock = getStock(symbol); - Share share = new Share(stock, quantity, stock.getSalesPrice()); - Transaction transaction = purchaseFactory.create(share, week); - transaction.commit(player); - - return transaction; + if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Quantity must be positive"); } - /** - * Represents a player selling a share on the exchange. - *Creates a {@link Sale} transaction for the sale.
- * - * @param share the share to be sold - * @param player the player selling the share - * @return a sale transaction containing all details of the sale - * @throws IllegalArgumentException if player or share is null - */ - public Transaction sell(Share share, Player player) { - if (player == null) throw new IllegalArgumentException("Player cannot be null"); - if (share == null) throw new IllegalArgumentException("Share cannot be null"); - - Transaction transaction = saleFactory.create(share, week); - transaction.commit(player); - return transaction; + Stock stock = getStock(symbol); + Share share = new Share(stock, quantity, stock.getSalesPrice()); + Transaction transaction = purchaseFactory.create(share, week); + transaction.commit(player); + + return transaction; + } + + /** + * Represents a player selling a share on the exchange. + * + *Creates a {@link Sale} transaction for the sale.
+ * + * @param share the share to be sold + * @param player the player selling the share + * @return a sale transaction containing all details of the sale + * @throws IllegalArgumentException if player or share is null + */ + public Transaction sell(Share share, Player player) { + if (player == null) { + throw new IllegalArgumentException("Player cannot be null"); } - /** - * Advances the timeline of the exchange by 1 week. - *Uses a randomized value for the increase and decrease of a stocks sales price, simulating market - * fluctuations on a weekly basis.
- */ - public void advance() { - this.week++; - this.currentWeekProperty.set(week); - - double negativePriceChange = -0.05; - double positivePriceChange = 0.1; - - for (Stock stock : stockMap.values()) { - BigDecimal currentPrice = stock.getSalesPrice(); - double randomPriceMultiplier = negativePriceChange + (positivePriceChange * random.nextDouble()); - BigDecimal bdRandomPriceMultiplier = BigDecimal.valueOf(1 + randomPriceMultiplier); + if (share == null) { + throw new IllegalArgumentException("Share cannot be null"); + } - BigDecimal changedPrice = currentPrice.multiply(bdRandomPriceMultiplier) + Transaction transaction = saleFactory.create(share, week); + transaction.commit(player); + return transaction; + } + + /** + * Advances the timeline of the exchange by 1 week. + * + *Uses a randomized value for the increase and decrease + * of a stocks sales price, simulating market fluctuations on a weekly basis.
+ */ + public void advance() { + this.week++; + this.currentWeekProperty.set(week); + + double negativePriceChange = -0.05; + double positivePriceChange = 0.1; + + for (Stock stock : stockMap.values()) { + BigDecimal currentPrice = stock.getSalesPrice(); + double randomPriceMultiplier = negativePriceChange + + (positivePriceChange * random.nextDouble()); + BigDecimal bdRandomPriceMultiplier = BigDecimal.valueOf(1 + randomPriceMultiplier); + + BigDecimal changedPrice = currentPrice.multiply(bdRandomPriceMultiplier) .setScale(2, RoundingMode.HALF_UP); - if (changedPrice.compareTo(BigDecimal.ZERO) <= 0) { - changedPrice = new BigDecimal(1); - } + if (changedPrice.compareTo(BigDecimal.ZERO) <= 0) { + changedPrice = new BigDecimal(1); + } - stock.addNewSalesPrice(changedPrice); - } + stock.addNewSalesPrice(changedPrice); + } + } + + /** + * Sorts the stocks in descending order and returns a list of stocks. + * + * @param entries amount of entries in the list to display + * @param comparator the comparator used to compare the stocks + * @return a list of stocks + * @throws IllegalArgumentException if the amount of entries is negative + */ + private List- * A {@code Player} has a name, a monetary balance, a {@link Portfolio} + * + *
A {@code Player} has a name, a monetary balance, a {@link Portfolio} * of owned {@link Share}s, and a {@link TransactionArchive} containing - * a history of the player's transactions. - *
+ * a history of the player's transactions. * - *- * The player starts with an initial amount of money and may gain or - * lose funds through transactions. - *
+ *The player starts with an initial amount of money and may gain or + * lose funds through transactions.
*/ public class Player { - private final String name; - private final BigDecimal startingMoney; - private BigDecimal money; - private final Portfolio portfolio; - private final TransactionArchive transactionArchive; - - private final ObjectPropertySubscribes to the netWorth property to notify of changes in the player's net worth.
- * - * @param name the player's name; must not be {@code null} or blank - * @param startingMoney the initial monetary balance of the - * player; must not be {@code null} or negative - * @throws IllegalArgumentException if {@code name} is {@code null} or - * blank, or if {@code startingMoney} - * is {@code null}, negative, or 0 - */ - public Player(String name, BigDecimal startingMoney) { - if (name == null || name.isBlank()) { - throw new IllegalArgumentException("Player name must not be null or empty."); - } - - if (startingMoney == null || startingMoney.signum() <= 0 ) { - throw new IllegalArgumentException("Starting money must not be null, negative, nor 0."); - } - - this.name = name; - this.startingMoney = startingMoney; - this.money = startingMoney; - this.moneyProperty.set(startingMoney); - this.portfolio = new Portfolio(); - this.transactionArchive = new TransactionArchive(); - - portfolio.netWorthProperty().subscribe(price -> updateNetWorth()); - updateNetWorth(); + private final String name; + private final BigDecimal startingMoney; + private BigDecimal money; + private final Portfolio portfolio; + private final TransactionArchive transactionArchive; + + private final ObjectPropertySubscribes to the netWorth property to notify of changes in the player's net worth.
+ * + * @param name the player's name; must not be {@code null} or blank + * @param startingMoney the initial monetary balance of the + * player; must not be {@code null} or negative + * @throws IllegalArgumentException if {@code name} is {@code null} or + * blank, or if {@code startingMoney} + * is {@code null}, negative, or 0 + */ + public Player(String name, BigDecimal startingMoney) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("Player name must not be null or empty."); } - /** - * Returns an {@link ObjectProperty} of the player's net worth. - * - * @return an observable of the player's net worth - */ - public ObjectPropertyMoney added must be a {@code positive} number.
+ * + * @param amount the amount to add; must be positive and not {@code null} + * @throws IllegalArgumentException if {@code amount} is {@code null} + * or not positive + */ + public void addMoney(BigDecimal amount) { + if (amount == null) { + throw new IllegalArgumentException("Amount must not be null."); } - - /** - * Returns the full name of the player. - * - * @return the player's name - */ - public String getName() { - return this.name; + if (amount.signum() <= 0) { + throw new IllegalArgumentException("Amount must be positive."); } - /** - * Returns the player's current monetary balance. - * - * @return the player's current balance. - */ - public BigDecimal getMoney() { - return this.money; + this.money = this.money.add(amount); + this.moneyProperty.set(this.money); + updateNetWorth(); + } + + /** + * Decreases the player's monetary balance by the given amount. + * + *The player's monetary balance must not decrease below {@code 0} + * (overdrawn not possible).
+ * + * @param amount the amount to subtract; must not be negative or {@code null} + * @throws IllegalArgumentException if the amount is not positive, + * {@code null}, or insufficient funds are available + */ + public void withdrawMoney(BigDecimal amount) { + if (amount == null) { + throw new IllegalArgumentException("Amount must not be null."); } - /** - * Increases the players monetary balance by the given amount. - *- * Money added must be a {@code positive} number. - *
- * - * @param amount the amount to add; must be positive and not {@code null} - * @throws IllegalArgumentException if {@code amount] is {@code null} - * or not positive - */ - public void addMoney(BigDecimal amount) { - if (amount == null) throw new IllegalArgumentException("Amount must not be null."); - if (amount.signum() <= 0) throw new IllegalArgumentException("Amount must be positive."); - this.money = this.money.add(amount); - this.moneyProperty.set(this.money); - updateNetWorth(); + if (amount.signum() <= 0) { + throw new IllegalArgumentException("Amount must be positive."); } - /** - * Decreases the player's monetary balance by the given amount. - *- * The player's monetary balance must not decrease below {@code 0} - * (overdrawn not possible). - *
- * - * @param amount the amount to subtract; must not be negative or {@code null} - * @throws IllegalArgumentException if the amount is not positive, {@code null} - * , or insufficient funds are available - */ - public void withdrawMoney(BigDecimal amount) { - if (amount == null) throw new IllegalArgumentException("Amount must not be null."); - if (amount.signum() <= 0) throw new IllegalArgumentException("Amount must be positive."); - if (this.money.subtract(amount).signum() < 0) throw new IllegalArgumentException("Insufficient funds."); - this.money = this.money.subtract(amount); - this.moneyProperty.set(this.money); - updateNetWorth(); + if (this.money.subtract(amount).signum() < 0) { + throw new IllegalArgumentException("Insufficient funds."); } - - /** - * Returns an observable of the player's current monetary funds - * @return an observable of the player's current money - */ - public ObjectPropertyThe net worth is the sum of the player's current money and + * all the value of their shares in their portfolio.
+ * + * @return the player's current net worth + */ + public BigDecimal getNetWorth() { + return money.add(portfolio.getNetWorth()); + } + + /** + * Returns the current status/title of the player, representing their achievements. + * + * @param exchange the exchange the player is trading at + * @return a string representing the players current status + */ + public String getStatus(Exchange exchange) { + String status = "Novice"; + BigDecimal ratio = getNetWorth().divide(this.startingMoney, RoundingMode.HALF_EVEN); + + if (ratio.compareTo(new BigDecimal("1.20")) >= 0 && exchange.getWeek() >= 10) { + status = "Investor"; } - - /** - * Return's the player's portfolio of shares. - * - * @return the player's portfolio - */ - public Portfolio getPortfolio() { - return this.portfolio; + if (ratio.compareTo(new BigDecimal("2")) >= 0 && exchange.getWeek() >= 20) { + status = "Speculator"; } - /** - * Returns the archive of the player's transaction history. - * - * @return the player's transaction archive - */ - public TransactionArchive getTransactionArchive() { - return this.transactionArchive; - } - - /** - * Returns the player's current net worth. - *The net worth is the sum of the player's current money and all the value of their shares in their - * portfolio.
- * - * @return the player's current net worth - */ - public BigDecimal getNetWorth() { - return money.add(portfolio.getNetWorth()); - } - - /** - * Returns the current status/title of the player, representing their achievements. - * - * @param exchange the exchange the player is trading at - * @return a string representing the players current status - */ - public String getStatus(Exchange exchange) { - String status = "Novice"; - BigDecimal ratio = getNetWorth().divide(this.startingMoney, RoundingMode.HALF_EVEN); - if (ratio.compareTo(new BigDecimal("1.20")) >= 0 && exchange.getWeek() >= 10) status = "Investor"; - if (ratio.compareTo(new BigDecimal("2")) >= 0 && exchange.getWeek() >= 20) status = "Speculator"; - return status; - } + return status; + } } \ No newline at end of file diff --git a/millions/src/main/java/no/ntnu/gruppe53/model/Portfolio.java b/millions/src/main/java/no/ntnu/gruppe53/model/Portfolio.java index 6a3c5f9..31fda6d 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/model/Portfolio.java +++ b/millions/src/main/java/no/ntnu/gruppe53/model/Portfolio.java @@ -11,155 +11,152 @@ /** * Represents the portfolio of the Player, containing information * about which {@link Share}s are owned. - *- * A {@code Portfolio} manages a mutable collection of shares, allowing - * shares to be added, removed, and queried. - *
* - *- * Shares are associated with a unique Transaction, however, multiple - * shares from the same stock can exist in the portfolio. - *
+ *A {@code Portfolio} manages a mutable collection of shares, allowing + * shares to be added, removed, and queried.
+ * + *Shares are associated with a unique Transaction, however, multiple + * shares from the same stock can exist in the portfolio.
*/ public class Portfolio { - private final ObservableListThe {@link Stock} must not be {@code null} and the share quantity + * and purchase price must be above zero.
+ * + * @param share the share owned by the player; must not be {@code null} + * @return {@code true} if the share was added successfully, + * {@code false} otherwise + * @throws IllegalArgumentException if {@code share} is {@code null} + */ + public boolean addShare(Share share) { + if (share == null) { + throw new IllegalArgumentException("Share cannot be null"); } - /** - * Adds a share to the player's portfolio. - *- * The {@link Stock} must not be {@code null} and the share quantity - * and purchase price must be above zero. - *
- * - * @param share the share owned by the player; must not be {@code null} - * @return {@code true} if the share was added successfully, - * {@code false} otherwise - * @throws IllegalArgumentException if {@code share} is {@code null} - */ - public boolean addShare(Share share) { - if (share == null) { - throw new IllegalArgumentException("Share cannot be null"); - } - - this.shares.add(share); - - share.getStock().salesPriceProperty() + this.shares.add(share); + + share.getStock().salesPriceProperty() .subscribe(price -> updateNetWorth()); - updateNetWorth(); - return true; + updateNetWorth(); + + return true; + } + + /** + * Removes a share from the player's portfolio. + * + *The list must already exist in the portfolio to be successfully + * removed.
+ * + * @param share the share being removed; must not be {@code null} + * @return {@code true} if the share removal was successful, + * {@code false} otherwise. + * @throws IllegalArgumentException if {@code share} is {@code null} + */ + public boolean removeShare(Share share) { + if (share == null) { + throw new IllegalArgumentException("Share cannot be null"); } - /** - * Removes a share from the player's portfolio. - *- * The list must already exist in the portfolio to be successfully - * removed. - *
- * - * @param share the share being removed; must not be {@code null} - * @return {@code true} if the share removal was successful, - * {@code false} otherwise. - * @throws IllegalArgumentException if {@code share} is {@code null} - */ - public boolean removeShare(Share share) { - if (share == null) { - throw new IllegalArgumentException("Share cannot be null"); - } - - boolean removed = this.shares.removeIf(s -> s.equals(share)); - - if (removed) { - updateNetWorth(); - } - - return removed; - } + boolean removed = this.shares.removeIf(s -> s.equals(share)); - /** - * Returns a list of the shares currently in the player's portfolio. - * - * @return a list of the shares in this portfolio - */ - public ObservableList- * Portfolio value is based on current market price, not transaction rules. - *
- * - * @return the total market value of the portfolio - */ - public BigDecimal getNetWorth() { - return this.shares.stream() + return tempSymbols; + } + + /** + * Determines whether the player's portfolio contains the specified + * share. + * + * @param share the share to search for in this portfolio + * @return {@code true} if the player's portfolio contains the share + * {@code false} otherwise + */ + public boolean contains(Share share) { + return shares.stream().anyMatch(s -> s.equals(share)); + } + + /** + * Calculates the net total value of the shares in the portfolio. + * + *Portfolio value is based on current market price, not transaction rules.
+ * + * @return the total market value of the portfolio + */ + public BigDecimal getNetWorth() { + return this.shares.stream() .map(share -> share.getStock().getSalesPrice() .multiply(share.getQuantity()) ) .reduce(BigDecimal.ZERO, BigDecimal::add); - } - - /** - * Returns an {@link ObjectProperty} of the net worth of the portfolio - * - * @return an observable of the portfolio's net worth - */ - public ObjectProperty- * A purchase can only be commited if: + * a {@link Share}. + * + *
A purchase can only be commited if: *
If the transaction is not already committed, and the player + * has sufficient funds, the transaction will: + *
- * If the transaction is not already committed, and the player - * has sufficient funds, the transaction will: - *
Also sets up the ObjectProperties of the different calculations such - * that Subscriptions can listen to the calculations
- * @param share the share to calculate purchase costs for - */ - public PurchaseCalculator(Share share) { - - this.purchasePrice = share.getPurchasePrice(); - this.quantity = share.getQuantity(); - - this.grossProperty.set(calculateGross()); - this.commissionProperty.set(calculateCommission()); - this.totalProperty.set(calculateTotal()); - - } - - /** - * Calculates the gross purchase price of the given share. - * - * @return the gross associated cost of purchasing the share - */ - @Override - public BigDecimal calculateGross(){ - - return this.quantity.multiply(this.purchasePrice); - } - - /** - * Calculates the commission of the exchange for purchasing a share. - *Commission is set to a fixed 0.5% of gross purchase price.
- * - * @return the commission taken by the exchange - */ - @Override +public class PurchaseCalculator implements TransactionCalculator { + private final BigDecimal purchasePrice; + private final BigDecimal quantity; + + // Objectproperties + private final ObjectPropertyAlso sets up the ObjectProperties of the different calculations such + * that Subscriptions can listen to the calculations
+ * + * @param share the share to calculate purchase costs for + */ + public PurchaseCalculator(Share share) { + this.purchasePrice = share.getPurchasePrice(); + this.quantity = share.getQuantity(); + + this.grossProperty.set(calculateGross()); + this.commissionProperty.set(calculateCommission()); + this.totalProperty.set(calculateTotal()); + + } + + /** + * Calculates the gross purchase price of the given share. + * + * @return the gross associated cost of purchasing the share + */ + @Override + public BigDecimal calculateGross() { + return this.quantity.multiply(this.purchasePrice); + } + + /** + * Calculates the commission of the exchange for purchasing a share. + * + *Commission is set to a fixed 0.5% of gross purchase price.
+ * + * @return the commission taken by the exchange + */ + @Override public BigDecimal calculateCommission() { - BigDecimal commission = new BigDecimal("0.005"); - - - - return calculateGross().multiply(commission); - } - - /** - * Calculates the tax for a purchase. - *For a purchase, there is no tax.
- * - * @return the tax for a purchase, in this case zero - */ - @Override - public BigDecimal calculateTax() { - return new BigDecimal(0); - } - - /** - * Calculates the total costs associated with the purchase of a share - * - * @return the net total cost of purchasing a share - */ - @Override - public BigDecimal calculateTotal() { - - - return calculateGross().add(calculateCommission()).add(calculateTax()); - } - - /** - * Returns an observable of the calculated gross - * @return an observable of the calculated gross - */ - public ObjectPropertyFor a purchase, there is no tax.
+ * + * @return the tax for a purchase, in this case zero + */ + @Override + public BigDecimal calculateTax() { + return new BigDecimal(0); + } + + /** + * Calculates the total costs associated with the purchase of a share. + * + * @return the net total cost of purchasing a share + */ + @Override + public BigDecimal calculateTotal() { + return calculateGross().add(calculateCommission()).add(calculateTax()); + } + + /** + * Returns an observable of the calculated gross. + * + * @return an observable of the calculated gross + */ + public ObjectProperty- * A sale can only be commited if: + * a {@link Share}. + * + *
A sale can only be commited if: *
If the player ows the share, the transaction will: + *
- * If the player ows the share, the transaction will: - *
The commission is set to a fixed 1% of the gross total sale value.
- * - * @return the commission taken by the exchange - */ - @Override + } + + /** + * Calculates the exchange's commission for selling the share. + * + *The commission is set to a fixed 1% of the gross total sale value.
+ * + * @return the commission taken by the exchange + */ + @Override public BigDecimal calculateCommission() { - BigDecimal commissionRate = new BigDecimal("0.01"); + BigDecimal commissionRate = new BigDecimal("0.01"); - return calculateGross() + return calculateGross() .multiply(commissionRate) .setScale(2, RoundingMode.HALF_UP); - } + } - /** - * Calculates the taxable profit from the sale. - *Only calculates tax for profitable sales.
> - * - * @return the tax amount - */ - @Override - public BigDecimal calculateTax() { + /** + * Calculates the taxable profit from the sale. + * + *Only calculates tax for profitable sales.
> + * + * @return the tax amount + */ + @Override + public BigDecimal calculateTax() { - BigDecimal taxRate = new BigDecimal("0.30"); + BigDecimal taxRate = new BigDecimal("0.30"); - BigDecimal originalCost = purchasePrice.multiply(quantity); + BigDecimal originalCost = purchasePrice.multiply(quantity); - BigDecimal profit = calculateGross() + BigDecimal profit = calculateGross() .subtract(calculateCommission()) .subtract(originalCost); - if (profit.compareTo(BigDecimal.ZERO) <= 0) { - return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); - } + if (profit.compareTo(BigDecimal.ZERO) <= 0) { + return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); + } - return profit.multiply(taxRate) + return profit.multiply(taxRate) .setScale(2, RoundingMode.HALF_UP); - } + } - /** - * Returns the net total gain for selling the share. - * - * @return the net total winnings of selling the share - */ - @Override - public BigDecimal calculateTotal() { + /** + * Returns the net total gain for selling the share. + * + * @return the net total winnings of selling the share + */ + @Override + public BigDecimal calculateTotal() { - return calculateGross() + return calculateGross() .subtract(calculateCommission()) .subtract(calculateTax()) .setScale(2, RoundingMode.HALF_UP); - } - - /** - * Returns an observable of the calculated gross - * @return an observable of the calculated gross - */ - public ObjectProperty- * A {@code Share} contains the quantity of shares owned and + * + *
A {@code Share} contains the quantity of shares owned and * the price per share at the time of purchase. Future price * fluctuations are not included in share. *
*/ public class Share { - private final Stock stock; - private final BigDecimal quantity; - private final BigDecimal purchasePrice; + private final Stock stock; + private final BigDecimal quantity; + private final BigDecimal purchasePrice; - /** - * Creates a share for a given stock. - * - * @param stock the stock associated with the share; must not be {@code null} - * @param quantity the number of shares purchased; must not be {@code null}, negative, or zero - * @param purchasePrice price per share at the time of purchase; must not be {@code null}, negative or zero - * @throws IllegalArgumentException if {@code stock} is {@code null}, or {@code quantity} is {@code null}, - * negative, or zero, or {@code purchasePrice} is {@code null}, negative, - * or zero - */ - - public Share(Stock stock, BigDecimal quantity, BigDecimal purchasePrice) { - if (stock == null) { - throw new IllegalArgumentException("Stock cannot be null."); - } - - if (quantity == null || quantity.signum() <= 0) { - throw new IllegalArgumentException("Quantity cannot be null, negative, or zero."); - } - - if (purchasePrice == null || purchasePrice.signum() <= 0) { - throw new IllegalArgumentException("PurchasePrice cannot be null, negative, or zero."); - } + /** + * Creates a share for a given stock. + * + * @param stock the stock associated with the share; must not be {@code null} + * @param quantity the number of shares purchased; must not be {@code null}, negative, or zero + * @param purchasePrice price per share at the time of purchase; + * must not be {@code null}, negative or zero + * @throws IllegalArgumentException if {@code stock} is {@code null}, + * or {@code quantity} is {@code null}, + * negative, or zero, or {@code purchasePrice} + * is {@code null}, negative, or zero + */ + public Share(Stock stock, BigDecimal quantity, BigDecimal purchasePrice) { + if (stock == null) { + throw new IllegalArgumentException("Stock cannot be null."); + } - this.stock = stock; - this.quantity = quantity; - this.purchasePrice = purchasePrice; + if (quantity == null || quantity.signum() <= 0) { + throw new IllegalArgumentException("Quantity cannot be null, negative, or zero."); } - /** - * Returns the stock the share is associated with. - * - * @return the stock - */ - public Stock getStock() { - return stock; + if (purchasePrice == null || purchasePrice.signum() <= 0) { + throw new IllegalArgumentException("PurchasePrice cannot be null, negative, or zero."); } - /** - * Returns the quantity of shares owned in the stock. - * - * @return the quantity of shares - */ + this.stock = stock; + this.quantity = quantity; + this.purchasePrice = purchasePrice; + } - public BigDecimal getQuantity() { - return quantity; - } + /** + * Returns the stock the share is associated with. + * + * @return the stock + */ + public Stock getStock() { + return stock; + } - /** - * Returns the purchase price per share for the stock at time of purchase. - * - * @return the purchase price of the shares. - */ - public BigDecimal getPurchasePrice() { - return purchasePrice; - } + /** + * Returns the quantity of shares owned in the stock. + * + * @return the quantity of shares + */ + + public BigDecimal getQuantity() { + return quantity; + } + + /** + * Returns the purchase price per share for the stock at time of purchase. + * + * @return the purchase price of the shares. + */ + public BigDecimal getPurchasePrice() { + return purchasePrice; + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/model/Stock.java b/millions/src/main/java/no/ntnu/gruppe53/model/Stock.java index 6fe5424..04c53c8 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/model/Stock.java +++ b/millions/src/main/java/no/ntnu/gruppe53/model/Stock.java @@ -1,162 +1,163 @@ package no.ntnu.gruppe53.model; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; /** * Represents a stock with its ticker symbol, company name, * and a list of sales prices. - *- * The list is a historical representation of price changes + * + *
The list is a historical representation of price changes * over time for the given stock. *
- *- * The most recently added price represents the current market price. - *
* - *- * A {@code Stock} instance always contains at least one price, - * instantiated at construction. + *
The most recently added price represents the current market price. *
+ * + *A {@code Stock} instance always contains at least one price, + * instantiated at construction.
*/ public class Stock { - private final String symbol; - private final String company; - private final List- * The current price is defined as the most recent price in the price history. - *
- * @return the current sale price of the stock - */ - public BigDecimal getSalesPrice() { - return salesPrice.get(); - } - - /** - * Adds a new sales price to the stock's price history. - *- * The added price becomes the new current market price. - *
- * - *- * The new price must be positive. - *
- * @param price the new market price per share of the stock; must not be {@code null}, negative or zero - * @throws IllegalArgumentException if {@code price} is {@code null}, negative, or zero - */ - public void addNewSalesPrice(BigDecimal price) { - if (price == null || price.signum() <= 0) { - throw new IllegalArgumentException("Price cannot be null, negative, or zero."); - } - this.prices.add(price); - this.salesPrice.set(price); - } - - - /** - * Returns an {@link ObjectProperty} of the sales price. - * - * @return an observable of the sales price - */ - public ObjectPropertyThe current price is defined as the most recent price + * in the price history.
+ * + * @return the current sale price of the stock + */ + public BigDecimal getSalesPrice() { + return salesPrice.get(); + } + + /** + * Adds a new sales price to the stock's price history. + * + *The added price becomes the new current market price.
+ * + *The new price must be positive.
+ * + * @param price the new market price per share of the stock; + * must not be {@code null}, negative or zero + * @throws IllegalArgumentException if {@code price} is {@code null}, negative, or zero + */ + public void addNewSalesPrice(BigDecimal price) { + if (price == null || price.signum() <= 0) { + throw new IllegalArgumentException("Price cannot be null, negative, or zero."); } - /** - * Returns the sales price increase/decrease from previous week to this week. Returns 0 if - * the price list only has 1 value. - * - * @return the net difference in sales price between the last and second last week - */ - public BigDecimal getLatestPriceChange() { - if (prices.size() >= 2) { - return prices.getLast().subtract(prices.get(prices.size() - 2)); - } else { - return BigDecimal.ZERO; - } + this.prices.add(price); + this.salesPrice.set(price); + } + + /** + * Returns an {@link ObjectProperty} of the sales price. + * + * @return an observable of the sales price + */ + public ObjectProperty- * A transaction occurs in a specific week and uses a + * + *
A transaction occurs in a specific week and uses a * {@link TransactionCalculator} to determine the monetary change. * Transactions are unique and is committed to a {@link Player}'s - * {@link Portfolio}. - *
+ * {@link Portfolio}. * - *- * Subclasses define the nature of the transaction (e.g. purchase - * or sale) by implementing the {@link #commit(Player)} - *
+ *Subclasses define the nature of the transaction (e.g. purchase + * or sale) by implementing the {@link #commit(Player)}
*/ +public abstract class Transaction { + private final Share share; + private final int week; + private final TransactionCalculator calculator; + protected boolean committed; -abstract public class Transaction { - private final Share share; - private final int week; - private final TransactionCalculator calculator; - protected boolean committed; - - /** - * Constructor for a transaction. - * - * @param share the share for the transaction; must not be {@code null} - * @param week the week the transaction takes place; must be greater than 0 - * @param calculator the transaction calculator used to - * compute the {@link Transaction} - * @throws IllegalArgumentException if {@code share} is {@code null} or {@code week} - * is less than 1 - */ - - protected Transaction(Share share, int week, TransactionCalculator calculator) { - if (week <= 0) { - throw new IllegalArgumentException("Week cannot be negative or zero"); - } + /** + * Constructor for a transaction. + * + * @param share the share for the transaction; must not be {@code null} + * @param week the week the transaction takes place; must be greater than 0 + * @param calculator the transaction calculator used to + * compute the {@link Transaction} + * @throws IllegalArgumentException if {@code share} is {@code null} or {@code week} + * is less than 1 + */ - this.share = share; - this.week = week; - this.calculator = calculator; - this.committed = false; + protected Transaction(Share share, int week, TransactionCalculator calculator) { + if (week <= 0) { + throw new IllegalArgumentException("Week cannot be negative or zero"); } - /** - * Returns the share for the current transaction. - * - * @return the share belonging to the transaction - */ - public Share getShare() { - return this.share; - } + this.share = share; + this.week = week; + this.calculator = calculator; + this.committed = false; + } - /** - * Returns the week the transaction took place. - * - * @return the week for the transaction - */ - public int getWeek() { - return this.week; - } + /** + * Returns the share for the current transaction. + * + * @return the share belonging to the transaction + */ + public Share getShare() { + return this.share; + } - /** - * Returns the transaction calculator used to calculate - * the transaction. - * - * @return the transaction calculator used for the transaction - */ + /** + * Returns the week the transaction took place. + * + * @return the week for the transaction + */ + public int getWeek() { + return this.week; + } - public TransactionCalculator getCalculator() { - return this.calculator; - } + /** + * Returns the transaction calculator used to calculate + * the transaction. + * + * @return the transaction calculator used for the transaction + */ - /** - * Returns whether the transaction has been committed to a player. - * - * @return {@code true} if committed, {@code false} otherwise - */ + public TransactionCalculator getCalculator() { + return this.calculator; + } - public boolean isCommitted() { - return this.committed; - } + /** + * Returns whether the transaction has been committed to a player. + * + * @return {@code true} if committed, {@code false} otherwise + */ - /** - * Commits the transaction to a player, making it unique. - * - *- * Subclasses should define the business logic required before - * committing. Then {@code super.commit(player)} should be called - * once the transaction has been successfully applied. - *
- * @param player the player performing the transaction; must not be {@code null} - */ + public boolean isCommitted() { + return this.committed; + } - public void commit(Player player) { - this.committed = true; - } + /** + * Commits the transaction to a player, making it unique. + * + *Subclasses should define the business logic required before + * committing. Then {@code super.commit(player)} should be called + * once the transaction has been successfully applied.
+ * + * @param player the player performing the transaction; must not be {@code null} + */ + + public void commit(Player player) { + this.committed = true; + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/model/TransactionArchive.java b/millions/src/main/java/no/ntnu/gruppe53/model/TransactionArchive.java index 70bba67..88e5f7b 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/model/TransactionArchive.java +++ b/millions/src/main/java/no/ntnu/gruppe53/model/TransactionArchive.java @@ -1,143 +1,145 @@ package no.ntnu.gruppe53.model; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; /** * Maintains a history of all completed {@link Transaction}s. - *- * A {@code TransactionArchive} stores transactions in the order + * + *
A {@code TransactionArchive} stores transactions in the order * they are added and provides methods to retrieve transactions - * filtered by type and week. - *
+ * filtered by type and week. */ public class TransactionArchive { - private final ObservableListA week is considered distinct if at least one transaction has + * occurred within that week. Multiple transactions in the span of the + * same week will only be counted as one distinct week.
+ * + * @return the number of unique week values in the archive; + * {@code 0} if the archive contains no transactions + */ + + public int countDistinctWeeks() { + var distinctWeeks = new HashSet- * A week is considered distinct if at least one transaction has - * occurred within that week. Multiple transactions in the span of the - * same week will only be counted as one distinct week. - *
- * - * @return the number of unique week values in the archive; - * {@code 0} if the archive contains no transactions - */ - - public int countDistinctWeeks() { - var distinctWeeks = new HashSet{@link Purchase} and {@link Sale} are examples of implementations of a transaction.
*/ public interface TransactionCalculator { - BigDecimal calculateGross(); - BigDecimal calculateCommission(); - BigDecimal calculateTax(); - BigDecimal calculateTotal(); + /** + * Calculates the gross value of a transaction. + * + * @return the gross value of a transaction + */ + BigDecimal calculateGross(); + + /** + * Calculates the commission for the exchange of a transaction. + * + * @return the commission of a transaction + */ + BigDecimal calculateCommission(); + + /** + * Calculates the tax of a transaction. + * + * @return the tax of a transaction + */ + BigDecimal calculateTax(); + + /** + * Returns the total value of a transaction. + * + * @return the total value of a transaction + */ + BigDecimal calculateTotal(); } diff --git a/millions/src/main/java/no/ntnu/gruppe53/model/TransactionFactory.java b/millions/src/main/java/no/ntnu/gruppe53/model/TransactionFactory.java index 9ddb766..cb27b9f 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/model/TransactionFactory.java +++ b/millions/src/main/java/no/ntnu/gruppe53/model/TransactionFactory.java @@ -2,31 +2,38 @@ /** * Factory for creating a {@link Transaction} for a {@link Share} in an {@link Exchange}. + * *Subclasses implement the specific type of transaction to be used.
*/ public abstract class TransactionFactory { - /** - * Creates a transaction. - * - * @param share the share involved - * @param week the transaction week - * @return a transaction - */ - public Transaction create(Share share, int week) { - - if (share == null) { - throw new IllegalArgumentException("Share cannot be null."); - } - - if (week <= 0) { - throw new IllegalArgumentException("Week must be positive whole number."); - } + /** + * Creates a transaction. + * + * @param share the share involved + * @param week the transaction week + * @return a transaction + */ + public Transaction create(Share share, int week) { + if (share == null) { + throw new IllegalArgumentException("Share cannot be null."); + } - return createTransaction(share, week); + if (week <= 0) { + throw new IllegalArgumentException("Week must be positive whole number."); } - protected abstract Transaction createTransaction( + return createTransaction(share, week); + } + + /** + * Creates a transaction from a share and week. + * + * @param share the share for the transaction + * @param week the week of the transaction + * @return a transaction for the share and week + */ + protected abstract Transaction createTransaction( Share share, int week ); diff --git a/millions/src/main/java/no/ntnu/gruppe53/service/FileHandler.java b/millions/src/main/java/no/ntnu/gruppe53/service/FileHandler.java index 8ed2b0d..a587c09 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/service/FileHandler.java +++ b/millions/src/main/java/no/ntnu/gruppe53/service/FileHandler.java @@ -1,8 +1,5 @@ package no.ntnu.gruppe53.service; -import no.ntnu.gruppe53.model.Stock; - - import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; @@ -13,131 +10,146 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import no.ntnu.gruppe53.model.Stock; /** * Handles reading and writing of .csv files for stocks. */ public class FileHandler { + /** + * Writes a list of {@link Stock}s to a CSV file in UTF-8 encoding. + * + * @param stockList the list of stocks to be written + * @param path directory path for the file + * @param filename name of the file (without or with .csv extension) + * @throws IllegalArgumentException if stockList, path, + * or filename is null, or if stockList is empty + */ + public static void writeStocksToFile(ListReturns "0.00" if the BigDecimal is null.
- * - * @param number the BigDecimal to be formated - * @return the string representation of the BigDecimal with 2 decimals accuracy. - */ - public static String formatNumber(BigDecimal number) { - return number == null ? "0.00" : number.setScale(2, RoundingMode.HALF_UP).toPlainString(); - } + /** + * Static method used to format a Big Decimal to a string representation with 2 decimals accuracy. + * + *Returns "0.00" if the BigDecimal is null.
+ * + * @param number the BigDecimal to be formated + * @return the string representation of the BigDecimal with 2 decimals accuracy. + */ + public static String formatNumber(BigDecimal number) { + return number == null ? "0.00" : number.setScale(2, RoundingMode.HALF_UP).toPlainString(); + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java index 776f15a..6d6356d 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java +++ b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java @@ -1,97 +1,101 @@ package no.ntnu.gruppe53.service; +import java.util.Locale; +import java.util.ResourceBundle; import javafx.beans.binding.Bindings; import javafx.beans.binding.StringBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import java.util.Locale; -import java.util.ResourceBundle; - /** * Manages the language used in the user-interface. + * *The possible languages to be used are set by {@link LanguageRegistry}.
*/ public class LanguageManager { - /** - * Sets a default language and creates an observable {@link ResourceBundle}. - */ - private final ObjectPropertySearches through the registry to find english and sets it as default.
- */ - private static final LanguageOption defaultLang = languages.stream() - .filter(lang -> lang.locale().getLanguage().equals("en")) - .findFirst() - .orElse(languages.getFirst()); + /** + * Sets the default language to be used. + * + *Searches through the registry to find english and sets it as default.
+ */ + private static final LanguageOption defaultLang = + languages.stream() + .filter(lang -> lang.locale().getLanguage().equals("en")) + .findFirst() + .orElse(languages.getFirst()); - /** - * Returns all registered languages - * - * @return a list of registered languages - */ - public static ListReturns the {@code XYChart.Series}.
- * - * @param stock the stock whose price values will be shown in the LineChart - * @param exchange the exchange containing the stock whose week number will be shown in the LineChart - * @return the {@code XYChart.SeriesReturns the {@code XYChart.Series}.
+ * + * @param stock the stock whose price values will be shown in the LineChart + * @param exchange the exchange containing the stock whose + * week number will be shown in the LineChart + * @return the {@code XYChart.SeriesReturns a list of the {@code XYChart.Series} for each stock.
- * - * @param stocks the list of stocks whose price values will be shown in the LineChart - * @param exchange the exchange containing the stock whose week number will be shown in the LineChart - * @return the list of {@code XYChart.SeriesReturns a list of the {@code XYChart.Series} for each stock.
+ * + * @param stocks the list of stocks whose price values will be shown + * in the LineChart + * @param exchange the exchange containing the stock whose week number + * will be shown in the LineChart + * @return the list of {@code XYChart.SeriesUses a {@link LanguageManager} to insert set text to target language.
- */ -public class CSVChooserView { - private final FileChooser fileChooser = new FileChooser(); - Alert newGameAlert = new Alert(Alert.AlertType.INFORMATION); - private final LanguageManager lm = LanguageManager.getInstance(); - - /** - * Returns the dialog for the alert and file chooser and exposes it to controllers. - * - * @param stage the stage for the dialog to be shown - * @return the dialog for the alert and file chooser - */ - public File getDialog(Stage stage) { - // Notifies the user about .csv selection - newGameAlert.setTitle(null); - newGameAlert.headerTextProperty().bind(lm.bindString("csvHeader")); - newGameAlert.contentTextProperty().bind(lm.bindString("csvContent")); - - // Sets the dimensions of the alert to show all text, and as a band-aid, be resizable - newGameAlert.setResizable(true); - - newGameAlert.getDialogPane().setPrefWidth(450); - newGameAlert.getDialogPane().setPrefHeight(350); - - newGameAlert.showAndWait(); - - // File chooser to choose .csv file - fileChooser.setTitle(lm.getString("csvFileChooserTitle")); - - // Limits to .csv file only - fileChooser.getExtensionFilters().clear(); - fileChooser.getExtensionFilters().add( - new FileChooser.ExtensionFilter(lm.getString("csvFileFilterLabel"), "*.csv") - ); - - return fileChooser.showOpenDialog(stage); } -} \ No newline at end of file diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/CsvChooserView.java b/millions/src/main/java/no/ntnu/gruppe53/view/CsvChooserView.java new file mode 100644 index 0000000..4264887 --- /dev/null +++ b/millions/src/main/java/no/ntnu/gruppe53/view/CsvChooserView.java @@ -0,0 +1,51 @@ +package no.ntnu.gruppe53.view; + +import java.io.File; +import javafx.scene.control.Alert; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import no.ntnu.gruppe53.service.LanguageManager; + +/** + * Represents an {@link Alert} and {@link FileChooser} for + * informing user of .csv requirement and picking said file. + * + *Uses a {@link LanguageManager} to insert set text to target language.
+ */ +public class CsvChooserView { + private final FileChooser fileChooser = new FileChooser(); + Alert newGameAlert = new Alert(Alert.AlertType.INFORMATION); + private final LanguageManager lm = LanguageManager.getInstance(); + + /** + * Returns the dialog for the alert and file chooser and exposes it to controllers. + * + * @param stage the stage for the dialog to be shown + * @return the dialog for the alert and file chooser + */ + public File getDialog(Stage stage) { + // Notifies the user about .csv selection + newGameAlert.setTitle(null); + newGameAlert.headerTextProperty().bind(lm.bindString("csvHeader")); + newGameAlert.contentTextProperty().bind(lm.bindString("csvContent")); + + // Sets the dimensions of the alert to show all text, and as a band-aid, be resizable + newGameAlert.setResizable(true); + + newGameAlert.getDialogPane().setPrefWidth(450); + newGameAlert.getDialogPane().setPrefHeight(350); + + newGameAlert.showAndWait(); + + // File chooser to choose .csv file + fileChooser.setTitle(lm.getString("csvFileChooserTitle")); + + // Limits to .csv file only + fileChooser.getExtensionFilters().clear(); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter(lm.getString("csvFileFilterLabel"), "*.csv") + ); + + return fileChooser.showOpenDialog(stage); + } +} \ No newline at end of file diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/EndView.java b/millions/src/main/java/no/ntnu/gruppe53/view/EndView.java index 9ac97de..611e997 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/view/EndView.java +++ b/millions/src/main/java/no/ntnu/gruppe53/view/EndView.java @@ -4,200 +4,210 @@ import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.layout.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import no.ntnu.gruppe53.service.LanguageManager; - /** * Represents the End Game screen, showing the Level the Player reached, * money earned in total after selling portfolio and gives the option * to quit the program or start a new game. */ public class EndView extends BorderPane { - - - private Button newGameButton; - private Button quitButton; - - private Label statusLabel; - private Label moneyLabel; - - - private final LanguageManager lm = LanguageManager.getInstance(); - - public EndView() { - this.setCenter(centerPane()); - } - - private BorderPane centerPane() { - BorderPane pane = new BorderPane(); - pane.setPadding(new Insets(15, 12, 15, 12)); - //pane.setSpacing(10); - //Blue Background color - pane.setStyle("-fx-background-color: #00bcf0;"); - - VBox centerPane = new VBox(); - - HBox congratsBox = new HBox(); - Label congratsLabel = new Label(); - congratsLabel.textProperty().bind(lm.bindString("congratsLabel")); - congratsLabel.setStyle("-fx-font-size: 18"); - congratsLabel.setTextAlignment(TextAlignment.CENTER); - - congratsBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - congratsBox.setPadding(new Insets(8, 14, 8, 14)); - congratsBox.setAlignment(Pos.CENTER); - congratsBox.setMinHeight(65); - - congratsBox.getChildren().add(congratsLabel); - - HBox statusLabelBox = new HBox(); - Label statusLabelText = new Label(); - statusLabelText.textProperty().bind(lm.bindString("statusLabelText")); - statusLabelText.setStyle("-fx-font-size: 18"); - statusLabel = new Label(); - statusLabel.setStyle("-fx-font-size: 18"); - - statusLabelBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - statusLabelBox.setPadding(new Insets(8, 14, 8, 14)); - statusLabelBox.setAlignment(Pos.CENTER); - statusLabelBox.setMinHeight(65); - - statusLabelBox.getChildren().addAll(statusLabelText, statusLabel); - - HBox moneyLabelBox = new HBox(); - Label moneyLabelText = new Label(); - moneyLabelText.textProperty().bind(lm.bindString("navMoneyLabelText")); - moneyLabelText.setStyle("-fx-font-size: 18"); - moneyLabel = new Label(); - moneyLabel.setStyle("-fx-font-size: 18"); - - moneyLabelBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - moneyLabelBox.setPadding(new Insets(8, 14, 8, 14)); - moneyLabelBox.setAlignment(Pos.CENTER); - moneyLabelBox.setMinHeight(65); - - moneyLabelBox.getChildren().addAll(moneyLabelText, moneyLabel); - - centerPane.setPadding(new Insets(15, 12, 15, 12)); - centerPane.setAlignment(Pos.CENTER); - centerPane.setSpacing(10); - centerPane.setMaxWidth(700); - - - centerPane.getChildren().addAll(congratsBox, statusLabelBox, moneyLabelBox); - - HBox quitButtonBox = new HBox(); - quitButton = new Button(); - quitButton.textProperty().bind(lm.bindString("quitGame")); - quitButton.setPrefSize(200, 50); - quitButton.setStyle("-fx-text-fill: #303539;" + - "-fx-font-size: 18px;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" + - "-fx-background-radius: 8;" - ); - - - quitButtonBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - quitButtonBox.setPadding(new Insets(8, 14, 8, 14)); - quitButtonBox.setAlignment(Pos.CENTER); - - - quitButtonBox.getChildren().addAll(quitButton); - - HBox newGameButtonBox = new HBox(); - newGameButton = new Button(); - newGameButton.textProperty().bind(lm.bindString("newGame")); - newGameButton.setPrefSize(200, 50); - newGameButton.setStyle("-fx-text-fill: #303539;" + - "-fx-font-size: 18px;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" + - "-fx-background-radius: 8;" - ); - newGameButtonBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - newGameButtonBox.setPadding(new Insets(8, 14, 8, 14)); - newGameButtonBox.setAlignment(Pos.CENTER); - newGameButtonBox.getChildren().add(newGameButton); - - HBox buttons = new HBox(quitButtonBox, newGameButtonBox); - - - - Region spacerBottomLeft = new Region(); - Region spacerBottomRight = new Region(); - - HBox.setHgrow(spacerBottomLeft, Priority.ALWAYS); - HBox.setHgrow(spacerBottomRight, Priority.ALWAYS); - - HBox bottomPane = new HBox(spacerBottomLeft, buttons, spacerBottomRight); - - pane.setCenter(centerPane); - pane.setBottom(bottomPane); - - return pane; - } - - /** - * Sets the action to be run when clicking the quit button - * - * @param action the action to run when clicking the quit button - */ - public void onQuitButton(Runnable action) { - quitButton.setOnAction(e -> action.run()); - } - - /** - * Sets the action to be run when clicking the New Game button - * - * @param action the action to run when clicking the New Game button - */ - public void onNewGameButton(Runnable action) { - newGameButton.setOnAction(e -> action.run()); - } - - /** - * Returns the statusLabel. - * - * @return the statusLabel - */ - public Label getStatusLabel() { - return statusLabel; - } - - /** - * Returns the moneyLabel. - * - * @return the moneyLabel - */ - public Label getMoneyLabel() { - return moneyLabel; - } + private Button newGameButton; + private Button quitButton; + + private Label statusLabel; + private Label moneyLabel; + + private final LanguageManager lm = LanguageManager.getInstance(); + + /** + * Sets the centerPane to the constructed centerPane. + */ + public EndView() { + this.setCenter(centerPane()); + } + + /** + * Constructs the centerPane using a {@link BorderPane}. + * + * @return a constructed borderpane + */ + private BorderPane centerPane() { + BorderPane pane = new BorderPane(); + pane.setPadding(new Insets(15, 12, 15, 12)); + // pane.setSpacing(10); + // Blue Background color + pane.setStyle("-fx-background-color: #00bcf0;"); + + Label congratsLabel = new Label(); + congratsLabel.textProperty().bind(lm.bindString("congratsLabel")); + congratsLabel.setStyle("-fx-font-size: 18"); + congratsLabel.setTextAlignment(TextAlignment.CENTER); + + HBox congratsBox = new HBox(); + congratsBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + congratsBox.setPadding(new Insets(8, 14, 8, 14)); + congratsBox.setAlignment(Pos.CENTER); + congratsBox.setMinHeight(65); + + congratsBox.getChildren().add(congratsLabel); + + Label statusLabelText = new Label(); + statusLabelText.textProperty().bind(lm.bindString("statusLabelText")); + statusLabelText.setStyle("-fx-font-size: 18"); + statusLabel = new Label(); + statusLabel.setStyle("-fx-font-size: 18"); + + HBox statusLabelBox = new HBox(); + statusLabelBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + statusLabelBox.setPadding(new Insets(8, 14, 8, 14)); + statusLabelBox.setAlignment(Pos.CENTER); + statusLabelBox.setMinHeight(65); + + statusLabelBox.getChildren().addAll(statusLabelText, statusLabel); + + Label moneyLabelText = new Label(); + moneyLabelText.textProperty().bind(lm.bindString("navMoneyLabelText")); + moneyLabelText.setStyle("-fx-font-size: 18"); + moneyLabel = new Label(); + moneyLabel.setStyle("-fx-font-size: 18"); + + HBox moneyLabelBox = new HBox(); + moneyLabelBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + moneyLabelBox.setPadding(new Insets(8, 14, 8, 14)); + moneyLabelBox.setAlignment(Pos.CENTER); + moneyLabelBox.setMinHeight(65); + + moneyLabelBox.getChildren().addAll(moneyLabelText, moneyLabel); + + VBox centerPane = new VBox(); + centerPane.setPadding(new Insets(15, 12, 15, 12)); + centerPane.setAlignment(Pos.CENTER); + centerPane.setSpacing(10); + centerPane.setMaxWidth(700); + + + centerPane.getChildren().addAll(congratsBox, statusLabelBox, moneyLabelBox); + + quitButton = new Button(); + quitButton.textProperty().bind(lm.bindString("quitGame")); + quitButton.setPrefSize(200, 50); + quitButton.setStyle("-fx-text-fill: #303539;" + + "-fx-font-size: 18px;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + + "-fx-background-radius: 8;" + ); + + HBox quitButtonBox = new HBox(); + quitButtonBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + quitButtonBox.setPadding(new Insets(8, 14, 8, 14)); + quitButtonBox.setAlignment(Pos.CENTER); + + + quitButtonBox.getChildren().addAll(quitButton); + + newGameButton = new Button(); + newGameButton.textProperty().bind(lm.bindString("newGame")); + newGameButton.setPrefSize(200, 50); + newGameButton.setStyle("-fx-text-fill: #303539;" + + "-fx-font-size: 18px;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + + "-fx-background-radius: 8;" + ); + + HBox newGameButtonBox = new HBox(); + newGameButtonBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + newGameButtonBox.setPadding(new Insets(8, 14, 8, 14)); + newGameButtonBox.setAlignment(Pos.CENTER); + newGameButtonBox.getChildren().add(newGameButton); + + HBox buttons = new HBox(quitButtonBox, newGameButtonBox); + + Region spacerBottomLeft = new Region(); + Region spacerBottomRight = new Region(); + + HBox.setHgrow(spacerBottomLeft, Priority.ALWAYS); + HBox.setHgrow(spacerBottomRight, Priority.ALWAYS); + + HBox bottomPane = new HBox(spacerBottomLeft, buttons, spacerBottomRight); + + pane.setCenter(centerPane); + pane.setBottom(bottomPane); + + return pane; + } + + /** + * Sets the action to be run when clicking the quit button. + * + * @param action the action to run when clicking the quit button + */ + public void onQuitButton(Runnable action) { + quitButton.setOnAction(e -> action.run()); + } + + /** + * Sets the action to be run when clicking the New Game button. + * + * @param action the action to run when clicking the New Game button + */ + public void onNewGameButton(Runnable action) { + newGameButton.setOnAction(e -> action.run()); + } + + /** + * Returns the statusLabel. + * + * @return the statusLabel + */ + public Label getStatusLabel() { + return statusLabel; + } + + /** + * Returns the moneyLabel. + * + * @return the moneyLabel + */ + public Label getMoneyLabel() { + return moneyLabel; + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java b/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java index 8785d28..dd9857b 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java +++ b/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java @@ -2,7 +2,6 @@ import java.util.Locale; import java.util.function.Consumer; - import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -14,13 +13,12 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.util.Subscription; - import no.ntnu.gruppe53.controller.GameController; import no.ntnu.gruppe53.model.Exchange; +import no.ntnu.gruppe53.model.Stock; import no.ntnu.gruppe53.service.LanguageManager; import no.ntnu.gruppe53.service.LanguageOption; import no.ntnu.gruppe53.service.LanguageRegistry; -import no.ntnu.gruppe53.model.Stock; /* Yellow Color, Hex: #ffe556 RGB(255, 229, 86) @@ -28,208 +26,208 @@ Black Color, Hex: #303539 RGB(48, 53, 57) */ - /** * A {@link HBox} view representing the footer/bottom to be used in a {@link BorderPane} layout. + * *Uses a {@link LanguageManager} to change text to target language dynamically.
*/ public class FooterBar extends HBox { - - private final Button advanceWeekButton; - private Label currentWeekLabel; - private Label biggestLoserLabel; - private Label biggestLoserPriceLabel; - private Label biggestWinnerLabel; - private Label biggestWinnerPriceLabel; - private Subscription currentWeekSubscription; - private Subscription biggestLoserSubscription; - private Subscription biggestWinnerSubscription; - - private Exchange exchange; - private final LanguageManager lm = LanguageManager.getInstance(); - - private final ComboBoxConstructs a {@link ComboBox} to let the user switch language on the fly.
- *Sets the text of the UI elements dynamically.
- */ - public FooterBar() { - this.setSpacing(10); - this.setPadding(new Insets(15, 12, 15, 12)); - this.setStyle("-fx-background-color: #00bcf0;" + - "-fx-border-color: #303539 transparent transparent transparent;" + - "-fx-border-width: 5 0 0 0;"); - - languageBox = new ComboBox<>(); - languageBox.getItems().addAll(LanguageRegistry.getLanguages()); - - Locale activeLocale = lm.getLocale(); - LanguageOption activeOption = LanguageRegistry.getLanguages().stream() + private final Button advanceWeekButton; + private final Label currentWeekLabel; + private final Label biggestLoserLabel; + private final Label biggestLoserPriceLabel; + private final Label biggestWinnerLabel; + private final Label biggestWinnerPriceLabel; + private Subscription biggestLoserSubscription; + private Subscription biggestWinnerSubscription; + + private final LanguageManager lm = LanguageManager.getInstance(); + + private final ComboBoxConstructs a {@link ComboBox} to let the user switch language on the fly.
+ * + *Sets the text of the UI elements dynamically.
+ */ + public FooterBar() { + this.setSpacing(10); + this.setPadding(new Insets(15, 12, 15, 12)); + this.setStyle("-fx-background-color: #00bcf0;" + + "-fx-border-color: #303539 transparent transparent transparent;" + + "-fx-border-width: 5 0 0 0;"); + + languageBox = new ComboBox<>(); + languageBox.getItems().addAll(LanguageRegistry.getLanguages()); + + Locale activeLocale = lm.getLocale(); + LanguageOption activeOption = LanguageRegistry.getLanguages().stream() .filter(lang -> lang.locale().equals(activeLocale)) .findFirst() .orElse(LanguageRegistry.getDefaultLanguage()); - languageBox.setValue(activeOption); + languageBox.setValue(activeOption); - // Listens for changes in language from other sources (e.g. startView) - lm.getBundleProperty().subscribe(bundle -> { - java.util.Locale currentLocale = lm.getLocale(); + // Listens for changes in language from other sources (e.g. startView) + lm.getBundleProperty().subscribe(bundle -> { + java.util.Locale currentLocale = lm.getLocale(); - LanguageOption matchingOption = LanguageRegistry.getLanguages().stream() + LanguageOption matchingOption = LanguageRegistry.getLanguages().stream() .filter(lang -> lang.locale().equals(currentLocale)) .findFirst() .orElse(null); - if (matchingOption != null && !matchingOption.equals(languageBox.getValue())) { - languageBox.setValue(matchingOption); + if (matchingOption != null && !matchingOption.equals(languageBox.getValue())) { + languageBox.setValue(matchingOption); + } + }); + + languageBox.setCellFactory(list -> new ListCell<>() { + private Subscription cellSub; + @Override + protected void updateItem(LanguageOption item, boolean empty) { + super.updateItem(item, empty); + if (cellSub != null) { + cellSub.unsubscribe(); } - }); - - languageBox.setCellFactory(list -> new ListCell<>() { - private Subscription cellSub; - @Override - protected void updateItem(LanguageOption item, boolean empty) { - super.updateItem(item, empty); - if (cellSub != null) cellSub.unsubscribe(); - - if (empty || item == null) { - setText(null); - } else { - cellSub = lm.getBundleProperty().subscribe(bundle -> setText(item.name())); - } - } - }); - - languageBox.setButtonCell(languageBox.getCellFactory().call(null)); - - HBox loserBox = new HBox(); - - Label biggestLoserLabelText = new Label(); - biggestLoserLabelText.textProperty().bind(lm.bindString("biggestLoserLabelText")); - biggestLoserLabel = new Label(); - biggestLoserPriceLabel = new Label(); - biggestLoserPriceLabel.setStyle("-fx-text-fill: red;"); - - loserBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - loserBox.setPadding(new Insets(8, 14, 8, 14)); - loserBox.setAlignment(Pos.CENTER); - - loserBox.getChildren().addAll(biggestLoserLabelText, biggestLoserLabel, biggestLoserPriceLabel); - - HBox winnerBox = new HBox(); - - Label biggestWinnerLabelText = new Label(); - biggestWinnerLabelText.textProperty().bind(lm.bindString("biggestWinnerLabelText")); - biggestWinnerLabel = new Label(); - biggestWinnerPriceLabel = new Label(); - biggestWinnerPriceLabel.setStyle("-fx-text-fill: green;"); - winnerBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - winnerBox.setPadding(new Insets(8, 14, 8, 14)); - winnerBox.setAlignment(Pos.CENTER); - - winnerBox.getChildren().addAll(biggestWinnerLabelText, biggestWinnerLabel, biggestWinnerPriceLabel); - - - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - - HBox currentWeekBox = new HBox(); - currentWeekBox.setSpacing(10); - - currentWeekBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - currentWeekBox.setPadding(new Insets(8, 14, 8, 14)); - currentWeekBox.setAlignment(Pos.CENTER); - - Label currentWeekLabelText = new Label(); - currentWeekLabelText.textProperty().bind(lm.bindString("currentWeekLabelText")); - currentWeekLabel = new Label(); - currentWeekBox.getChildren().addAll(currentWeekLabelText, currentWeekLabel); - - advanceWeekButton = new Button(); - advanceWeekButton.textProperty().bind(lm.bindString("advanceWeekButton")); - advanceWeekButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - - this.getChildren().addAll(languageBox, loserBox, winnerBox, spacer, currentWeekBox, advanceWeekButton); - } - - /** - * Sets the exchange and subscribes to week updates. - * - * @param exchange the {@link Exchange} used in the game - */ - public void setExchange(Exchange exchange) { - this.exchange = exchange; - currentWeekSubscription = exchange.getCurrentWeekProperty().subscribe(newVal -> { - if (exchange != null) { - currentWeekLabel.setText(newVal.toString()); + if (empty || item == null) { + setText(null); } else { - currentWeekLabel.setText("None"); + cellSub = lm.getBundleProperty().subscribe(bundle -> setText(item.name())); } - }); - - if (biggestLoserSubscription != null) { - biggestLoserSubscription.unsubscribe(); - } - - biggestLoserSubscription = exchange.getCurrentWeekProperty().subscribe(newWeek -> { - Stock loser = exchange.getLosers(1).getFirst(); - biggestLoserLabel.setText(loser.getSymbol() + " - " + loser.getCompany()); - biggestLoserPriceLabel.setText(" $" + loser.getLatestPriceChange()); - }); - - if (biggestWinnerSubscription != null) { - biggestWinnerSubscription.unsubscribe(); - } - - biggestWinnerSubscription = exchange.getCurrentWeekProperty().subscribe(newWeek -> { - Stock winner = exchange.getGainers(1).getFirst(); - biggestWinnerLabel.setText(winner.getSymbol() + " - " + winner.getCompany()); - biggestWinnerPriceLabel.setText(" $" + winner.getLatestPriceChange()); - }); + } + }); + + languageBox.setButtonCell(languageBox.getCellFactory().call(null)); + + Label biggestLoserLabelText = new Label(); + biggestLoserLabelText.textProperty().bind(lm.bindString("biggestLoserLabelText")); + biggestLoserLabel = new Label(); + biggestLoserPriceLabel = new Label(); + biggestLoserPriceLabel.setStyle("-fx-text-fill: red;"); + + HBox loserBox = new HBox(); + loserBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + loserBox.setPadding(new Insets(8, 14, 8, 14)); + loserBox.setAlignment(Pos.CENTER); + + loserBox.getChildren().addAll(biggestLoserLabelText, biggestLoserLabel, biggestLoserPriceLabel); + + Label biggestWinnerLabelText = new Label(); + biggestWinnerLabelText.textProperty().bind(lm.bindString("biggestWinnerLabelText")); + biggestWinnerLabel = new Label(); + biggestWinnerPriceLabel = new Label(); + biggestWinnerPriceLabel.setStyle("-fx-text-fill: green;"); + + HBox winnerBox = new HBox(); + winnerBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + winnerBox.setPadding(new Insets(8, 14, 8, 14)); + winnerBox.setAlignment(Pos.CENTER); + + winnerBox.getChildren().addAll( + biggestWinnerLabelText, biggestWinnerLabel, biggestWinnerPriceLabel); + + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + HBox currentWeekBox = new HBox(); + currentWeekBox.setSpacing(10); + + currentWeekBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + currentWeekBox.setPadding(new Insets(8, 14, 8, 14)); + currentWeekBox.setAlignment(Pos.CENTER); + + Label currentWeekLabelText = new Label(); + currentWeekLabelText.textProperty().bind(lm.bindString("currentWeekLabelText")); + currentWeekLabel = new Label(); + currentWeekBox.getChildren().addAll(currentWeekLabelText, currentWeekLabel); + + advanceWeekButton = new Button(); + advanceWeekButton.textProperty().bind(lm.bindString("advanceWeekButton")); + advanceWeekButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + + this.getChildren().addAll( + languageBox, loserBox, winnerBox, spacer, currentWeekBox, advanceWeekButton); + } + + /** + * Sets the exchange and subscribes to week updates. + * + * @param exchange the {@link Exchange} used in the game + */ + public void setExchange(Exchange exchange) { + Subscription currentWeekSubscription = exchange.getCurrentWeekProperty().subscribe(newVal -> { + if (exchange != null) { + currentWeekLabel.setText(newVal.toString()); + } else { + currentWeekLabel.setText("None"); + } + }); + + if (biggestLoserSubscription != null) { + biggestLoserSubscription.unsubscribe(); } - /** - * Exposes the advanceWeekButton to {@link GameController} so it can - * advance the week of the {@link Exchange}. - * - * @param action the action to be performed when clicking the button - */ - public void onAdvanceButtonClick(Runnable action) { - advanceWeekButton.setOnAction(e -> action.run()); - } + biggestLoserSubscription = exchange.getCurrentWeekProperty().subscribe(newWeek -> { + Stock loser = exchange.getLosers(1).getFirst(); + biggestLoserLabel.setText(loser.getSymbol() + " - " + loser.getCompany()); + biggestLoserPriceLabel.setText(" $" + loser.getLatestPriceChange()); + }); - - /** - * Exoposes the LanguageOption ComboButton to the controller. - * - * @param action the action to be performed when selecting the language - */ - public void onLanguageChange(ConsumerExtends borderpane to allow easy organizing of the internal layout.
+ * *Uses a {@link LanguageManager} to set text to the targer language dynamically.
*/ public class MarketView extends BorderPane { - private final LanguageManager lm = LanguageManager.getInstance(); - - private Button purchaseButton; - private Label selectedStock; - - //Dynamic Labels/Text - public TextField gross; - public TextField commission; - public TextField total; - private TextField searchField; - - //StockListView - private ListViewSubscribes to a stock's sales price to update the value immediately.
+ * + *Subscribes to the current language to change text immediately.
+ * + *Uses a cell factory to format the string found in the stocksListView.
+ * + * @return the constructed MarketView + */ + private VBox centerPane() { + VBox vbox = new VBox(); + + vbox.setPadding(new Insets(15, 12, 15, 12)); + vbox.setSpacing(10); + + // Blue Background color + vbox.setStyle("-fx-background-color: #00bcf0;"); + + Label titleLabel = new Label(); + titleLabel.textProperty().bind(lm.bindString("marketTitle")); + titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; "); + + VBox titleBox = new VBox(titleLabel); + titleBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + titleBox.setAlignment(Pos.CENTER); + titleBox.setPadding(new Insets(4)); + titleBox.setMaxWidth(200); + + // Stock Graph + stockLineChartView.getChart().setMinHeight(0); + stockLineChartView.getChart().setMaxHeight(Double.MAX_VALUE); + stockLineChartView.getChart().setMinWidth(0); + stockLineChartView.getChart().setMaxWidth(Double.MAX_VALUE); + stockLineChartView.getChart().getXAxis().labelProperty().bind(lm.bindString("xAxisLabel")); + stockLineChartView.getChart().getYAxis().labelProperty().bind(lm.bindString("yAxisLabel")); + + // List of stocks + stocksListView = new ListView<>(); + stocksListView.setMinHeight(0); + stocksListView.setMaxHeight(Double.MAX_VALUE); + stocksListView.setMinWidth(0); + stocksListView.setMaxWidth(Double.MAX_VALUE); + stocksListView.setStyle("-fx-background-color: transparent;"); + + stocksListView.setCellFactory(list -> new ListCell<>() { + private Subscription cellSubscription; + private Subscription langSubscription; + + @Override + protected void updateItem(Stock stock, boolean empty) { + super.updateItem(stock, empty); + + if (cellSubscription != null) { + cellSubscription.unsubscribe(); + cellSubscription = null; + } + + if (langSubscription != null) { + langSubscription.unsubscribe(); + langSubscription = null; + } - /** - * Constructs the view and its components. - *Subscribes to a stock's sales price to update the value immediately.
- *Subscribes to the current language to change text immediately.
- *Uses a cell factory to format the string found in the stocksListView.
- * - * @return the constructed MarketView - */ - private VBox centerPane() { - VBox vBox = new VBox(); - - vBox.setPadding(new Insets(15, 12, 15, 12)); - vBox.setSpacing(10); - //Blue Background color - vBox.setStyle("-fx-background-color: #00bcf0;"); - - Label titleLabel = new Label(); - titleLabel.textProperty().bind(lm.bindString("marketTitle")); - titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; "); - - VBox titleBox = new VBox(titleLabel); - titleBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - titleBox.setAlignment(Pos.CENTER); - titleBox.setPadding(new Insets(4)); - titleBox.setMaxWidth(200); - - // Stock Graph - stockLineChartView.getChart().setMinHeight(0); - stockLineChartView.getChart().setMaxHeight(Double.MAX_VALUE); - stockLineChartView.getChart().setMinWidth(0); - stockLineChartView.getChart().setMaxWidth(Double.MAX_VALUE); - stockLineChartView.getChart().getXAxis().labelProperty().bind(lm.bindString("xAxisLabel")); - stockLineChartView.getChart().getYAxis().labelProperty().bind(lm.bindString("yAxisLabel")); - - // List of stocks - stocksListView = new ListView<>(); - stocksListView.setMinHeight(0); - stocksListView.setMaxHeight(Double.MAX_VALUE); - stocksListView.setMinWidth(0); - stocksListView.setMaxWidth(Double.MAX_VALUE); - stocksListView.setStyle("-fx-background-color: transparent;"); - - stocksListView.setCellFactory(list -> new ListCell<>() { - private Subscription cellSubscription; - private Subscription langSubscription; - - @Override - protected void updateItem(Stock stock, boolean empty) { - super.updateItem(stock, empty); - - if (cellSubscription != null) { - cellSubscription.unsubscribe(); - cellSubscription = null; - } - if (langSubscription != null) { - langSubscription.unsubscribe(); - langSubscription = null; - } - - if (empty || stock == null) { - setText(null); - } else { - Runnable updateTextAction = () -> { - String template = lm.getString("stockCellFormat"); - setText(String.format(template, + if (empty || stock == null) { + setText(null); + } else { + Runnable updateTextAction = () -> { + String template = lm.getString("stockCellFormat"); + setText(String.format(template, stock.getSymbol(), stock.getCompany(), FormatBigDecimal.formatNumber(stock.salesPriceProperty().get()), FormatBigDecimal.formatNumber(stock.getLatestPriceChange()), FormatBigDecimal.formatNumber(stock.getHighestPrice()), FormatBigDecimal.formatNumber(stock.getLowestPrice()) - )); - }; - - cellSubscription = stock.salesPriceProperty().subscribe(newPrice -> updateTextAction.run()); - langSubscription = lm.getBundleProperty().subscribe(bundle -> updateTextAction.run()); - - updateTextAction.run(); - } - } - }); - - HBox searchBox = new HBox(); - searchField = new TextField(); - searchField.promptTextProperty().bind(lm.bindString("search")); - searchBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - searchBox.setPadding(new Insets(8, 14, 8, 14)); - searchBox.setAlignment(Pos.CENTER_LEFT); - searchBox.setMaxWidth(Region.USE_PREF_SIZE); - searchBox.getChildren().addAll(searchField); - - - // Container for list of stocks - HBox stockListBox = new HBox(stocksListView); - stockListBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - // Container for graph of stocks - HBox stockChartBox = new HBox(stockLineChartView.getChart()); - stockChartBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - HBox stockBox = new HBox(stockListBox, stockChartBox); - stockBox.setSpacing(10); - - stockListBox.setPadding(new Insets(4)); - stockListBox.setMinHeight(0); - stockListBox.setMaxHeight(Double.MAX_VALUE); - stockListBox.setMinWidth(0); - stockListBox.setMaxWidth(Double.MAX_VALUE); - - VBox.setVgrow(stocksListView, Priority.ALWAYS); - HBox.setHgrow(stocksListView, Priority.ALWAYS); - VBox.setVgrow(stockLineChartView.getChart(), Priority.ALWAYS); - HBox.setHgrow(stockLineChartView.getChart(), Priority.ALWAYS); - VBox.setVgrow(stockListBox, Priority.ALWAYS); - HBox.setHgrow(stockListBox, Priority.ALWAYS); - VBox.setVgrow(stockChartBox, Priority.ALWAYS); - HBox.setHgrow(stockChartBox, Priority.ALWAYS); - VBox.setVgrow(stockBox, Priority.ALWAYS); - HBox.setHgrow(stockBox, Priority.ALWAYS); - - HBox selectedStockBox = new HBox(); - - Label selectedStockLabel = new Label(); - selectedStockLabel.textProperty().bind(lm.bindString("selectedStock")); - selectedStock = new Label(""); - - selectedStockBox.getChildren().addAll(selectedStockLabel, selectedStock); - - HBox quantityBox = new HBox(); + )); + }; - Label quantityLabel = new Label(); - quantityLabel.textProperty().bind(lm.bindString("purchaseQuantity")); - quantitySpinner = new Spinner<>(1, 1_000_000, 1); - quantitySpinner.setEditable(true); + cellSubscription = stock.salesPriceProperty() + .subscribe(newPrice -> updateTextAction.run()); + langSubscription = lm.getBundleProperty() + .subscribe(bundle -> updateTextAction.run()); - quantityBox.getChildren().addAll(quantityLabel, quantitySpinner); - - HBox calculationBox = new HBox(); - - Label grossLabel = new Label(); - grossLabel.textProperty().bind(lm.bindString("purchaseGross")); - gross = new TextField(); - gross.setEditable(false); - gross.setMaxWidth(200); - Label commissionLabel = new Label(); - commissionLabel.textProperty().bind(lm.bindString("purchaseCommission")); - commission = new TextField(); - commission.setEditable(false); - commission.setMaxWidth(200); - - calculationBox.getChildren().addAll(grossLabel, gross, commissionLabel, commission); - - HBox totalBox = new HBox(); - - Label totalLabel = new Label(); - totalLabel.textProperty().bind(lm.bindString("purchaseTotal")); - total = new TextField(""); - total.setEditable(false); - total.setMaxWidth(200); - - totalBox.getChildren().addAll(totalLabel, total); - - purchaseButton = new Button(); - purchaseButton.textProperty().bind(lm.bindString("purchaseButton")); - - VBox purchaseBox = new VBox(5); - - purchaseBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - purchaseBox.setPadding(new Insets(8, 14, 8, 14)); - purchaseBox.setAlignment(Pos.CENTER_LEFT); - - purchaseBox.getChildren().addAll(selectedStockBox, quantityBox, calculationBox, totalBox, purchaseButton); - - - - vBox.getChildren().addAll(titleBox, searchBox, + updateTextAction.run(); + } + } + }); + + HBox searchBox = new HBox(); + searchField = new TextField(); + searchField.promptTextProperty().bind(lm.bindString("search")); + searchBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + searchBox.setPadding(new Insets(8, 14, 8, 14)); + searchBox.setAlignment(Pos.CENTER_LEFT); + searchBox.setMaxWidth(Region.USE_PREF_SIZE); + searchBox.getChildren().addAll(searchField); + + + // Container for list of stocks + HBox stockListBox = new HBox(stocksListView); + stockListBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + // Container for graph of stocks + HBox stockChartBox = new HBox(stockLineChartView.getChart()); + stockChartBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + HBox stockBox = new HBox(stockListBox, stockChartBox); + stockBox.setSpacing(10); + + stockListBox.setPadding(new Insets(4)); + stockListBox.setMinHeight(0); + stockListBox.setMaxHeight(Double.MAX_VALUE); + stockListBox.setMinWidth(0); + stockListBox.setMaxWidth(Double.MAX_VALUE); + + VBox.setVgrow(stocksListView, Priority.ALWAYS); + HBox.setHgrow(stocksListView, Priority.ALWAYS); + VBox.setVgrow(stockLineChartView.getChart(), Priority.ALWAYS); + HBox.setHgrow(stockLineChartView.getChart(), Priority.ALWAYS); + VBox.setVgrow(stockListBox, Priority.ALWAYS); + HBox.setHgrow(stockListBox, Priority.ALWAYS); + VBox.setVgrow(stockChartBox, Priority.ALWAYS); + HBox.setHgrow(stockChartBox, Priority.ALWAYS); + VBox.setVgrow(stockBox, Priority.ALWAYS); + HBox.setHgrow(stockBox, Priority.ALWAYS); + + HBox selectedStockBox = new HBox(); + + Label selectedStockLabel = new Label(); + selectedStockLabel.textProperty().bind(lm.bindString("selectedStock")); + selectedStock = new Label(""); + + selectedStockBox.getChildren().addAll(selectedStockLabel, selectedStock); + + Label quantityLabel = new Label(); + quantityLabel.textProperty().bind(lm.bindString("purchaseQuantity")); + quantitySpinner = new Spinner<>(1, 1_000_000, 1); + quantitySpinner.setEditable(true); + + HBox quantityBox = new HBox(); + quantityBox.getChildren().addAll(quantityLabel, quantitySpinner); + + Label grossLabel = new Label(); + grossLabel.textProperty().bind(lm.bindString("purchaseGross")); + gross = new TextField(); + gross.setEditable(false); + gross.setMaxWidth(200); + Label commissionLabel = new Label(); + commissionLabel.textProperty().bind(lm.bindString("purchaseCommission")); + commission = new TextField(); + commission.setEditable(false); + commission.setMaxWidth(200); + + HBox calculationBox = new HBox(); + calculationBox.getChildren().addAll(grossLabel, gross, commissionLabel, commission); + + Label totalLabel = new Label(); + totalLabel.textProperty().bind(lm.bindString("purchaseTotal")); + total = new TextField(""); + total.setEditable(false); + total.setMaxWidth(200); + + HBox totalBox = new HBox(); + totalBox.getChildren().addAll(totalLabel, total); + + purchaseButton = new Button(); + purchaseButton.textProperty().bind(lm.bindString("purchaseButton")); + + VBox purchaseBox = new VBox(5); + + purchaseBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + purchaseBox.setPadding(new Insets(8, 14, 8, 14)); + purchaseBox.setAlignment(Pos.CENTER_LEFT); + + purchaseBox.getChildren().addAll( + selectedStockBox, quantityBox, calculationBox, totalBox, purchaseButton); + + + + vbox.getChildren().addAll(titleBox, searchBox, stockBox, purchaseBox); - return vBox; - } - - /** - * Sets and updates subscription to the selected stock's sales price. - * @param stock the selected stock - * @param priceChangeAction the action to run on price change (updates the sales price) - */ - public void setupPriceSubscription(Stock stock, ConsumerUses first a {@link FilteredList} to account for what the user has searched for.
- *Then utilises a {@link SortedList} to sort the stocks in the exchange alphabetically - * on their symbol/ticker.
- * - * @param exchange the exchange to be used for the view - */ - public void setExchange(Exchange exchange) { - this.exchange = exchange; - - - if (exchange != null) { - filteredStocksList = new FilteredList<>(exchange.getObservableStocks()); - - SortedListUses first a {@link FilteredList} to account for what the user has searched for.
+ * + *Then utilises a {@link SortedList} to sort the stocks in the exchange alphabetically + * on their symbol/ticker.
+ * + * @param exchange the exchange to be used for the view + */ + public void setExchange(Exchange exchange) { + if (exchange != null) { + filteredStocksList = new FilteredList<>(exchange.getObservableStocks()); + + SortedListUses a {@link Subscription} to observe the calculations made by the current - * Stock and Quantity selected.
- * - * @param purchaseCalculator the purchaseCalculator to be used for the view - */ - public void setPurchaseCalculator(PurchaseCalculator purchaseCalculator) { - this.purchaseCalculator = purchaseCalculator; - grossSubscription = purchaseCalculator.getGrossProperty().subscribe(newVal -> { - if (newVal != null) { - gross.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { - gross.setText(""); - } - }); - commissionSubscription = purchaseCalculator.getCommissionProperty().subscribe(newVal -> { - if (newVal != null) { + } + + /** + * Sets the PurchaseCalculator to use for the view. + * + *Uses a {@link Subscription} to observe the calculations made by the current + * Stock and Quantity selected.
+ * + * @param purchaseCalculator the purchaseCalculator to be used for the view + */ + public void setPurchaseCalculator(PurchaseCalculator purchaseCalculator) { + Subscription grossSubscription = purchaseCalculator.getGrossProperty().subscribe(newVal -> { + if (newVal != null) { + gross.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); + } else { + gross.setText(""); + } + }); + + Subscription commissionSubscription = + purchaseCalculator.getCommissionProperty().subscribe(newVal -> { + if (newVal != null) { commission.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { + } else { commission.setText(""); - } - }); - totalSubscription = purchaseCalculator.getTotalProperty().subscribe(newVal -> { - if (newVal != null) { - total.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { - total.setText(""); - } - }); - } - - /** - * Returns the selected stock in the stocksListView. - * - * @return the selected stock in the stocksListView - */ - public Stock getSelectedStock() { - return stocksListView.getSelectionModel().getSelectedItem(); - } - - /** - * Returns the numeric value of the quantitySpinner for purchasing {@link Share}s in a stock. - * - * @return an integer of the amount of shares to buy - */ - public int getQuantityPurchase() { - return quantitySpinner.getValue(); - } - - /** - * Sets the company name of the selected stock in the stocksListView. - * - * @param selectedStockString the company name of the selected stock - */ - public void setSelectedStockLabel(String selectedStockString) { - selectedStock.setText(selectedStockString); - } - + } + }); + Subscription totalSubscription = purchaseCalculator.getTotalProperty().subscribe(newVal -> { + if (newVal != null) { + total.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); + } else { + total.setText(""); + } + }); + } + + /** + * Returns the selected stock in the stocksListView. + * + * @return the selected stock in the stocksListView + */ + public Stock getSelectedStock() { + return stocksListView.getSelectionModel().getSelectedItem(); + } + + /** + * Returns the numeric value of the quantitySpinner for purchasing {@link Share}s in a stock. + * + * @return an integer of the amount of shares to buy + */ + public int getQuantityPurchase() { + return quantitySpinner.getValue(); + } + + /** + * Sets the company name of the selected stock in the stocksListView. + * + * @param selectedStockString the company name of the selected stock + */ + public void setSelectedStockLabel(String selectedStockString) { + selectedStock.setText(selectedStockString); + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java b/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java index 2360452..838e521 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java +++ b/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java @@ -1,5 +1,6 @@ package no.ntnu.gruppe53.view; +import java.math.RoundingMode; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -12,235 +13,228 @@ import no.ntnu.gruppe53.model.Player; import no.ntnu.gruppe53.service.LanguageManager; -import java.math.RoundingMode; - - /* Yellow Color, Hex: #ffe556 RGB(255, 229, 86) Blue Color, Hex: #00bcf0 RGB(0, 188, 240) Black Color, Hex: #303539 RGB(48, 53, 57) */ - /** * Represents the top bar of the views. + * *Provides the user with the ability to switch between views on the fly by * clicking on the buttons representing the desired view.
+ * *The {@link no.ntnu.gruppe53.controller.GameController} handles * the actions for the buttons.
+ * *Uses a {@link LanguageManager} to set text of UI elements dynamically.
*/ public class NavigationBar extends HBox { - private final LanguageManager lm = LanguageManager.getInstance(); - - private final Button marketButton; - private final Button portfolioButton; - private final Button historyButton; - private final Button endGameButton; - private Subscription netWorthSubscription; - private Subscription moneySubscription; - private Subscription currentWeekSubscription; - - - - private Player player; - private Exchange exchange; - - //Navigation Buttons - - - //Dynamic Labels - private Label playerLabel; - private Label statusLabel; - private Label netWorthLabel; - private Label moneyLabel; - - /** - * Constructs the UI elements of the navigationBar. - *Binds the language manager to the textProperty of the UI elements to dynamically change the text.
- */ - public NavigationBar() { - - this.setPadding(new Insets(15, 12, 15, 12)); - this.setSpacing(10); - //Blue Background color - this.setStyle("-fx-background-color: #00bcf0;" + - "-fx-border-color: transparent transparent #303539 transparent;" + - "-fx-border-width: 0 0 5 0;"); - - - marketButton = new Button(); - marketButton.textProperty().bind(lm.bindString("marketButton")); - marketButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - portfolioButton = new Button(); - portfolioButton.textProperty().bind(lm.bindString("portfolioButton")); - portfolioButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - historyButton = new Button(); - historyButton.textProperty().bind(lm.bindString("historyButton")); - historyButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - - - - HBox playerBox = new HBox(); - - Label playerLabelText = new Label(); - playerLabelText.textProperty().bind(lm.bindString("navPlayerLabelText")); - playerLabel = new Label("Player"); - - playerBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - playerBox.setPadding(new Insets(8, 14, 8, 14)); - playerBox.setAlignment(Pos.CENTER); - - playerBox.getChildren().addAll(playerLabelText, playerLabel); - - HBox statusBox = new HBox(); - - Label statusLabelText = new Label(); - statusLabelText.textProperty().bind(lm.bindString("statusLabelText")); - statusLabel = new Label(); - - statusBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - statusBox.setPadding(new Insets(8, 14, 8, 14)); - statusBox.setAlignment(Pos.CENTER); - - statusBox.getChildren().addAll(statusLabelText, statusLabel); - - HBox moneyBox = new HBox(); - - Label moneyLabelText = new Label(); - moneyLabelText.textProperty().bind(lm.bindString("navMoneyLabelText")); - moneyLabel = new Label(); - - moneyBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - moneyBox.setPadding(new Insets(8, 14, 8, 14)); - moneyBox.setAlignment(Pos.CENTER); - moneyBox.getChildren().addAll(moneyLabelText, moneyLabel); - - HBox networthBox = new HBox(); - - Label networthLabelText = new Label(); - networthLabelText.textProperty().bind(lm.bindString("navNetWorthLabelText")); - netWorthLabel = new Label(); - - networthBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - networthBox.setPadding(new Insets(8, 14, 8, 14)); - networthBox.setAlignment(Pos.CENTER); - networthBox.getChildren().addAll(networthLabelText, netWorthLabel); - - endGameButton = new Button(); - endGameButton.textProperty().bind(lm.bindString("endGameButton")); - endGameButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - - this.getChildren().addAll(marketButton, portfolioButton, historyButton, spacer, playerBox, statusBox, moneyBox, networthBox, endGameButton); - } - /** - * Exposes the marketButton to the GameController. - * - * @param action the action to be run when clicking the button - */ - public void onMarketButtonClick(Runnable action) { - marketButton.setOnAction(e -> action.run()); + private final Button marketButton; + private final Button portfolioButton; + private final Button historyButton; + private final Button endGameButton; + private Subscription netWorthSubscription; + + private Player player; + + // Dynamic Labels + private final Label playerLabel; + private final Label statusLabel; + private final Label netWorthLabel; + private final Label moneyLabel; + + /** + * Constructs the UI elements of the navigationBar. + * + *Binds the language manager to the textProperty of the + * UI elements to dynamically change the text.
+ */ + public NavigationBar() { + this.setPadding(new Insets(15, 12, 15, 12)); + this.setSpacing(10); + // Blue Background color + this.setStyle("-fx-background-color: #00bcf0;" + + "-fx-border-color: transparent transparent #303539 transparent;" + + "-fx-border-width: 0 0 5 0;"); + + marketButton = new Button(); + LanguageManager lm = LanguageManager.getInstance(); + marketButton.textProperty().bind(lm.bindString("marketButton")); + marketButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + portfolioButton = new Button(); + portfolioButton.textProperty().bind(lm.bindString("portfolioButton")); + portfolioButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + historyButton = new Button(); + historyButton.textProperty().bind(lm.bindString("historyButton")); + historyButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + HBox playerBox = new HBox(); + + Label playerLabelText = new Label(); + playerLabelText.textProperty().bind(lm.bindString("navPlayerLabelText")); + playerLabel = new Label("Player"); + + playerBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + playerBox.setPadding(new Insets(8, 14, 8, 14)); + playerBox.setAlignment(Pos.CENTER); + + playerBox.getChildren().addAll(playerLabelText, playerLabel); + + HBox statusBox = new HBox(); + + Label statusLabelText = new Label(); + statusLabelText.textProperty().bind(lm.bindString("statusLabelText")); + statusLabel = new Label(); + + statusBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + statusBox.setPadding(new Insets(8, 14, 8, 14)); + statusBox.setAlignment(Pos.CENTER); + + statusBox.getChildren().addAll(statusLabelText, statusLabel); + + HBox moneyBox = new HBox(); + + Label moneyLabelText = new Label(); + moneyLabelText.textProperty().bind(lm.bindString("navMoneyLabelText")); + moneyLabel = new Label(); + + moneyBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + moneyBox.setPadding(new Insets(8, 14, 8, 14)); + moneyBox.setAlignment(Pos.CENTER); + moneyBox.getChildren().addAll(moneyLabelText, moneyLabel); + + HBox networthBox = new HBox(); + + Label networthLabelText = new Label(); + networthLabelText.textProperty().bind(lm.bindString("navNetWorthLabelText")); + netWorthLabel = new Label(); + + networthBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + networthBox.setPadding(new Insets(8, 14, 8, 14)); + networthBox.setAlignment(Pos.CENTER); + networthBox.getChildren().addAll(networthLabelText, netWorthLabel); + + endGameButton = new Button(); + endGameButton.textProperty().bind(lm.bindString("endGameButton")); + endGameButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + + this.getChildren().addAll( + marketButton, portfolioButton, historyButton, + spacer, playerBox, statusBox, moneyBox, networthBox, endGameButton); + } + + /** + * Exposes the marketButton to the GameController. + * + * @param action the action to be run when clicking the button + */ + public void onMarketButtonClick(Runnable action) { + marketButton.setOnAction(e -> action.run()); + } + + /** + * Exposes the portfolioButton to the GameController. + * + * @param action the action to be run when clicking the button + */ + public void onPortfolioButtonClick(Runnable action) { + portfolioButton.setOnAction(e -> action.run()); + } + + /** + * Exposes the historyButton to the GameController. + * + * @param action the action to be run when clicking the button + */ + public void onHistoryButtonClick(Runnable action) { + historyButton.setOnAction(e -> action.run()); + } + + /** + * Exposes the endGameButton to the GameController. + * + * @param action the action to be run when clicking the button + */ + public void onEndGameButtonClick(Runnable action) { + endGameButton.setOnAction(e -> action.run()); + } + + /** + * Binds a subscription/observer to a {@link Player}. + * + *It specifically listens to the players money, net worth and name.
+ * + * @param player the player object to be observed + */ + public void bindPlayer(Player player) { + this.player = player; + + if (netWorthSubscription != null) { + netWorthSubscription.unsubscribe(); } - /** - * Exposes the portfolioButton to the GameController. - * - * @param action the action to be run when clicking the button - */ - public void onPortfolioButtonClick(Runnable action) { - portfolioButton.setOnAction(e -> action.run()); - } + if (player != null) { + playerLabel.setText(player.getName()); + netWorthSubscription = player.getNetWorthProperty().subscribe(newVal -> { + + if (newVal != null) { + netWorthLabel.setText("$" + newVal.setScale(2, RoundingMode.HALF_EVEN)); + } else { + netWorthLabel.setText("$0.00"); + } + }); + + Subscription moneySubscription = player.getMoneyProperty().subscribe(newVal -> { + if (newVal != null) { + moneyLabel.setText("$" + newVal.setScale(2, RoundingMode.HALF_EVEN)); + } else { + moneyLabel.setText("$0.00"); + } + }); - /** - * Exposes the historyButton to the GameController. - * - * @param action the action to be run when clicking the button - */ - public void onHistoryButtonClick(Runnable action) { - historyButton.setOnAction(e -> action.run()); } - - /** - * Exposes the endGameButton to the GameController. - * - * @param action the action to be run when clicking the button - */ - public void onEndGameButtonClick(Runnable action) { - endGameButton.setOnAction(e -> action.run()); - } - - - /** - * Binds a subscription/observer to a {@link Player}. - *It specifically listens to the players money, net worth and name.
- * - * @param player the player object to be observed - */ - public void bindPlayer(Player player) { - this.player = player; - - if (netWorthSubscription != null) { - netWorthSubscription.unsubscribe(); - } - - if (player != null) { - playerLabel.setText(player.getName()); - netWorthSubscription = player.getNetWorthProperty().subscribe(newVal -> { - if (newVal != null) { - netWorthLabel.setText("$" + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { - netWorthLabel.setText("$0.00"); - } - }); - moneySubscription = player.getMoneyProperty().subscribe(newVal -> { - if (newVal != null) { - moneyLabel.setText("$" + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { - moneyLabel.setText("$0.00"); - } - }); - - } - } - - /** - * Binds a subscription/observer to a {@link Exchange}. - *It specifically listens to week changes so that it can update the player status.
- * - * @param exchange the exchange object to be observed - */ - public void bindExchange(Exchange exchange) { - this.exchange = exchange; - - currentWeekSubscription = exchange.getCurrentWeekProperty().subscribe(newVal -> { - statusLabel.setText(player.getStatus(exchange)); - }); - } - - + } + + /** + * Binds a subscription/observer to a {@link Exchange}. + * + *It specifically listens to week changes so that it can update the player status.
+ * + * @param exchange the exchange object to be observed + */ + public void bindExchange(Exchange exchange) { + Subscription currentWeekSubscription = exchange.getCurrentWeekProperty().subscribe(newVal -> { + statusLabel.setText(player.getStatus(exchange)); + }); + } } diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java b/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java index 0711ae2..046fcb1 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java +++ b/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java @@ -1,15 +1,19 @@ package no.ntnu.gruppe53.view; -import no.ntnu.gruppe53.model.Player; - +import java.util.Optional; import javafx.geometry.Insets; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Dialog; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.control.TextFormatter; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; +import no.ntnu.gruppe53.model.Player; import no.ntnu.gruppe53.service.LanguageManager; -import java.util.Optional; - /* Yellow Color, Hex: #ffe556 RGB(255, 229, 86) Blue Color, Hex: #00bcf0 RGB(0, 188, 240) @@ -17,93 +21,94 @@ */ /** - * Represents a prompt for the {@link Player}'s name + * Represents a prompt for the {@link Player}'s name. */ public class PlayerNameChooserView { - private final LanguageManager lm = LanguageManager.getInstance(); - DialogA {@link TextFormatter} is used to set the maximum amount - * of characters for the player name.
- *Uses a {@link LanguageManager} to determine text language.
- * - * @return the dialog for player name input - */ - public OptionalA {@link TextFormatter} is used to set the maximum amount + * of characters for the player name.
+ * + *Uses a {@link LanguageManager} to determine text language.
+ * + * @return the dialog for player name input + */ + public OptionalUses a {@link LanguageManager} to set text to target language.
*/ public class PlayerStartingMoneyChooserView { - DialogUses a {@link TextFormatter} and listener for value integrity.
- *Uses a {@link SpinnerValueFactory} to set min, max, default, - * and step size values for the spinner.
- * @return the dialog for player's starting money - */ - public OptionalUses a {@link TextFormatter} and listener for value integrity.
+ * + *Uses a {@link SpinnerValueFactory} to set min, max, default, + * and step size values for the spinner.
+ * + * @return the dialog for player's starting money + */ + public OptionalLets the player view their current {@link Share} assets, see their * current sale value, whether the value has gone up or down since they purchased, * and the ability to sell the share for money.
+ * *Extends the {@link BorderPane}
for easy layout setup. + * *Events are handled by the {@link no.ntnu.gruppe53.controller.GameController}.
+ * *Uses a {@link LanguageManager} to dynamically set text based on target language.
*/ public class PortfolioView extends BorderPane { - private final LanguageManager lm = LanguageManager.getInstance(); - - private Button sellButton; - - //Static Labels - private Label titleLabel; - - //Dynamic Labels - private Label selectedShare; - private TextField gross; - private TextField commission; - private TextField tax; - private TextField total; - private TextField profit; - private TextField searchField; - - //PortfolioListView - private ListViewSubscribes to shares for sales price updates.
- *Uses a cell factory to format the displayed string representing each share. - * The sell factory has the text dynamically set by the language manager.
- * - * @return a VBox of the portfolio view - */ - private VBox centerPane() { - VBox vBox = new VBox(); - vBox.setPadding(new Insets(15, 12, 15, 12)); - vBox.setSpacing(10); - //Blue Background color - vBox.setStyle("-fx-background-color: #00bcf0; "); - - titleLabel = new Label(); - titleLabel.textProperty().bind(lm.bindString("portfolioTitle")); - titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #303539;"); - - VBox titleBox = new VBox(titleLabel); - titleBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - titleBox.setAlignment(Pos.CENTER); - titleBox.setPadding(new Insets(4)); - titleBox.setMaxWidth(200); - - portfolioListView = new ListView<>(); - portfolioListView.setMinHeight(0); - portfolioListView.setMaxHeight(Double.MAX_VALUE); - - portfolioListView.setCellFactory(list -> new ListCell<>() { - private Subscription cellSubscription; - private Subscription langSubscription; - - @Override - protected void updateItem(Share item, boolean empty) { - super.updateItem(item, empty); - - if (cellSubscription != null) { - cellSubscription.unsubscribe(); - cellSubscription = null; - } - if (langSubscription != null) { - langSubscription.unsubscribe(); - langSubscription = null; - } - - if (empty || item == null || item.getStock() == null) { - setText(null); - setGraphic(null); - } else { - setText(null); - - - Runnable updateGraphicAction = () -> { - BigDecimal currentPrice = item.getStock().salesPriceProperty().get(); - setGraphic(createColoredShareText(item, currentPrice)); - }; - - cellSubscription = item.getStock().salesPriceProperty().subscribe(currentPrice -> { - updateGraphicAction.run(); - }); - - langSubscription = lm.getBundleProperty().subscribe(bundle -> { - updateGraphicAction.run(); - }); - - updateGraphicAction.run(); - } + private final LanguageManager lm = LanguageManager.getInstance(); + + private Button sellButton; + + // Dynamic Labels + private Label selectedShare; + private TextField gross; + private TextField commission; + private TextField tax; + private TextField total; + private TextField profit; + private TextField searchField; + + // PortfolioListView + private ListViewSubscribes to shares for sales price updates.
+ * + *Uses a cell factory to format the displayed string representing each share. + * The sell factory has the text dynamically set by the language manager.
+ * + * @return a VBox of the portfolio view + */ + private VBox centerPane() { + VBox vbox = new VBox(); + vbox.setPadding(new Insets(15, 12, 15, 12)); + vbox.setSpacing(10); + // Blue Background color + vbox.setStyle("-fx-background-color: #00bcf0; "); + + Label titleLabel = new Label(); + titleLabel.textProperty().bind(lm.bindString("portfolioTitle")); + titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #303539;"); + + VBox titleBox = new VBox(titleLabel); + titleBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + titleBox.setAlignment(Pos.CENTER); + titleBox.setPadding(new Insets(4)); + titleBox.setMaxWidth(200); + + portfolioListView = new ListView<>(); + portfolioListView.setMinHeight(0); + portfolioListView.setMaxHeight(Double.MAX_VALUE); + + portfolioListView.setCellFactory(list -> new ListCell<>() { + private Subscription cellSubscription; + private Subscription langSubscription; + + @Override + protected void updateItem(Share item, boolean empty) { + super.updateItem(item, empty); + + if (cellSubscription != null) { + cellSubscription.unsubscribe(); + cellSubscription = null; } - }); - - HBox searchBox = new HBox(); - searchField = new TextField(); - searchField.promptTextProperty().bind(lm.bindString("search")); - searchBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - - searchBox.setPadding(new Insets(8, 14, 8, 14)); - searchBox.setAlignment(Pos.CENTER_LEFT); - searchBox.setMaxWidth(Region.USE_PREF_SIZE); - searchBox.getChildren().addAll(searchField); - - VBox portfolioListBox = new VBox(portfolioListView); - portfolioListBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - portfolioListBox.setPadding(new Insets(4)); - portfolioListBox.setMinHeight(0); - portfolioListBox.setMaxHeight(Double.MAX_VALUE); - - // Ensures the portfoliolist grows to fit the available screenspace - VBox.setVgrow(portfolioListView, Priority.ALWAYS); - VBox.setVgrow(portfolioListBox, Priority.ALWAYS); - - - HBox selectedShareBox = new HBox(); - - Label selectedShareLabel = new Label(); - selectedShareLabel.textProperty().bind(lm.bindString("selectedShare")); - selectedShare = new Label(); - - selectedShareBox.getChildren().addAll(selectedShareLabel, selectedShare); - - HBox calculationBox = new HBox(); - Label grossLabel = new Label(); - grossLabel.textProperty().bind(lm.bindString("portfolioGross")); - gross = new TextField(); - gross.setEditable(false); - gross.setMaxWidth(200); - Label commissionLabel = new Label(); - commissionLabel.textProperty().bind(lm.bindString("portfolioCommission")); - commission = new TextField(); - commission.setEditable(false); - commission.setMaxWidth(200); - Label taxLabel = new Label(); - taxLabel.textProperty().bind(lm.bindString("portfolioTax")); - tax = new TextField(); - tax.setEditable(false); - tax.setMaxWidth(200); - - calculationBox.getChildren().addAll(grossLabel, gross, commissionLabel, commission, taxLabel, tax); - - - - HBox totalBox = new HBox(); - - Label totalLabel = new Label(); - totalLabel.textProperty().bind(lm.bindString("portfolioTotal")); - total = new TextField(); - total.setEditable(false); - total.setMaxWidth(200); - - Label profitLabel = new Label(); - profitLabel.textProperty().bind(lm.bindString("portfolioProfit")); - profit = new TextField(); - profit.setEditable(false); - profit.setMaxWidth(200); - - totalBox.getChildren().addAll(totalLabel, total, profitLabel, profit); - - sellButton = new Button(); - sellButton.textProperty().bind(lm.bindString("sellButton")); + if (langSubscription != null) { + langSubscription.unsubscribe(); + langSubscription = null; + } - VBox saleBox = new VBox(5); + if (empty || item == null || item.getStock() == null) { + setText(null); + setGraphic(null); + } else { + setText(null); - saleBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - saleBox.setPadding(new Insets(8, 14, 8, 14)); - saleBox.setAlignment(Pos.CENTER_LEFT); + Runnable updateGraphicAction = () -> { + BigDecimal currentPrice = item.getStock().salesPriceProperty().get(); + setGraphic(createColoredShareText(item, currentPrice)); + }; - saleBox.getChildren().addAll(selectedShareBox, calculationBox, totalBox, sellButton); + cellSubscription = item.getStock().salesPriceProperty().subscribe(currentPrice -> { + updateGraphicAction.run(); + }); + langSubscription = lm.getBundleProperty().subscribe(bundle -> { + updateGraphicAction.run(); + }); - vBox.getChildren().addAll(titleBox, searchBox, + updateGraphicAction.run(); + } + } + }); + + HBox searchBox = new HBox(); + searchField = new TextField(); + searchField.promptTextProperty().bind(lm.bindString("search")); + searchBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + searchBox.setPadding(new Insets(8, 14, 8, 14)); + searchBox.setAlignment(Pos.CENTER_LEFT); + searchBox.setMaxWidth(Region.USE_PREF_SIZE); + searchBox.getChildren().addAll(searchField); + + VBox portfolioListBox = new VBox(portfolioListView); + portfolioListBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + portfolioListBox.setPadding(new Insets(4)); + portfolioListBox.setMinHeight(0); + portfolioListBox.setMaxHeight(Double.MAX_VALUE); + + // Ensures the portfoliolist grows to fit the available screenspace + VBox.setVgrow(portfolioListView, Priority.ALWAYS); + VBox.setVgrow(portfolioListBox, Priority.ALWAYS); + + HBox selectedShareBox = new HBox(); + + Label selectedShareLabel = new Label(); + selectedShareLabel.textProperty().bind(lm.bindString("selectedShare")); + selectedShare = new Label(); + + selectedShareBox.getChildren().addAll(selectedShareLabel, selectedShare); + + Label grossLabel = new Label(); + grossLabel.textProperty().bind(lm.bindString("portfolioGross")); + gross = new TextField(); + gross.setEditable(false); + gross.setMaxWidth(200); + Label commissionLabel = new Label(); + commissionLabel.textProperty().bind(lm.bindString("portfolioCommission")); + commission = new TextField(); + commission.setEditable(false); + commission.setMaxWidth(200); + Label taxLabel = new Label(); + taxLabel.textProperty().bind(lm.bindString("portfolioTax")); + tax = new TextField(); + tax.setEditable(false); + tax.setMaxWidth(200); + + HBox calculationBox = new HBox(); + calculationBox.getChildren().addAll( + grossLabel, gross, commissionLabel, commission, taxLabel, tax); + + Label totalLabel = new Label(); + totalLabel.textProperty().bind(lm.bindString("portfolioTotal")); + total = new TextField(); + total.setEditable(false); + total.setMaxWidth(200); + + Label profitLabel = new Label(); + profitLabel.textProperty().bind(lm.bindString("portfolioProfit")); + profit = new TextField(); + profit.setEditable(false); + profit.setMaxWidth(200); + + HBox totalBox = new HBox(); + totalBox.getChildren().addAll(totalLabel, total, profitLabel, profit); + + sellButton = new Button(); + sellButton.textProperty().bind(lm.bindString("sellButton")); + + VBox saleBox = new VBox(5); + + saleBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + saleBox.setPadding(new Insets(8, 14, 8, 14)); + saleBox.setAlignment(Pos.CENTER_LEFT); + + saleBox.getChildren().addAll(selectedShareBox, calculationBox, totalBox, sellButton); + + + vbox.getChildren().addAll(titleBox, searchBox, portfolioListBox, saleBox); - return vBox; - } - - /** - * Exposes the sell button to the GameController. - * @param action the action to be run when pressing the sell button - */ - public void onSellButton(Runnable action) { - sellButton.setOnAction(e -> action.run()); - } - - /** - * Sets the action to be run on selecting a share in portfolioListView. - * @param action the action to run when selecting a share - */ - public void onShareSelection(ConsumerThe values observed are the players portfolio and name.
- *Uses a {@link FilteredList} to get and filter the shares and - * binds it to the portfolioListView.
- * - * @param player the player to be observed - */ - public void setPlayer(Player player) { - this.player = player; - - if (player != null && player.getPortfolio() != null) { - - filteredPortfolioList = new FilteredList<>(player.getPortfolio().getShares()); - - portfolioListView.setItems(filteredPortfolioList); - - searchField.textProperty().addListener((observable, oldText, newText) -> { - String search = newText == null ? "" : newText.toLowerCase(); - - filteredPortfolioList.setPredicate(share -> search.isBlank() + return vbox; + } + + /** + * Exposes the sell button to the GameController. + * + * @param action the action to be run when pressing the sell button + */ + public void onSellButton(Runnable action) { + sellButton.setOnAction(e -> action.run()); + } + + /** + * Sets the action to be run on selecting a share in portfolioListView. + * + * @param action the action to run when selecting a share + */ + public void onShareSelection(ConsumerThe values observed are the players portfolio and name.
+ * + *Uses a {@link FilteredList} to get and filter the shares and + * binds it to the portfolioListView.
+ * + * @param player the player to be observed + */ + public void setPlayer(Player player) { + if (player != null && player.getPortfolio() != null) { + filteredPortfolioList = new FilteredList<>(player.getPortfolio().getShares()); + + portfolioListView.setItems(filteredPortfolioList); + + searchField.textProperty().addListener((observable, oldText, newText) -> { + String search = newText == null ? "" : newText.toLowerCase(); + + filteredPortfolioList.setPredicate(share -> search.isBlank() || share.getStock().getSymbol().toLowerCase().contains(search) || share.getStock().getCompany().toLowerCase().contains(search) - ); - }); - - } else { + ); + }); - portfolioListView.setItems(null); - } - } + } else { - /** - * Sets the share name of the selected share in the portfolioListView. - * - * @param selectedShareString the share name of the selected share - */ - public void setSelectedShareLabel(String selectedShareString) { - selectedShare.setText(selectedShareString); + portfolioListView.setItems(null); } - - /** - * Sets the SaleCalculator to use for the view. - *Uses a {@link Subscription} to observe the calculations made by the current - * Share selected.
- * - * @param saleCalculator the SaleCalculator to be used for the view - */ - public void setSaleCalculator(SaleCalculator saleCalculator) { - this.saleCalculator = saleCalculator; - grossSubscription = saleCalculator.getGrossProperty().subscribe(newVal -> { - if (newVal != null) { + } + + /** + * Sets the share name of the selected share in the portfolioListView. + * + * @param selectedShareString the share name of the selected share + */ + public void setSelectedShareLabel(String selectedShareString) { + selectedShare.setText(selectedShareString); + } + + /** + * Sets the SaleCalculator to use for the view. + * + *Uses a {@link Subscription} to observe the calculations made by the current + * Share selected.
+ * + * @param saleCalculator the SaleCalculator to be used for the view + */ + public void setSaleCalculator(SaleCalculator saleCalculator) { + Subscription grossSubscription = + saleCalculator.getGrossProperty().subscribe(newVal -> { + if (newVal != null) { gross.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { + } else { gross.setText(""); - } - }); - commissionSubscription = saleCalculator.getCommissionProperty().subscribe(newVal -> { - if (newVal != null) { + } + }); + + Subscription commissionSubscription = + saleCalculator.getCommissionProperty().subscribe(newVal -> { + if (newVal != null) { commission.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { + } else { commission.setText(""); - } - }); - taxSubscription = saleCalculator.getTaxProperty().subscribe(newVal -> { - if (newVal != null) { - tax.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN).abs()); - } else { - tax.setText(""); - } - }); - totalSubscription = saleCalculator.getTotalProperty().subscribe(newVal -> { - if (newVal != null) { - total.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { - total.setText(""); - } - }); - profitSubscription = saleCalculator.getProfitProperty().subscribe(newVal -> { - if (newVal != null) { - profit.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); - } else { - profit.setText(""); - } - }); - } - + } + }); - /** - * Formats the text representation of the share in the portfolioListView - * and applies a color to it based on its current sales price compared - * to the buy price of the share. - * - * @param share the share to be formatted - * @param currentPrice the cusort the stocks in the exchange alphabetically - * on their symbol/tickerrrent price of the share - * @return a {@link TextFlow} representation of the share - */ - private TextFlow createColoredShareText(Share share, BigDecimal currentPrice) { - String text = share.getStock().getSymbol() + Subscription taxSubscription = saleCalculator.getTaxProperty().subscribe(newVal -> { + if (newVal != null) { + tax.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN).abs()); + } else { + tax.setText(""); + } + }); + + Subscription totalSubscription = saleCalculator.getTotalProperty().subscribe(newVal -> { + if (newVal != null) { + total.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); + } else { + total.setText(""); + } + }); + + Subscription profitSubscription = saleCalculator.getProfitProperty().subscribe(newVal -> { + if (newVal != null) { + profit.setText("$ " + newVal.setScale(2, RoundingMode.HALF_EVEN)); + } else { + profit.setText(""); + } + }); + } + + /** + * Formats the text representation of the share in the portfolioListView + * and applies a color to it based on its current sales price compared + * to the buy price of the share. + * + * @param share the share to be formatted + * @param currentPrice current price of the stock the share belongs to + * @return a {@link TextFlow} representation of the share + */ + private TextFlow createColoredShareText(Share share, BigDecimal currentPrice) { + String text = share.getStock().getSymbol() + " - " + share.getStock().getCompany() + " | " @@ -390,17 +389,22 @@ private TextFlow createColoredShareText(Share share, BigDecimal currentPrice) { + lm.getString("portfolioCurrentPrice") + "$"; - Text t1 = new Text(text); - Text tValue = new Text(FormatBigDecimal.formatNumber(currentPrice)); + Text t1 = new Text(text); + Text tvalue = new Text(FormatBigDecimal.formatNumber(currentPrice)); - BigDecimal buyPrice = share.getPurchasePrice(); - if (buyPrice != null && currentPrice != null) { - int compare = currentPrice.compareTo(buyPrice); - if (compare > 0) tValue.setStyle("-fx-fill: #2ecc71; -fx-font-weight: bold;"); - else if (compare < 0) tValue.setStyle("-fx-fill: #e74c3c; -fx-font-weight: bold;"); - else tValue.setStyle("-fx-fill: #000000;"); - } + BigDecimal buyPrice = share.getPurchasePrice(); + if (buyPrice != null && currentPrice != null) { + int compare = currentPrice.compareTo(buyPrice); - return new TextFlow(t1, tValue); + if (compare > 0) { + tvalue.setStyle("-fx-fill: #2ecc71; -fx-font-weight: bold;"); + } else if (compare < 0) { + tvalue.setStyle("-fx-fill: #e74c3c; -fx-font-weight: bold;"); + } else { + tvalue.setStyle("-fx-fill: #000000;"); + } } + + return new TextFlow(t1, tvalue); + } } \ No newline at end of file diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java b/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java index a49d52b..264fa74 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java +++ b/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java @@ -5,7 +5,6 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; - import javafx.util.Subscription; import no.ntnu.gruppe53.controller.StartViewController; import no.ntnu.gruppe53.controller.ViewController; @@ -13,77 +12,77 @@ /** * Represents the first page seen by the user of the application. + * *Uses a {@link LanguageManager} for dynamically setting button and label text. * Subscribes to changes in language set by the manager.
*/ public class StartView extends VBox { - private final Button newGameButton; - private final Button languageButton; - private final Button quitButton; - private final Image logo; - private Subscription languageSubscription; - - /** - * Constructs the view and sets parameters to be exposed for the {@link StartViewController}. - * - * @param vm the {@link ViewController} for the view - */ - public StartView(ViewController vm) { - LanguageManager lm = LanguageManager.getInstance(); + private final Button newGameButton; + private final Button languageButton; + private final Button quitButton; - this.setAlignment(Pos.CENTER); - this.setSpacing(20); + /** + * Constructs the view and sets parameters to be exposed for the {@link StartViewController}. + * + * @param vm the {@link ViewController} for the view + */ + public StartView(ViewController vm) { + this.setAlignment(Pos.CENTER); + this.setSpacing(20); - this.setStyle("-fx-background-color: #00bcf0;"); + this.setStyle("-fx-background-color: #00bcf0;"); - logo = new Image(getClass().getClassLoader().getResourceAsStream("Millions-logo.png")); - ImageView logoView = new ImageView(logo); - logoView.setFitWidth(300); - logoView.setPreserveRatio(true); + Image logo = new Image(getClass().getClassLoader().getResourceAsStream("Millions-logo.png")); + ImageView logoView = new ImageView(logo); + logoView.setFitWidth(300); + logoView.setPreserveRatio(true); - newGameButton = new Button(); - newGameButton.textProperty().bind(lm.bindString("newGame")); + LanguageManager lm = LanguageManager.getInstance(); + newGameButton = new Button(); + newGameButton.textProperty().bind(lm.bindString("newGame")); - languageButton = new Button(); - languageButton.setPrefWidth(200); - languageButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - languageSubscription = lm.getBundleProperty().subscribe(bundle -> { - languageButton.setText(lm.getString("languageButtonLabel")); - }); + languageButton = new Button(); + languageButton.setPrefWidth(200); + languageButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + Subscription languageSubscription = lm.getBundleProperty().subscribe(bundle -> { + languageButton.setText(lm.getString("languageButtonLabel")); + }); - quitButton = new Button(); - quitButton.textProperty().bind(lm.bindString("quitGame")); + quitButton = new Button(); + quitButton.textProperty().bind(lm.bindString("quitGame")); - newGameButton.setPrefWidth(200); - newGameButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - quitButton.setPrefWidth(200); - quitButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + newGameButton.setPrefWidth(200); + newGameButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); + quitButton.setPrefWidth(200); + quitButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;"); - this.getChildren().addAll(logoView, newGameButton, languageButton, quitButton); - } + this.getChildren().addAll(logoView, newGameButton, languageButton, quitButton); + } - /** - * Sets the method to be run when the new game button is pressed - * - * @param action the method to execute on button press - */ - public void setOnNewGame(Runnable action) { - newGameButton.setOnAction(e -> action.run()); - } + /** + * Sets the method to be run when the new game button is pressed. + * + * @param action the method to execute on button press + */ + public void setOnNewGame(Runnable action) { + newGameButton.setOnAction(e -> action.run()); + } - /** - * Sets the method to be run when the languageButton is pressed - * - * @param action the method to execute on button press - */ - public void setOnLanguage(Runnable action) {languageButton.setOnAction(e -> action.run());} + /** + * Sets the method to be run when the languageButton is pressed. + * + * @param action the method to execute on button press + */ + public void setOnLanguage(Runnable action) { + languageButton.setOnAction(e -> action.run()); + } - /** - * Sets the method to be run when the quit game button is pressed - * - * @param action the method to execute on button press - */ - public void setOnQuit(Runnable action) { - quitButton.setOnAction(e -> action.run()); - } + /** + * Sets the method to be run when the quit game button is pressed. + * + * @param action the method to execute on button press + */ + public void setOnQuit(Runnable action) { + quitButton.setOnAction(e -> action.run()); + } } \ No newline at end of file diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/StockLineChartView.java b/millions/src/main/java/no/ntnu/gruppe53/view/StockLineChartView.java index adb5dc5..048d5b9 100644 --- a/millions/src/main/java/no/ntnu/gruppe53/view/StockLineChartView.java +++ b/millions/src/main/java/no/ntnu/gruppe53/view/StockLineChartView.java @@ -1,126 +1,128 @@ package no.ntnu.gruppe53.view; + +import java.util.List; import javafx.geometry.Side; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import no.ntnu.gruppe53.model.Stock; -import java.util.List; - /** * Represents a {@link LineChart} view of a {@link Stock}. + * *Y-axis: Price as a {@code double} value. Represented by a {@link NumberAxis}
+ * *X-axis: Week as an {@code int} value. Represented by a {@code NumberAxis}
*/ public class StockLineChartView { - private final NumberAxis xAxis = new NumberAxis(); - private final NumberAxis yAxis = new NumberAxis(); - - private final LineChartSets autoscale, stepsize and manual values for the range x- and y-axis, as well - * as the labels.
- *Initializes the LineChart.
- */ - - public StockLineChartView() { - xAxis.setAutoRanging(false); - yAxis.setAutoRanging(true); - yAxis.setForceZeroInRange(false); - - xAxis.setLowerBound(1); - - xAxis.setLabel("Week"); - yAxis.setLabel("Price"); - - chart = new LineChart<>(xAxis, yAxis); - } - - /** - * Returns the constructed LineChart. - * - * @return the current LineChart - */ - public LineChartSets autoscale, stepsize and manual values for the range x- and y-axis, as well + * as the labels.
+ * + *Initializes the LineChart.
+ */ + public StockLineChartView() { + xaxis.setAutoRanging(false); + NumberAxis yaxis = new NumberAxis(); + yaxis.setAutoRanging(true); + yaxis.setForceZeroInRange(false); + + xaxis.setLowerBound(1); + + xaxis.setLabel("Week"); + yaxis.setLabel("Price"); + + chart = new LineChart<>(xaxis, yaxis); + } + + /** + * Returns the constructed LineChart. + * + * @return the current LineChart + */ + public LineChartHere the series values are given as a list of {@code XYChart.Series} objects.
+ * + * @param seriesList the list of XYChart.Series containing the x- and y-values for each stock + */ + public void setSeries(ListHere the series values are given as a list of {@code XYChart.Series} objects.
- * - * @param seriesList the list of XYChart.Series containing the x- and y-values for each stock - */ + double xax = calculatexAxisMax(seriesList); - public void setSeries(ListExtends the {@link BorderPane} for easy layout setup.
*/ public class TransactionArchiveView extends BorderPane { - private final LanguageManager lm = LanguageManager.getInstance(); + private final LanguageManager lm = LanguageManager.getInstance(); + + private ListViewBuilds the necessary components and places them in a {@link VBox}.
+ * + *Uses a cell factory to format the string shown
+ * + *Assigns red text to purchases, and green text to sales.
+ * + *Uses the formatBigDecimal method to convert the BigDecimal to a + * string with 2 decimals.
+ * + *Uses a {@link LanguageManager} to dynamically set text based on target language.
+ * + * @return a VBox of the constructed view of the transaction archive + */ + private VBox centerPane() { + VBox vbox = new VBox(10); + vbox.setPadding(new Insets(15, 12, 15, 12)); + vbox.setStyle("-fx-background-color: #00bcf0; "); + + Label titleLabel = new Label(); + titleLabel.textProperty().bind(lm.bindString("historyTitle")); + titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #303539;"); + + VBox titleBox = new VBox(titleLabel); + titleBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + titleBox.setAlignment(Pos.CENTER); + titleBox.setPadding(new Insets(4)); + titleBox.setMaxWidth(300); + + historyListView = new ListView<>(); + historyListView.setMinHeight(0); + historyListView.setMaxHeight(Double.MAX_VALUE); + + historyListView.setCellFactory(list -> new ListCell<>() { + private Subscription langSubscription; + + @Override + protected void updateItem(Transaction item, boolean empty) { + super.updateItem(item, empty); + + if (langSubscription != null) { + langSubscription.unsubscribe(); + langSubscription = null; + } - private ListViewBuilds the necessary components and places them in a {@link VBox}.
- *Uses a cell factory to format the string shown
- *Assigns red text to purchases, and green text to sales.
- *Uses the formatBigDecimal method to convert the BigDecimal to a - * string with 2 decimals.
- *Uses a {@link LanguageManager} to dynamically set text based on target language.
- * - * @return a VBox of the constructed view of the transaction archive - */ - private VBox centerPane() { - VBox vBox = new VBox(10); - vBox.setPadding(new Insets(15, 12, 15, 12)); - vBox.setStyle("-fx-background-color: #00bcf0; "); - - titleLabel = new Label(); - titleLabel.textProperty().bind(lm.bindString("historyTitle")); - titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #303539;"); - - VBox titleBox = new VBox(titleLabel); - titleBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); - titleBox.setAlignment(Pos.CENTER); - titleBox.setPadding(new Insets(4)); - titleBox.setMaxWidth(300); - - historyListView = new ListView<>(); - historyListView.setMinHeight(0); - historyListView.setMaxHeight(Double.MAX_VALUE); - - historyListView.setCellFactory(list -> new ListCell<>() { - private Subscription langSubscription; - - @Override - protected void updateItem(Transaction item, boolean empty) { - super.updateItem(item, empty); - - if (langSubscription != null) { - langSubscription.unsubscribe(); - langSubscription = null; - } - - if (empty || item == null) { - setText(null); - setStyle(""); - } else { - Runnable updateTextAction = () -> { - String typeStr; - BigDecimal price = BigDecimal.ZERO; - BigDecimal profit = BigDecimal.ZERO; - // Changed to allow for more transaction types in the future - if (item instanceof Purchase) { - typeStr = lm.getString("historyTypePurchase"); - price = item.getShare().getPurchasePrice(); - } else if (item instanceof Sale sale) { - typeStr = lm.getString("historyTypeSale"); - price = item.getShare().getStock().getSalesPrice(); - if (sale.getCalculator() instanceof SaleCalculator saleCalc) { - profit = saleCalc.getProfitProperty().getValue(); - } - } else { - typeStr = "ERROR"; - } - - BigDecimal quantity = item.getShare().getQuantity(); - BigDecimal gross = item.getCalculator().calculateGross(); - BigDecimal commission = item.getCalculator().calculateCommission(); - BigDecimal tax = item.getCalculator().calculateTax(); - BigDecimal total = item.getCalculator().calculateTotal(); - - String symbol = item.getShare().getStock().getSymbol(); - - String template = lm.getString("historyCellFormat"); - - setText(String.format(template, + setText(String.format(template, item.getWeek(), typeStr, symbol, @@ -135,81 +148,88 @@ protected void updateItem(Transaction item, boolean empty) { FormatBigDecimal.formatNumber(tax), FormatBigDecimal.formatNumber(total), FormatBigDecimal.formatNumber(profit) - )); - }; - - langSubscription = lm.getBundleProperty().subscribe(bundle -> updateTextAction.run()); + )); + }; - updateTextAction.run(); + langSubscription = lm.getBundleProperty() + .subscribe(bundle -> updateTextAction.run()); - if (item instanceof Purchase) { - setStyle("-fx-text-fill: #c0392b; -fx-font-weight: bold;"); - } else if (item instanceof Sale) { - setStyle("-fx-text-fill: #27ae60; -fx-font-weight: bold;"); - } - } - } - }); - - HBox searchBox = new HBox(); - searchField = new TextField(); - searchField.promptTextProperty().bind(lm.bindString("search")); - searchBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" - ); + updateTextAction.run(); - searchBox.setPadding(new Insets(8, 14, 8, 14)); - searchBox.setAlignment(Pos.CENTER_LEFT); - searchBox.setMaxWidth(Region.USE_PREF_SIZE); - searchBox.getChildren().addAll(searchField); - - VBox historyListBox = new VBox(historyListView); - historyListBox.setStyle("-fx-background-color: #ffe556;" + - "-fx-text-fill: #303539;" + - "-fx-background-radius: 8;" + - "-fx-border-color: #303539;" + - "-fx-border-radius: 8;" + if (item instanceof Purchase) { + setStyle("-fx-text-fill: #c0392b; -fx-font-weight: bold;"); + } else if (item instanceof Sale) { + setStyle("-fx-text-fill: #27ae60; -fx-font-weight: bold;"); + } + } + } + }); + + HBox searchBox = new HBox(); + searchField = new TextField(); + searchField.promptTextProperty().bind(lm.bindString("search")); + searchBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + searchBox.setPadding(new Insets(8, 14, 8, 14)); + searchBox.setAlignment(Pos.CENTER_LEFT); + searchBox.setMaxWidth(Region.USE_PREF_SIZE); + searchBox.getChildren().addAll(searchField); + + VBox historyListBox = new VBox(historyListView); + historyListBox.setStyle("-fx-background-color: #ffe556;" + + "-fx-text-fill: #303539;" + + "-fx-background-radius: 8;" + + "-fx-border-color: #303539;" + + "-fx-border-radius: 8;" + ); + + historyListBox.setPadding(new Insets(4)); + historyListBox.setMinHeight(0); + historyListBox.setMaxHeight(Double.MAX_VALUE); + + VBox.setVgrow(historyListView, Priority.ALWAYS); + VBox.setVgrow(historyListBox, Priority.ALWAYS); + + vbox.getChildren().addAll(titleBox, searchBox, historyListBox); + return vbox; + } + + /** + * Sets the player to be observed. + * + *And puts the {@link TransactionArchive} into a {@link FilteredList} for searching.
+ * + * @param player the player to be observed + */ + public void setPlayer(Player player) { + if (player != null && player.getTransactionArchive().getObservableTransactions() != null) { + + filteredTransactionList = new FilteredList<>( + player.getTransactionArchive().getObservableTransactions()); + + historyListView.setItems(filteredTransactionList); + + searchField.textProperty().addListener(( + observable, oldText, newText) -> { + String search = newText == null ? "" : newText.toLowerCase(); + + filteredTransactionList.setPredicate(transaction -> search.isBlank() + || + transaction.getShare().getStock() + .getSymbol().toLowerCase().contains(search) + || + transaction.getShare().getStock() + .getCompany().toLowerCase().contains(search) ); + }); - historyListBox.setPadding(new Insets(4)); - historyListBox.setMinHeight(0); - historyListBox.setMaxHeight(Double.MAX_VALUE); - - VBox.setVgrow(historyListView, Priority.ALWAYS); - VBox.setVgrow(historyListBox, Priority.ALWAYS); - - vBox.getChildren().addAll(titleBox, searchBox, historyListBox); - return vBox; - } - - /** - * Sets the player to be observed. - *And puts the {@link TransactionArchive} into a {@link FilteredList} for searching.
- * - * @param player the player to be observed - */ - public void setPlayer(Player player) { - if (player != null && player.getTransactionArchive().getObservableTransactions() != null) { - - - filteredTransactionList = new FilteredList<>(player.getTransactionArchive().getObservableTransactions()); - - historyListView.setItems(filteredTransactionList); - - searchField.textProperty().addListener((observable, oldText, newText) -> { - String search = newText == null ? "" : newText.toLowerCase(); - - filteredTransactionList.setPredicate( transaction -> search.isBlank() - || transaction.getShare().getStock().getSymbol().toLowerCase().contains(search) - || transaction.getShare().getStock().getCompany().toLowerCase().contains(search) - ); - }); - - } else { - historyListView.setItems(null); - } + } else { + historyListView.setItems(null); } + } }