From a71aeb0cd8ac5b634b240d5cdd81ac57d44b7286 Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 21 Oct 2024 11:46:07 -0400 Subject: [PATCH 001/113] Logout finished --- src/main/java/use_case/logout/LogoutInputData.java | 7 +++++++ src/main/java/use_case/logout/LogoutInteractor.java | 6 ++++++ src/main/java/use_case/logout/LogoutOutputData.java | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/use_case/logout/LogoutInputData.java b/src/main/java/use_case/logout/LogoutInputData.java index 56a33b375..ea655256c 100644 --- a/src/main/java/use_case/logout/LogoutInputData.java +++ b/src/main/java/use_case/logout/LogoutInputData.java @@ -5,8 +5,15 @@ */ public class LogoutInputData { + private String userName; + public LogoutInputData(String username) { // TODO: save the current username in an instance variable and add a getter. + userName = username; + } + + public String getUserName() { + return userName; } } diff --git a/src/main/java/use_case/logout/LogoutInteractor.java b/src/main/java/use_case/logout/LogoutInteractor.java index 1ca93b44e..fea66975e 100644 --- a/src/main/java/use_case/logout/LogoutInteractor.java +++ b/src/main/java/use_case/logout/LogoutInteractor.java @@ -11,6 +11,8 @@ public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface, LogoutOutputBoundary logoutOutputBoundary) { // TODO: save the DAO and Presenter in the instance variables. // Which parameter is the DAO and which is the presenter? + this.userDataAccessObject = userDataAccessInterface; + this.logoutPresenter = logoutOutputBoundary; } @Override @@ -20,6 +22,10 @@ public void execute(LogoutInputData logoutInputData) { // * set the username to null in the DAO // * instantiate the `LogoutOutputData`, which needs to contain the username. // * tell the presenter to prepare a success view. + final String username = logoutInputData.getUserName(); + userDataAccessObject.setCurrentUsername(null); + final LogoutOutputData outputData = new LogoutOutputData(username, true); + logoutPresenter.prepareSuccessView(outputData); } } diff --git a/src/main/java/use_case/logout/LogoutOutputData.java b/src/main/java/use_case/logout/LogoutOutputData.java index 974279155..9f8e20539 100644 --- a/src/main/java/use_case/logout/LogoutOutputData.java +++ b/src/main/java/use_case/logout/LogoutOutputData.java @@ -9,7 +9,8 @@ public class LogoutOutputData { private boolean useCaseFailed; public LogoutOutputData(String username, boolean useCaseFailed) { - // TODO: save the parameters in the instance variables. + this.username = username; + this.useCaseFailed = useCaseFailed; } public String getUsername() { From 81dd674b7d7b322f969bd41aa024aa7f150c85db Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Mon, 21 Oct 2024 11:52:25 -0400 Subject: [PATCH 002/113] lab 5 --- .idea/compiler.xml | 13 +++++++++++++ .idea/encodings.xml | 7 +++++++ .idea/misc.xml | 12 ++++++++++++ .idea/vcs.xml | 6 ++++++ homework-5.iml | 6 ++++++ src/main/java/app/Main.java | 1 + .../interface_adapter/logout/LogoutController.java | 4 ++++ .../interface_adapter/logout/LogoutPresenter.java | 14 ++++++++++++++ src/main/java/view/LoggedInView.java | 3 +++ 9 files changed, 66 insertions(+) create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 homework-5.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 000000000..69d41be32 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..aa00ffab7 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..0abcc97ca --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/homework-5.iml b/homework-5.iml new file mode 100644 index 000000000..9e3449c9d --- /dev/null +++ b/homework-5.iml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index bef63ad7a..2f8f29b2a 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -19,6 +19,7 @@ public static void main(String[] args) { .addLoggedInView() .addSignupUseCase() .addLoginUseCase() + .addLogoutUseCase() .addChangePasswordUseCase() .build(); diff --git a/src/main/java/interface_adapter/logout/LogoutController.java b/src/main/java/interface_adapter/logout/LogoutController.java index e184a3bba..2bb7a3efd 100644 --- a/src/main/java/interface_adapter/logout/LogoutController.java +++ b/src/main/java/interface_adapter/logout/LogoutController.java @@ -1,6 +1,7 @@ package interface_adapter.logout; import use_case.logout.LogoutInputBoundary; +import use_case.logout.LogoutInputData; /** * The controller for the Logout Use Case. @@ -11,6 +12,7 @@ public class LogoutController { public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) { // TODO: Save the interactor in the instance variable. + this.logoutUseCaseInteractor = logoutUseCaseInteractor; } /** @@ -21,5 +23,7 @@ public void execute(String username) { // TODO: run the use case interactor for the logout use case // 1. instantiate the `LogoutInputData`, which should contain the username. // 2. tell the Interactor to execute. + final LogoutInputData loginInputData = new LogoutInputData(username); + logoutUseCaseInteractor.execute(loginInputData); } } diff --git a/src/main/java/interface_adapter/logout/LogoutPresenter.java b/src/main/java/interface_adapter/logout/LogoutPresenter.java index 78ef306a1..abf19aeaa 100644 --- a/src/main/java/interface_adapter/logout/LogoutPresenter.java +++ b/src/main/java/interface_adapter/logout/LogoutPresenter.java @@ -1,7 +1,9 @@ package interface_adapter.logout; import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; +import interface_adapter.login.LoginState; import interface_adapter.login.LoginViewModel; import use_case.logout.LogoutOutputBoundary; import use_case.logout.LogoutOutputData; @@ -19,6 +21,9 @@ public LogoutPresenter(ViewManagerModel viewManagerModel, LoggedInViewModel loggedInViewModel, LoginViewModel loginViewModel) { // TODO: assign to the three instance variables. + this.loggedInViewModel = loggedInViewModel; + this.viewManagerModel = viewManagerModel; + this.loginViewModel = loginViewModel; } @Override @@ -34,12 +39,21 @@ public void prepareSuccessView(LogoutOutputData response) { // 2. set the username in the state to the empty string // 3. set the state in the LoggedInViewModel to the updated state // 4. firePropertyChanged so that the View that is listening is updated. + final LoggedInState loggedInState = loggedInViewModel.getState(); + loggedInState.setUsername(""); + this.loggedInViewModel.setState(loggedInState); + this.loggedInViewModel.firePropertyChanged(); // TODO: have prepareSuccessView update the LoginState // 5. get the LoginState out of the appropriate View Model, // 6. set the username and password in the state to the empty string // 7. set the state in the LoginViewModel to the updated state // 8. firePropertyChanged so that the View that is listening is updated. + final LoginState loginState = loginViewModel.getState(); + loginState.setUsername(""); + loginState.setPassword(""); + this.loginViewModel.setState(loginState); + this.loginViewModel.firePropertyChanged(); // This code tells the View Manager to switch to the LoginView. this.viewManagerModel.setState(loginViewModel.getViewName()); diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java index 76d40c0f1..06f458568 100644 --- a/src/main/java/view/LoggedInView.java +++ b/src/main/java/view/LoggedInView.java @@ -103,6 +103,8 @@ public void changedUpdate(DocumentEvent e) { // TODO: execute the logout use case through the Controller // 1. get the state out of the loggedInViewModel. It contains the username. // 2. Execute the logout Controller. + final LoggedInState currentState = loggedInViewModel.getState(); + logoutController.execute(currentState.getUsername()); } } ); @@ -139,5 +141,6 @@ public void setChangePasswordController(ChangePasswordController changePasswordC public void setLogoutController(LogoutController logoutController) { // TODO: save the logout controller in the instance variable. + this.logoutController = logoutController; } } From f5eddd8fc8142857d014c61aa916495959bd37ca Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp <23240718+Kevin-Xiaoran@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:39:51 -0500 Subject: [PATCH 003/113] Update README.md Upload blueprint --- README.md | 61 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 8c4770018..5bbf41075 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,44 @@ -# Lab 5: Logout +# Course Project Blueprint + +## Domain + +Stock MarketPlace + +## Project Description +Introducing our Stock Marketplace Application, a user-friendly platform designed for both casual viewers and active investors. With this app, users can quickly look up stock information by entering ticker symbols without needing to log in, providing immediate access to essential market data. For personalized experience, users can create an account to build a watchlist of their favorite stocks, making it easy to track preferred investments. Additionally, the application features a simulated holdings section where logged-in users can add stocks along with their purchase amounts and prices, allowing for real-time calculations of day gains and total gains. + +## User Stories +- As a visitor, I want to view stock information by entering a ticker symbol, so that I can quickly access market data without needing to create an account. +- As a visitor, I want to sign up for an account. +- As a registered user, I want to log in to my account, so that I can save my favorite stocks in a personalized watchlist. +- As a registered user, I want to log in to my account, so that I can access my watchlist and view my favourite stocks’ information. +- As a registered user, I want to log in to my account, so that I can access add any stock in my simulated holding. +- As a registered user, I want to log in to my account, so that I can access my simulated holding and view day’s gain and total gain. +- As a registered user, I want to log out of my account, so that my information remains secure when I’m not using the application. + +## Proposed Entities +`User` +- Name string +- Password string +- Watchlist array of string +- SimulatedHoldings array of `SimulatedHolding` + +`SimulatedHolding (Stock)` +- Amount int +- Purchased Price int + +`Stock` +- Symbol string +- OpenPrice int +- ClosePrice int +- Volume int +- High int +- Low int + +## External APIs +- User related: http://vm003.teach.cs.toronto.edu:20112/user +- Stock information: https://api.marketstack.com/v1/eod?symbols=AAPL -## Preamble - -In the current homework, you added code to the login use case to save the currently-logged-in -user by saving the user in the Data Access Layer. You also added a unit test for this. - -In this lab, you will complete a logout use case as a team. You will also begin to discuss your project -and the use cases that need to be implemented. - -We have created all the Clean Architecture classes necessary for the logout use case. - -By Friday, your team will submit: -- your completed lab code [for credit] -- a draft of your project blueprint proposal. [required, but not for credit] - -# Phase 2 [for credit] -_(recall, Phase 1 was your solo task of adding the storage of the currently-logged-in user)_ - -## Task 0: Fork this repo on GitHub -**To get started, one team member should fork this repo on GitHub and share it with the team. -All of you should then clone it.** * * * From c528689ce1e9f3bf3cc55aa1ec40ca4e120ddafe Mon Sep 17 00:00:00 2001 From: Ryan Jin Date: Mon, 4 Nov 2024 11:35:31 -0500 Subject: [PATCH 004/113] test --- src/main/java/app/Main.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index bef63ad7a..3157d81f7 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -12,6 +12,7 @@ public class Main { */ public static void main(String[] args) { final AppBuilder appBuilder = new AppBuilder(); + System.out.println("hi"); // TODO: add the Logout Use Case to the app using the appBuilder final JFrame application = appBuilder .addLoginView() From e10fe605c93d46b6a19e7c684fe1bd70d6dc9992 Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 4 Nov 2024 11:59:53 -0500 Subject: [PATCH 005/113] Test --- src/main/java/app/Main.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 3157d81f7..e628d69da 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -13,6 +13,7 @@ public class Main { public static void main(String[] args) { final AppBuilder appBuilder = new AppBuilder(); System.out.println("hi"); + System.out.println("hi"); // TODO: add the Logout Use Case to the app using the appBuilder final JFrame application = appBuilder .addLoginView() From dfc348eff8a19d806fcd89e4ef2f5de6bcaf2aa1 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp <23240718+Kevin-Xiaoran@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:14:44 -0500 Subject: [PATCH 006/113] Update README.md Update user stories assignment --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5bbf41075..ac4ed4ba2 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,12 @@ Stock MarketPlace Introducing our Stock Marketplace Application, a user-friendly platform designed for both casual viewers and active investors. With this app, users can quickly look up stock information by entering ticker symbols without needing to log in, providing immediate access to essential market data. For personalized experience, users can create an account to build a watchlist of their favorite stocks, making it easy to track preferred investments. Additionally, the application features a simulated holdings section where logged-in users can add stocks along with their purchase amounts and prices, allowing for real-time calculations of day gains and total gains. ## User Stories -- As a visitor, I want to view stock information by entering a ticker symbol, so that I can quickly access market data without needing to create an account. -- As a visitor, I want to sign up for an account. -- As a registered user, I want to log in to my account, so that I can save my favorite stocks in a personalized watchlist. -- As a registered user, I want to log in to my account, so that I can access my watchlist and view my favourite stocks’ information. -- As a registered user, I want to log in to my account, so that I can access add any stock in my simulated holding. -- As a registered user, I want to log in to my account, so that I can access my simulated holding and view day’s gain and total gain. -- As a registered user, I want to log out of my account, so that my information remains secure when I’m not using the application. +- As a visitor, I want to view stock information by entering a ticker symbol, so that I can quickly access market data without needing to create an account. (kairanzh) +- As a registered user, I want to log in to my account, so that I can save my favorite stocks in a personalized watchlist. (huan4000) +- As a registered user, I want to log in to my account, so that I can access my watchlist and view my favourite stocks’ information. (guozifa1) +- As a registered user, I want to log in to my account, so that I can access add any stock in my simulated holding. (huan4066) +- As a registered user, I want to log in to my account, so that I can access my simulated holding and view day’s gain and total gain. (jinryan1) +- As a registered user, I want to log out of my account, so that my information remains secure when I’m not using the application. (huan4066) ## Proposed Entities `User` From 8ddb5e5a12fc1b6e8d45cb0392e35f91c3dae80e Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp <23240718+Kevin-Xiaoran@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:31:46 -0500 Subject: [PATCH 007/113] Update README.md update documentation info --- README.md | 160 +----------------------------------------------------- 1 file changed, 3 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index ac4ed4ba2..45bd0b6f4 100644 --- a/README.md +++ b/README.md @@ -38,162 +38,8 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed - User related: http://vm003.teach.cs.toronto.edu:20112/user - Stock information: https://api.marketstack.com/v1/eod?symbols=AAPL +## Documentation +`Link`: https://marketstack.com/documentation -* * * +`access_key`: 3847d86b56ca461a0da759024332c06a -Suggested logistics: One of you should invite the others to collaborate on their fork of the -original repo on GitHub. You can do this in your repo on GitHub under `Settings -> Collaborators`. -This will allow you to push branches to a common repo and then use pull requests to contribute -your code and review. To prevent others from pushing directly to the main branch, -we recommend you set branch protection rules on GitHub. Below are how the settings might look if you -add branch protection rules: - -![image of branch protection rules for main with the -requirement of two approvers to merge in pull requests. -](images/branch_protection_rules.png) - -* * * - -Open the project in IntelliJ and make sure you can successfully run `app/Main.java`. -Note: you may need to set the Project SDK in the `Project Structure...` menu, and possibly -also manually link the Maven project, as you did in Phase 1. - -## Task 1: Understanding the Program - -You may notice that we have refactored the CA engine code _slightly_ since Phase 1, but the -way we build the engine is drastically different: we have switched from using Factories to -using the Builder design pattern, which we'll be discussing in lecture soon. - -Open up `app.Main` and read it as a team. -- What are the Views and what are the current Use Cases? -- Which Uses Cases are triggered from each View? -- Which version of the DAO is `app.Main` using? - -The major change since Phase 1 is that we have added the `app.AppBuilder` class which makes -it easier to understand how our CA engine is being constructed — it also makes `app.Main` nice and concise! -- Why do all those `addX` methods end in `return this;`? - -Run the program and make sure the signup and login Use Cases work. - -Currently, you'll notice that the "Log Out" button still doesn't actually log you out. It's time to fix -that button, which is part of the `LoggedInView`. -We have created all the classes for you, but some of the code is missing. -As a team, your task is to fill in the missing code so that the Logout Use Case is implemented. -**The next part of the readme describes how your team will do this.** - -* * * - -**Your team will know when you are done when:** - -- Clicking the "Log Out" button takes the user back to the Login View when you use the program. -- The provided `LogoutInteractorTest` test passes. - -The "Log Out" button is an instance variable in class `LoggedInVew`. Go find it. -Also look at the `interface_adapter.change_password.LoggedInViewModel`, which contains any -data showing on the `LoggedInVew`. - -* * * - -## Task 2: Dividing up the work - -There are `TODO` comments left in the files -Recall that you can use the TODO tool window to conveniently pull up a complete list. - -Once the TODOs are all complete, the "Log Out" button _should_ work! - -As a team, split up the TODOs (see below) between the members of your team. - -There are TODOs in seven of the files. -Make sure each member has at least one TODO which they will be responsible for completing. -If your team prefers to work in pairs, that is fine too. Your individual branches -will not be graded for this — only the final, working version. - -The TODOs are summarized below (by file) to help your team decide how to split them up: - -* * * - -- `Main.java` - - - [ ] TODO: add the Logout Use Case to the app using the appBuilder - -* * * - -- `LoggedInView.java` (tip: refer to the other views for similar code) - - - [ ] TODO: save the logout controller in the instance variable. - - [ ] TODO: execute the logout use case through the Controller - -* * * - -- `LogoutController.java` (tip: refer to the other controllers for similar code) - - - [ ] TODO: Save the interactor in the instance variable. - - [ ] TODO: run the use case interactor for the logout use case - -* * * - -- `LogoutInputData.java` (should be done with the LogoutInteractor TODOs below) - - - [ ] TODO: save the current username in an instance variable and add a getter. - -- `LogoutInteractor.java` (tip: refer to `ChangePasswordInteractor.java` for similar code) - - - [ ] TODO: save the DAO and Presenter in the instance variables. - - [ ] TODO: implement the logic of the Logout Use Case - -* * * - -- `LogoutOutputData.java` - - - [ ] TODO: save the parameters in the instance variables. - -* * * - -- `LogoutPresenter.java` (tip: refer to `SignupPresenter.java` for similar code) - - - [ ] TODO: assign to the three instance variables. - - [ ] TODO: have prepareSuccessView update the LoggedInState - - [ ] TODO: have prepareSuccessView update the LoginState - -* * * - -1. Make a branch named the first part of your UofT email address, everything before the `@`. -For example, if your email address is `paul.gries@mail.utoronto.ca`, then the branch name would -be `paul.gries`. - -Make sure you switch to the new branch. - -In the terminal, this would look like below, but replaced with your own information: -``` -git branch paul.gries -git switch paul.gries -``` - -2. Complete your assigned TODOs and make a pull request on GitHub. In your pull request, - briefly describe what your TODOs were and how you implemented them. If you aren't sure - about part of it, include this in your pull request so everyone knows what to look - for when reviewing — or you can of course discuss with your team before making your - pull request since you are physically working in the same space. - - **Important: don't push any changes to the `.idea` folder, as that - may cause issues for your other teammates, as some files contain - configurations specific to your individual IntelliJ projects.** - -3. Review each other's pull requests to ensure each TODO is correctly implemented and that - there are no Checkstyle issues in the files that were modified. - -4. Once all TODOs are completed, your team should debug as needed to ensure the - correctness of the code. Setting a breakpoint where the log-out use case - interactor starts its work will likely be a great place to start when debugging. - -And that's it; you now have a working Logout Use Case! Instructions for -how to submit your work on MarkUs will be posted later. - -Your team should spend the rest of the lab working on your project blueprint. - -* * * - -# Project Blueprint - -See Quercus for details about the project blueprint! By the end of the week, -the goal is for your team to have a fully drafted blueprint so that your team -will be ready to get started on your project after Reading Week. From 92069627d9d1b91732e48f691415a2528f6ad870 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 7 Nov 2024 16:06:47 -0500 Subject: [PATCH 008/113] finish early version of home view --- src/main/java/app/AppBuilder.java | 46 +++++- src/main/java/app/Main.java | 4 +- .../data_access/DBUserDataAccessObject.java | 22 ++- src/main/java/entity/CommonStock.java | 53 +++++++ src/main/java/entity/CommonStockFactory.java | 12 ++ src/main/java/entity/Stock.java | 43 ++++++ src/main/java/entity/StockFactory.java | 19 +++ .../home_view/HomeController.java | 46 ++++++ .../home_view/HomePresenter.java | 52 +++++++ .../home_view/HomeState.java | 7 + .../home_view/HomeViewModel.java | 15 ++ .../home_view/HomeDataAccessInterface.java | 16 +++ .../use_case/home_view/HomeInputBoundary.java | 25 ++++ .../use_case/home_view/HomeInputData.java | 17 +++ .../use_case/home_view/HomeInteractor.java | 42 ++++++ .../home_view/HomeOutputBoundary.java | 34 +++++ .../use_case/home_view/HomeOutputData.java | 21 +++ src/main/java/view/HomeView.java | 133 ++++++++++++++++++ .../WatchListLabelButtonPanel.java | 15 ++ src/main/java/view/LoginView.java | 1 + 20 files changed, 612 insertions(+), 11 deletions(-) create mode 100644 src/main/java/entity/CommonStock.java create mode 100644 src/main/java/entity/CommonStockFactory.java create mode 100644 src/main/java/entity/Stock.java create mode 100644 src/main/java/entity/StockFactory.java create mode 100644 src/main/java/interface_adapter/home_view/HomeController.java create mode 100644 src/main/java/interface_adapter/home_view/HomePresenter.java create mode 100644 src/main/java/interface_adapter/home_view/HomeState.java create mode 100644 src/main/java/interface_adapter/home_view/HomeViewModel.java create mode 100644 src/main/java/use_case/home_view/HomeDataAccessInterface.java create mode 100644 src/main/java/use_case/home_view/HomeInputBoundary.java create mode 100644 src/main/java/use_case/home_view/HomeInputData.java create mode 100644 src/main/java/use_case/home_view/HomeInteractor.java create mode 100644 src/main/java/use_case/home_view/HomeOutputBoundary.java create mode 100644 src/main/java/use_case/home_view/HomeOutputData.java create mode 100644 src/main/java/view/HomeView.java create mode 100644 src/main/java/view/HomeViewComponents/WatchListLabelButtonPanel.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index e9eef5c81..05c308878 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -6,13 +6,19 @@ import javax.swing.JPanel; import javax.swing.WindowConstants; +import data_access.DBUserDataAccessObject; import data_access.InMemoryUserDataAccessObject; +import entity.CommonStockFactory; import entity.CommonUserFactory; +import entity.StockFactory; import entity.UserFactory; import interface_adapter.ViewManagerModel; import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; import interface_adapter.change_password.LoggedInViewModel; +import interface_adapter.home_view.HomeController; +import interface_adapter.home_view.HomePresenter; +import interface_adapter.home_view.HomeViewModel; import interface_adapter.login.LoginController; import interface_adapter.login.LoginPresenter; import interface_adapter.login.LoginViewModel; @@ -24,6 +30,7 @@ import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; +import use_case.home_view.*; import use_case.login.LoginInputBoundary; import use_case.login.LoginInteractor; import use_case.login.LoginOutputBoundary; @@ -33,10 +40,7 @@ import use_case.signup.SignupInputBoundary; import use_case.signup.SignupInteractor; import use_case.signup.SignupOutputBoundary; -import view.LoggedInView; -import view.LoginView; -import view.SignupView; -import view.ViewManager; +import view.*; /** * The AppBuilder class is responsible for putting together the pieces of @@ -54,12 +58,15 @@ public class AppBuilder { private final CardLayout cardLayout = new CardLayout(); // thought question: is the hard dependency below a problem? private final UserFactory userFactory = new CommonUserFactory(); + private final StockFactory stockFactory = new CommonStockFactory(); private final ViewManagerModel viewManagerModel = new ViewManagerModel(); private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel); // thought question: is the hard dependency below a problem? - private final InMemoryUserDataAccessObject userDataAccessObject = new InMemoryUserDataAccessObject(); + private final DBUserDataAccessObject userDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); + private HomeView homeView; + private HomeViewModel homeViewModel; private SignupView signupView; private SignupViewModel signupViewModel; private LoginViewModel loginViewModel; @@ -71,6 +78,17 @@ public AppBuilder() { cardPanel.setLayout(cardLayout); } + /** + * Adds the Home View to the application. + * @return this builder + */ + public AppBuilder addHomeView() { + homeViewModel = new HomeViewModel(); + homeView = new HomeView(homeViewModel); + cardPanel.add(homeView, homeView.getViewName()); + return this; + } + /** * Adds the Signup View to the application. * @return this builder @@ -104,6 +122,20 @@ public AppBuilder addLoggedInView() { return this; } + /** + * Adds the Home Use Case to the application. + * @return this builder + */ + public AppBuilder addHomeUseCase() { + final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, + loginViewModel, viewManagerModel); + final HomeInputBoundary homeInteractor = new HomeInteractor(userDataAccessObject, homeOutputBoundary); + + final HomeController controller = new HomeController(homeInteractor); + homeView.setHomeController(controller); + return this; + } + /** * Adds the Signup Use Case to the application. * @return this builder @@ -172,12 +204,12 @@ public AppBuilder addLogoutUseCase() { * @return the application */ public JFrame build() { - final JFrame application = new JFrame("Login Example"); + final JFrame application = new JFrame("Stock MarketPlace"); application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); application.add(cardPanel); - viewManagerModel.setState(signupView.getViewName()); + viewManagerModel.setState(homeView.getViewName()); viewManagerModel.firePropertyChanged(); return application; diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index e628d69da..959c27293 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -12,13 +12,13 @@ public class Main { */ public static void main(String[] args) { final AppBuilder appBuilder = new AppBuilder(); - System.out.println("hi"); - System.out.println("hi"); // TODO: add the Logout Use Case to the app using the appBuilder final JFrame application = appBuilder + .addHomeView() .addLoginView() .addSignupView() .addLoggedInView() + .addHomeUseCase() .addSignupUseCase() .addLoginUseCase() .addChangePasswordUseCase() diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index 377ee6e7e..b79b02755 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -2,6 +2,8 @@ import java.io.IOException; +import entity.Stock; +import entity.StockFactory; import org.json.JSONException; import org.json.JSONObject; @@ -13,6 +15,7 @@ import okhttp3.RequestBody; import okhttp3.Response; import use_case.change_password.ChangePasswordUserDataAccessInterface; +import use_case.home_view.HomeDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; import use_case.logout.LogoutUserDataAccessInterface; import use_case.signup.SignupUserDataAccessInterface; @@ -23,8 +26,16 @@ public class DBUserDataAccessObject implements SignupUserDataAccessInterface, LoginUserDataAccessInterface, ChangePasswordUserDataAccessInterface, - LogoutUserDataAccessInterface { + LogoutUserDataAccessInterface, + HomeDataAccessInterface { private static final int SUCCESS_CODE = 200; + + private static final int CLOSE = 138; + private static final int OPEN = 132; + private static final int VOLUME = 100502000; + private static final int HIGH = 140; + private static final int LOW = 125; + private static final String CONTENT_TYPE_LABEL = "Content-Type"; private static final String CONTENT_TYPE_JSON = "application/json"; private static final String STATUS_CODE_LABEL = "status_code"; @@ -32,9 +43,11 @@ public class DBUserDataAccessObject implements SignupUserDataAccessInterface, private static final String PASSWORD = "password"; private static final String MESSAGE = "message"; private final UserFactory userFactory; + private final StockFactory stockFactory; - public DBUserDataAccessObject(UserFactory userFactory) { + public DBUserDataAccessObject(UserFactory userFactory, StockFactory stockFactory) { this.userFactory = userFactory; + this.stockFactory = stockFactory; // No need to do anything to reinitialize a user list! The data is the cloud that may be miles away. } @@ -158,6 +171,11 @@ public void changePassword(User user) { } } + @Override + public Stock getStock(String symbol) { + return stockFactory.create("NVDA", OPEN, CLOSE, VOLUME, HIGH, LOW); + } + @Override public String getCurrentUsername() { return null; diff --git a/src/main/java/entity/CommonStock.java b/src/main/java/entity/CommonStock.java new file mode 100644 index 000000000..e54e6b443 --- /dev/null +++ b/src/main/java/entity/CommonStock.java @@ -0,0 +1,53 @@ +package entity; + +/** + * A simple implementation of the Stock interface. + */ +public class CommonStock implements Stock { + + private final String symbol; + private final int openPrice; + private final int closePrice; + private final int volume; + private final int high; + private final int low; + + public CommonStock(String symbol, int openPrice, int closePrice, int volume, int high, int low) { + this.symbol = symbol; + this.openPrice = openPrice; + this.closePrice = closePrice; + this.volume = volume; + this.high = high; + this.low = low; + } + + @Override + public String getSymbol() { + return symbol; + } + + @Override + public int getOpenPrice() { + return openPrice; + } + + @Override + public int getClosePrice() { + return closePrice; + } + + @Override + public int getVolume() { + return volume; + } + + @Override + public int getHigh() { + return high; + } + + @Override + public int getLow() { + return low; + } +} diff --git a/src/main/java/entity/CommonStockFactory.java b/src/main/java/entity/CommonStockFactory.java new file mode 100644 index 000000000..c62e87b56 --- /dev/null +++ b/src/main/java/entity/CommonStockFactory.java @@ -0,0 +1,12 @@ +package entity; + +/** + * Factory for creating CommonUser objects. + */ +public class CommonStockFactory implements StockFactory { + + @Override + public Stock create(String symbol, int openPrice, int closePrice, int volume, int high, int low) { + return new CommonStock(symbol, openPrice, closePrice, volume, high, low); + } +} diff --git a/src/main/java/entity/Stock.java b/src/main/java/entity/Stock.java new file mode 100644 index 000000000..f588e6cde --- /dev/null +++ b/src/main/java/entity/Stock.java @@ -0,0 +1,43 @@ +package entity; + +/** + * The representation of a stock in our program. + */ +public interface Stock { + + /** + * Returns the stock symbol/ticker of the stock. + * @return the stock symbol/ticker of the stock. + */ + String getSymbol(); + + /** + * Returns the stock open price of the stock. + * @return the stock open price of the stock. + */ + int getOpenPrice(); + + /** + * Returns the stock close price of the stock. + * @return the stock close price of the stock. + */ + int getClosePrice(); + + /** + * Returns the stock volume of the stock. + * @return the stock volume of the stock. + */ + int getVolume(); + + /** + * Returns the stock highest price of the stock. + * @return the stock highest price of the stock. + */ + int getHigh(); + + /** + * Returns the stock lowest price of the stock. + * @return the stock lowest price of the stock. + */ + int getLow(); +} diff --git a/src/main/java/entity/StockFactory.java b/src/main/java/entity/StockFactory.java new file mode 100644 index 000000000..2eb99b3af --- /dev/null +++ b/src/main/java/entity/StockFactory.java @@ -0,0 +1,19 @@ +package entity; + +/** + * Factory for creating stocks. + */ +public interface StockFactory { + + /** + * Creates a new User. + * @param symbol the symbol/ticker of the stock + * @param openPrice the open price of the stock + * @param closePrice the close price of the stock + * @param volume the volume of the stock + * @param high the highest price of the stock + * @param low the lowest price of the stock + * @return the new user + */ + Stock create(String symbol, int openPrice, int closePrice, int volume, int high, int low); +} diff --git a/src/main/java/interface_adapter/home_view/HomeController.java b/src/main/java/interface_adapter/home_view/HomeController.java new file mode 100644 index 000000000..507006d15 --- /dev/null +++ b/src/main/java/interface_adapter/home_view/HomeController.java @@ -0,0 +1,46 @@ +package interface_adapter.home_view; + +import use_case.home_view.HomeInputBoundary; +import use_case.home_view.HomeInputData; + +/** + * The controller for the Login Use Case. + */ +public class HomeController { + + private final HomeInputBoundary homeUseCaseInteractor; + + public HomeController(HomeInputBoundary homeUseCaseInteractor) { + this.homeUseCaseInteractor = homeUseCaseInteractor; + } + + /** + * Executes the Search Use Case. + * @param symbol the username of the user logging in + */ + public void search(String symbol) { + final HomeInputData searchInputData = new HomeInputData(symbol); + homeUseCaseInteractor.search(searchInputData); + } + + /** + * Executes the "switch to LoginView" Use Case. + */ + public void switchToPortfolio() { + homeUseCaseInteractor.switchToPortfolio(); + } + + /** + * Executes the "switch to LoginView" Use Case. + */ + public void switchToWatchList() { + homeUseCaseInteractor.switchToWatchList(); + } + + /** + * Executes the "switch to LoginView" Use Case. + */ + public void switchToLoginView() { + homeUseCaseInteractor.switchToLoginView(); + } +} diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java new file mode 100644 index 000000000..035ba9c8b --- /dev/null +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -0,0 +1,52 @@ +package interface_adapter.home_view; + +import interface_adapter.ViewManagerModel; +import interface_adapter.login.LoginViewModel; +import use_case.home_view.HomeOutputBoundary; +import use_case.home_view.HomeOutputData; + +/** + * The Presenter for the Search Use Case. + */ +public class HomePresenter implements HomeOutputBoundary { + + private final HomeViewModel homeViewModel; + private final LoginViewModel loginViewModel; + private final ViewManagerModel viewManagerModel; + + public HomePresenter(HomeViewModel homeViewModel, + LoginViewModel loginViewModel, + ViewManagerModel viewManagerModel) { + this.homeViewModel = homeViewModel; + this.loginViewModel = loginViewModel; + this.viewManagerModel = viewManagerModel; + } + + @Override + public void prepareSuccessView(HomeOutputData searchOutputData) { + // Present stock view + System.out.println("Show stock view after searching stock"); + } + + @Override + public void prepareFailView(String errorMessage) { + // Show error dialog + System.out.println("Show dialog view after failed to search stock"); + } + + @Override + public void switchToPortfolio() { + System.out.println("Show portfolio view"); + } + + @Override + public void switchToWatchList() { + System.out.println("Show watchlist view"); + } + + @Override + public void switchToLoginView() { + viewManagerModel.setState(loginViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/home_view/HomeState.java b/src/main/java/interface_adapter/home_view/HomeState.java new file mode 100644 index 000000000..6eccaa4db --- /dev/null +++ b/src/main/java/interface_adapter/home_view/HomeState.java @@ -0,0 +1,7 @@ +package interface_adapter.home_view; + +/** + * The state for the Home View Model. + */ +public class HomeState { +} diff --git a/src/main/java/interface_adapter/home_view/HomeViewModel.java b/src/main/java/interface_adapter/home_view/HomeViewModel.java new file mode 100644 index 000000000..c72165ac3 --- /dev/null +++ b/src/main/java/interface_adapter/home_view/HomeViewModel.java @@ -0,0 +1,15 @@ +package interface_adapter.home_view; + +import interface_adapter.ViewModel; + +/** + * The View Model for the Home View. + */ +public class HomeViewModel extends ViewModel { + + public HomeViewModel() { + super("home view"); + setState(new HomeState()); + } + +} diff --git a/src/main/java/use_case/home_view/HomeDataAccessInterface.java b/src/main/java/use_case/home_view/HomeDataAccessInterface.java new file mode 100644 index 000000000..c2b6a568f --- /dev/null +++ b/src/main/java/use_case/home_view/HomeDataAccessInterface.java @@ -0,0 +1,16 @@ +package use_case.home_view; + +import entity.Stock; + +/** + * DAO for the Login Use Case. + */ +public interface HomeDataAccessInterface { + + /** + * Returns the stock with the given symbol. + * @param symbol the stock symbol to look up + * @return the stock with the given symbol + */ + Stock getStock(String symbol); +} diff --git a/src/main/java/use_case/home_view/HomeInputBoundary.java b/src/main/java/use_case/home_view/HomeInputBoundary.java new file mode 100644 index 000000000..8b1be2c16 --- /dev/null +++ b/src/main/java/use_case/home_view/HomeInputBoundary.java @@ -0,0 +1,25 @@ +package use_case.home_view; + +public interface HomeInputBoundary { + + /** + * Executes the search use case. + * @param searchInputData the input data + */ + void search(HomeInputData searchInputData); + + /** + * Executes the switch to portfolio view use case. + */ + void switchToPortfolio(); + + /** + * Executes the switch to watch list view use case. + */ + void switchToWatchList(); + + /** + * Executes the switch to login view use case. + */ + void switchToLoginView(); +} diff --git a/src/main/java/use_case/home_view/HomeInputData.java b/src/main/java/use_case/home_view/HomeInputData.java new file mode 100644 index 000000000..017a62fbe --- /dev/null +++ b/src/main/java/use_case/home_view/HomeInputData.java @@ -0,0 +1,17 @@ +package use_case.home_view; + +/** + * The Input Data for the Search Use Case. + */ +public class HomeInputData { + + private final String stockSymbol; + + public HomeInputData(String stockSymbol) { + this.stockSymbol = stockSymbol; + } + + public String getStockSymbol() { + return stockSymbol; + } +} diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java new file mode 100644 index 000000000..3259dba11 --- /dev/null +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -0,0 +1,42 @@ +package use_case.home_view; + +import entity.Stock; + +/** + * The Home View Interactor. + */ +public class HomeInteractor implements HomeInputBoundary { + + private final HomeDataAccessInterface homeDataAccessObject; + private final HomeOutputBoundary homePresenter; + + public HomeInteractor(HomeDataAccessInterface homeDataAccessObject, + HomeOutputBoundary homeInteractor) { + this.homeDataAccessObject = homeDataAccessObject; + this.homePresenter = homeInteractor; + } + + @Override + public void search(HomeInputData searchInputData) { + final String stockSymbol = searchInputData.getStockSymbol(); + final Stock stock = homeDataAccessObject.getStock(stockSymbol); + + final HomeOutputData searchOutputData = new HomeOutputData(stock, false); + homePresenter.prepareSuccessView(searchOutputData); + } + + @Override + public void switchToPortfolio() { + homePresenter.switchToPortfolio(); + } + + @Override + public void switchToWatchList() { + homePresenter.switchToWatchList(); + } + + @Override + public void switchToLoginView() { + homePresenter.switchToLoginView(); + } +} diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java new file mode 100644 index 000000000..61cdc0516 --- /dev/null +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -0,0 +1,34 @@ +package use_case.home_view; + +/** + * The output boundary for the Home Use Case. + */ +public interface HomeOutputBoundary { + + /** + * Prepares the success view for the Search Use Case. + * @param searchOutputData the output data + */ + void prepareSuccessView(HomeOutputData searchOutputData); + + /** + * Prepares the failure view for the Search Use Case. + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); + + /** + * Switches to the portfolio View. + */ + void switchToPortfolio(); + + /** + * Switches to the watch list View. + */ + void switchToWatchList(); + + /** + * Switches to the Login View. + */ + void switchToLoginView(); +} diff --git a/src/main/java/use_case/home_view/HomeOutputData.java b/src/main/java/use_case/home_view/HomeOutputData.java new file mode 100644 index 000000000..dbfdf7e5a --- /dev/null +++ b/src/main/java/use_case/home_view/HomeOutputData.java @@ -0,0 +1,21 @@ +package use_case.home_view; + +import entity.Stock; + +/** + * Output Data for the Search Use Case. + */ +public class HomeOutputData { + + private final Stock stock; + private final boolean useCaseFailed; + + public HomeOutputData(Stock stock, boolean useCaseFailed) { + this.stock = stock; + this.useCaseFailed = useCaseFailed; + } + + public Stock getStock() { + return stock; + } +} diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java new file mode 100644 index 000000000..b09410741 --- /dev/null +++ b/src/main/java/view/HomeView.java @@ -0,0 +1,133 @@ +package view; + +import interface_adapter.home_view.HomeController; +import interface_adapter.home_view.HomeViewModel; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.*; + +/** + * The View for the Home Page. + */ +public class HomeView extends JPanel implements ActionListener, PropertyChangeListener { + + private static final String RIGHTARROW = "->"; + + private final String viewName = "home view"; + private final HomeViewModel homeViewModel; + private HomeController homeController; + + // Search components + private final JTextField searchTextField = new JTextField(30); + + // Stock view components + private final JLabel stockLabel = new JLabel("Stock View"); + private final JButton stockButton = new JButton(RIGHTARROW); + + // Portfolio components + private final JLabel portfolioLabel = new JLabel("Portfolio"); + private final JButton portfolioButton = new JButton(RIGHTARROW); + + // Watch list components + private final JLabel watchListLabel = new JLabel("Watchlist"); + private final JButton watchListButton = new JButton(RIGHTARROW); + private final JLabel firstStockLabel = new JLabel(" - AAPL"); + private final JButton firstStockButton = new JButton(RIGHTARROW); + private final JLabel secondStockLabel = new JLabel(" - NVDA"); + private final JButton secondStockButton = new JButton(RIGHTARROW); + private final JLabel thirdStockLabel = new JLabel(" - META"); + private final JButton thirdStockButton = new JButton(RIGHTARROW); + + // Login/Logout component + private final JButton loginButton = new JButton("Login"); + + public HomeView(HomeViewModel homeViewModel) { + this.homeViewModel = homeViewModel; + this.homeViewModel.addPropertyChangeListener(this); + + // Config search components style + searchTextField.setText("Search"); + final JPanel searchPanel = new JPanel(new FlowLayout()); + searchPanel.add(searchTextField); + + // Config stock view components style + final JPanel stockViewPanel = new JPanel(); + stockViewPanel.setLayout(new BoxLayout(stockViewPanel, BoxLayout.LINE_AXIS)); + stockViewPanel.add(stockLabel); + stockViewPanel.add(stockButton); + stockButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + homeController.search("NVDA"); + } + }); + + // Config portfolio components style + final JPanel portfolioPanel = new JPanel(); + portfolioPanel.setLayout(new BoxLayout(portfolioPanel, BoxLayout.LINE_AXIS)); + portfolioPanel.add(portfolioLabel); + portfolioPanel.add(portfolioButton); + portfolioButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + homeController.switchToPortfolio(); + } + }); + + // Config watch list components style + final JPanel watchListPanel = new JPanel(); + watchListPanel.setLayout(new BoxLayout(watchListPanel, BoxLayout.LINE_AXIS)); + watchListPanel.add(watchListLabel); + watchListPanel.add(watchListButton); + watchListButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + homeController.switchToWatchList(); + } + }); + + // Config login components style + loginButton.setAlignmentX(Component.CENTER_ALIGNMENT); + final JPanel loginPanel = new JPanel(); + loginPanel.setLayout(new BoxLayout(loginPanel, BoxLayout.LINE_AXIS)); + loginPanel.add(loginButton); + loginButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + homeController.switchToLoginView(); + } + }); + + // Config frame style + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + // Add all components + this.add(searchPanel); + this.add(stockViewPanel); + this.add(portfolioPanel); + this.add(watchListPanel); + this.add(loginPanel); + } + + /** + * React to a button click that results in evt. + * @param evt the ActionEvent to react to + */ + public void actionPerformed(ActionEvent evt) { + System.out.println("Click " + evt.getActionCommand()); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + + } + + public String getViewName() { + return viewName; + } + + public void setHomeController(HomeController homeController) { + this.homeController = homeController; + } +} diff --git a/src/main/java/view/HomeViewComponents/WatchListLabelButtonPanel.java b/src/main/java/view/HomeViewComponents/WatchListLabelButtonPanel.java new file mode 100644 index 000000000..d0cd3835c --- /dev/null +++ b/src/main/java/view/HomeViewComponents/WatchListLabelButtonPanel.java @@ -0,0 +1,15 @@ +package view.HomeViewComponents; + +import javax.swing.*; + +/** + * A panel containing a label and a button. + * - AAPL -> + */ +public class WatchListLabelButtonPanel extends JPanel { + WatchListLabelButtonPanel(JLabel label, JButton button) { + this.add(label); + this.add(button); + this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + } +} diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index 96d4f3845..4d0c271c3 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -52,6 +52,7 @@ public LoginView(LoginViewModel loginViewModel) { final JPanel buttons = new JPanel(); logIn = new JButton("log in"); + buttons.add(logIn); cancel = new JButton("cancel"); buttons.add(cancel); From e4cf0ea927b3250aad584331ae3069c00f5f99a4 Mon Sep 17 00:00:00 2001 From: misaka Date: Fri, 8 Nov 2024 13:24:16 -0500 Subject: [PATCH 009/113] Complete the view of WatchList Demo --- src/main/java/app/AppBuilder.java | 10 +++ src/main/java/app/Main.java | 1 + .../home_view/HomePresenter.java | 6 +- .../watchlist_view/WatchListController.java | 16 +++++ .../watchlist_view/WatchListViewModel.java | 24 +++++++ .../wathlist_view/WatchListInputBoundary.java | 15 +++++ .../wathlist_view/WatchListInputData.java | 17 +++++ src/main/java/view/WatchListView.java | 62 +++++++++++++++++++ 8 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/main/java/interface_adapter/watchlist_view/WatchListController.java create mode 100644 src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java create mode 100644 src/main/java/use_case/wathlist_view/WatchListInputBoundary.java create mode 100644 src/main/java/use_case/wathlist_view/WatchListInputData.java create mode 100644 src/main/java/view/WatchListView.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 05c308878..d6175876b 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -27,6 +27,7 @@ import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupPresenter; import interface_adapter.signup.SignupViewModel; +import interface_adapter.watchlist_view.WatchListViewModel; import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; @@ -73,6 +74,8 @@ public class AppBuilder { private LoggedInViewModel loggedInViewModel; private LoggedInView loggedInView; private LoginView loginView; + private WatchListView watchListView; + private WatchListViewModel watchListViewModel; public AppBuilder() { cardPanel.setLayout(cardLayout); @@ -122,6 +125,13 @@ public AppBuilder addLoggedInView() { return this; } + public AppBuilder addWatchListView() { + watchListViewModel = new WatchListViewModel(); + watchListView = new WatchListView(watchListViewModel); + cardPanel.add(watchListView, watchListView.getViewName()); + return this; + } + /** * Adds the Home Use Case to the application. * @return this builder diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 959c27293..6c513b18f 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -15,6 +15,7 @@ public static void main(String[] args) { // TODO: add the Logout Use Case to the app using the appBuilder final JFrame application = appBuilder .addHomeView() + .addWatchListView() .addLoginView() .addSignupView() .addLoggedInView() diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index 035ba9c8b..fc807a5df 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -4,6 +4,7 @@ import interface_adapter.login.LoginViewModel; import use_case.home_view.HomeOutputBoundary; import use_case.home_view.HomeOutputData; +import view.WatchListView; /** * The Presenter for the Search Use Case. @@ -14,6 +15,8 @@ public class HomePresenter implements HomeOutputBoundary { private final LoginViewModel loginViewModel; private final ViewManagerModel viewManagerModel; + private static final String WATCHLIST_VIEW_NAME = "WatchListView"; + public HomePresenter(HomeViewModel homeViewModel, LoginViewModel loginViewModel, ViewManagerModel viewManagerModel) { @@ -41,7 +44,8 @@ public void switchToPortfolio() { @Override public void switchToWatchList() { - System.out.println("Show watchlist view"); + viewManagerModel.setState("WatchListView"); + viewManagerModel.firePropertyChanged(); } @Override diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListController.java b/src/main/java/interface_adapter/watchlist_view/WatchListController.java new file mode 100644 index 000000000..36e28389b --- /dev/null +++ b/src/main/java/interface_adapter/watchlist_view/WatchListController.java @@ -0,0 +1,16 @@ +package interface_adapter.watchlist_view; + +import use_case.wathlist_view.WatchListInputBoundary; +import use_case.wathlist_view.WatchListInputData; + +/** + * The controller for the Login Use Case. + */ +public class WatchListController { + + private final WatchListInputBoundary WatchListUseCaseInteractor; + + public WatchListController(WatchListInputBoundary WatchListInteractor, WatchListInputBoundary watchListUseCaseInteractor) { + this.WatchListUseCaseInteractor = watchListUseCaseInteractor; + } +} diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java new file mode 100644 index 000000000..77e525022 --- /dev/null +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java @@ -0,0 +1,24 @@ +package interface_adapter.watchlist_view; + +import java.util.ArrayList; +import java.util.List; + +/** + * The View Model for the Home View. + */ +public class WatchListViewModel { + private List stockList; + + public WatchListViewModel() { + this.stockList = new ArrayList<>(); + } + + public List getStockList() { + return stockList; + } + + public void setStockList(List stockList) { + this.stockList = stockList; + } + +} diff --git a/src/main/java/use_case/wathlist_view/WatchListInputBoundary.java b/src/main/java/use_case/wathlist_view/WatchListInputBoundary.java new file mode 100644 index 000000000..ef34dd760 --- /dev/null +++ b/src/main/java/use_case/wathlist_view/WatchListInputBoundary.java @@ -0,0 +1,15 @@ +package use_case.wathlist_view; + +public interface WatchListInputBoundary { + + /** + * Executes the search use case. + * @param searchInputData the input data + */ + void search(WatchListInputData searchInputData); + + /** + * Executes the switch to login view use case. + */ + void switchToLoginView(); +} diff --git a/src/main/java/use_case/wathlist_view/WatchListInputData.java b/src/main/java/use_case/wathlist_view/WatchListInputData.java new file mode 100644 index 000000000..93984da9a --- /dev/null +++ b/src/main/java/use_case/wathlist_view/WatchListInputData.java @@ -0,0 +1,17 @@ +package use_case.wathlist_view; + +/** + * The Input Data for the Search Use Case. + */ +public class WatchListInputData { + + private final String stockSymbol; + + public WatchListInputData(String stockSymbol) { + this.stockSymbol = stockSymbol; + } + + public String getStockSymbol() { + return stockSymbol; + } +} diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java new file mode 100644 index 000000000..e9f6d38b5 --- /dev/null +++ b/src/main/java/view/WatchListView.java @@ -0,0 +1,62 @@ +package view; + +import interface_adapter.watchlist_view.WatchListViewModel; + +import javax.swing.*; +import java.awt.*; + +public class WatchListView extends JPanel { + + public WatchListView(WatchListViewModel viewModel) { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setBackground(Color.WHITE); + + addStockItem("AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09", "+24.10%"); + addStockItem("COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); + addStockItem("QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); + } + + private void addStockItem(String code, String price, String dailyChange, String dailyPercentage, String totalChange, String totalPercentage) { + JPanel stockPanel = new JPanel(new BorderLayout()); + stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLUE)); + stockPanel.setBackground(Color.WHITE); + + // Left part: Stock code and price + JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); + leftPanel.setBackground(Color.WHITE); + + JLabel stockCodeLabel = new JLabel(code); + stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + JLabel stockPriceLabel = new JLabel(price); + stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + + leftPanel.add(stockCodeLabel); + leftPanel.add(stockPriceLabel); + + // Right part: up and down information + JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.setBackground(Color.WHITE); + rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); + + JLabel dailyChangeLabel = new JLabel(dailyChange + "(" + dailyPercentage + ")"); + dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); + totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + + rightPanel.add(dailyChangeLabel); + rightPanel.add(totalChangeLabel); + + // Add all to stockPanel + stockPanel.add(leftPanel, BorderLayout.WEST); + stockPanel.add(rightPanel, BorderLayout.EAST); + + // Add all information + add(stockPanel); + } + + public String getViewName() { + return "WatchListView"; + } +} From e575cc48520b56b731d65d07653199dfb9350dec Mon Sep 17 00:00:00 2001 From: misaka Date: Fri, 8 Nov 2024 14:13:09 -0500 Subject: [PATCH 010/113] Deleted unused codes --- .idea/codeStyles/Project.xml | 7 +++++++ .idea/codeStyles/codeStyleConfig.xml | 5 +++++ .idea/compiler.xml | 13 +++++++++++++ .idea/encodings.xml | 7 +++++++ .idea/material_theme_project_new.xml | 13 +++++++++++++ .idea/misc.xml | 12 ++++++++++++ .idea/vcs.xml | 6 ++++++ homework-5.iml | 6 ++++++ .../watchlist_view/WatchListController.java | 16 ---------------- .../wathlist_view/WatchListInputBoundary.java | 15 --------------- .../wathlist_view/WatchListInputData.java | 17 ----------------- 11 files changed, 69 insertions(+), 48 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/material_theme_project_new.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 homework-5.iml delete mode 100644 src/main/java/interface_adapter/watchlist_view/WatchListController.java delete mode 100644 src/main/java/use_case/wathlist_view/WatchListInputBoundary.java delete mode 100644 src/main/java/use_case/wathlist_view/WatchListInputData.java diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..919ce1f1f --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..a55e7a179 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 000000000..69d41be32 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..aa00ffab7 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 000000000..16918dd4e --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..f0f82878c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/homework-5.iml b/homework-5.iml new file mode 100644 index 000000000..9e3449c9d --- /dev/null +++ b/homework-5.iml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListController.java b/src/main/java/interface_adapter/watchlist_view/WatchListController.java deleted file mode 100644 index 36e28389b..000000000 --- a/src/main/java/interface_adapter/watchlist_view/WatchListController.java +++ /dev/null @@ -1,16 +0,0 @@ -package interface_adapter.watchlist_view; - -import use_case.wathlist_view.WatchListInputBoundary; -import use_case.wathlist_view.WatchListInputData; - -/** - * The controller for the Login Use Case. - */ -public class WatchListController { - - private final WatchListInputBoundary WatchListUseCaseInteractor; - - public WatchListController(WatchListInputBoundary WatchListInteractor, WatchListInputBoundary watchListUseCaseInteractor) { - this.WatchListUseCaseInteractor = watchListUseCaseInteractor; - } -} diff --git a/src/main/java/use_case/wathlist_view/WatchListInputBoundary.java b/src/main/java/use_case/wathlist_view/WatchListInputBoundary.java deleted file mode 100644 index ef34dd760..000000000 --- a/src/main/java/use_case/wathlist_view/WatchListInputBoundary.java +++ /dev/null @@ -1,15 +0,0 @@ -package use_case.wathlist_view; - -public interface WatchListInputBoundary { - - /** - * Executes the search use case. - * @param searchInputData the input data - */ - void search(WatchListInputData searchInputData); - - /** - * Executes the switch to login view use case. - */ - void switchToLoginView(); -} diff --git a/src/main/java/use_case/wathlist_view/WatchListInputData.java b/src/main/java/use_case/wathlist_view/WatchListInputData.java deleted file mode 100644 index 93984da9a..000000000 --- a/src/main/java/use_case/wathlist_view/WatchListInputData.java +++ /dev/null @@ -1,17 +0,0 @@ -package use_case.wathlist_view; - -/** - * The Input Data for the Search Use Case. - */ -public class WatchListInputData { - - private final String stockSymbol; - - public WatchListInputData(String stockSymbol) { - this.stockSymbol = stockSymbol; - } - - public String getStockSymbol() { - return stockSymbol; - } -} From afbd82b5854e5e7b9c051744f21530c491eab156 Mon Sep 17 00:00:00 2001 From: misaka Date: Fri, 8 Nov 2024 17:11:28 -0500 Subject: [PATCH 011/113] Add the return button to home page for WatchList --- src/main/java/app/AppBuilder.java | 2 +- src/main/java/view/WatchListView.java | 68 +++++++++++++++++++++------ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index d6175876b..aa7c64357 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -127,7 +127,7 @@ public AppBuilder addLoggedInView() { public AppBuilder addWatchListView() { watchListViewModel = new WatchListViewModel(); - watchListView = new WatchListView(watchListViewModel); + watchListView = new WatchListView(watchListViewModel,viewManagerModel); cardPanel.add(watchListView, watchListView.getViewName()); return this; } diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index e9f6d38b5..53a7bdd5d 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -1,48 +1,85 @@ package view; +import interface_adapter.ViewManagerModel; import interface_adapter.watchlist_view.WatchListViewModel; import javax.swing.*; import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + public class WatchListView extends JPanel { - public WatchListView(WatchListViewModel viewModel) { - setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + private final ViewManagerModel viewManagerModel; + + public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel) { + this.viewManagerModel = viewManagerModel; + setLayout(new BorderLayout()); setBackground(Color.WHITE); - addStockItem("AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09", "+24.10%"); - addStockItem("COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); - addStockItem("QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); + // return button + JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + topPanel.setBackground(Color.WHITE); + + // create return button + JButton backButton = new JButton("←"); + backButton.setFont(new Font("SansSerif", Font.PLAIN, 20)); + backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + backButton.setFocusPainted(false); + backButton.setContentAreaFilled(false); + topPanel.add(backButton, BorderLayout.WEST); + + // Listener for return button to home page + backButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); + } + }); + + add(topPanel, BorderLayout.NORTH); + + // stock information + JPanel contentPanel = new JPanel(); + contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); + contentPanel.setBackground(Color.WHITE); + + addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09", "+24.10%"); + addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); + addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); + + add(contentPanel, BorderLayout.CENTER); } - private void addStockItem(String code, String price, String dailyChange, String dailyPercentage, String totalChange, String totalPercentage) { - JPanel stockPanel = new JPanel(new BorderLayout()); - stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLUE)); + private void addStockItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage, String totalChange, String totalPercentage) { + final JPanel stockPanel = new JPanel(new BorderLayout()); + stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); stockPanel.setBackground(Color.WHITE); // Left part: Stock code and price - JPanel leftPanel = new JPanel(); + final JPanel leftPanel = new JPanel(); leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); leftPanel.setBackground(Color.WHITE); - JLabel stockCodeLabel = new JLabel(code); + final JLabel stockCodeLabel = new JLabel(code); stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); - JLabel stockPriceLabel = new JLabel(price); + final JLabel stockPriceLabel = new JLabel(price); stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); leftPanel.add(stockCodeLabel); leftPanel.add(stockPriceLabel); // Right part: up and down information - JPanel rightPanel = new JPanel(); + final JPanel rightPanel = new JPanel(); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); rightPanel.setBackground(Color.WHITE); rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); - JLabel dailyChangeLabel = new JLabel(dailyChange + "(" + dailyPercentage + ")"); + final JLabel dailyChangeLabel = new JLabel(dailyChange + "(" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); + final JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); rightPanel.add(dailyChangeLabel); @@ -53,10 +90,11 @@ private void addStockItem(String code, String price, String dailyChange, String stockPanel.add(rightPanel, BorderLayout.EAST); // Add all information - add(stockPanel); + contentPanel.add(stockPanel); } public String getViewName() { return "WatchListView"; } } + From 3610af5677b7eb82f2c6b2a09ffd1f95ce9738fd Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 10 Nov 2024 16:04:21 -0500 Subject: [PATCH 012/113] Logout Feature --- .../java/use_case/logout/LogoutInputData.java | 7 ++++++- .../java/use_case/logout/LogoutInteractor.java | 15 ++++++++------- .../java/use_case/logout/LogoutOutputData.java | 5 ++++- src/main/java/view/LoggedInView.java | 8 ++++---- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/java/use_case/logout/LogoutInputData.java b/src/main/java/use_case/logout/LogoutInputData.java index 56a33b375..47988d20a 100644 --- a/src/main/java/use_case/logout/LogoutInputData.java +++ b/src/main/java/use_case/logout/LogoutInputData.java @@ -5,8 +5,13 @@ */ public class LogoutInputData { + private final String username; + public LogoutInputData(String username) { - // TODO: save the current username in an instance variable and add a getter. + this.username = username; } + String getUsername() { + return username; + } } diff --git a/src/main/java/use_case/logout/LogoutInteractor.java b/src/main/java/use_case/logout/LogoutInteractor.java index 1ca93b44e..2a92394be 100644 --- a/src/main/java/use_case/logout/LogoutInteractor.java +++ b/src/main/java/use_case/logout/LogoutInteractor.java @@ -9,17 +9,18 @@ public class LogoutInteractor implements LogoutInputBoundary { public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface, LogoutOutputBoundary logoutOutputBoundary) { - // TODO: save the DAO and Presenter in the instance variables. - // Which parameter is the DAO and which is the presenter? + + this.userDataAccessObject = userDataAccessInterface; + this.logoutPresenter = logoutOutputBoundary; } @Override public void execute(LogoutInputData logoutInputData) { - // TODO: implement the logic of the Logout Use Case (depends on the LogoutInputData.java TODO) - // * get the username out of the input data, - // * set the username to null in the DAO - // * instantiate the `LogoutOutputData`, which needs to contain the username. - // * tell the presenter to prepare a success view. + + final String username = logoutInputData.getUsername(); + userDataAccessObject.setCurrentUsername(null); + final LogoutOutputData logoutOutputData = new LogoutOutputData(username, false); + logoutPresenter.prepareSuccessView(logoutOutputData); } } diff --git a/src/main/java/use_case/logout/LogoutOutputData.java b/src/main/java/use_case/logout/LogoutOutputData.java index 974279155..e4e678ffa 100644 --- a/src/main/java/use_case/logout/LogoutOutputData.java +++ b/src/main/java/use_case/logout/LogoutOutputData.java @@ -9,7 +9,9 @@ public class LogoutOutputData { private boolean useCaseFailed; public LogoutOutputData(String username, boolean useCaseFailed) { - // TODO: save the parameters in the instance variables. + + this.username = username; + this.useCaseFailed = useCaseFailed; } public String getUsername() { @@ -20,3 +22,4 @@ public boolean isUseCaseFailed() { return useCaseFailed; } } + diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java index 76d40c0f1..63a27fa09 100644 --- a/src/main/java/view/LoggedInView.java +++ b/src/main/java/view/LoggedInView.java @@ -100,9 +100,8 @@ public void changedUpdate(DocumentEvent e) { // This creates an anonymous subclass of ActionListener and instantiates it. evt -> { if (evt.getSource().equals(logOut)) { - // TODO: execute the logout use case through the Controller - // 1. get the state out of the loggedInViewModel. It contains the username. - // 2. Execute the logout Controller. + final LoggedInState currentState1 = loggedInViewModel.getState(); + this.logoutController.execute(currentState1.getUsername()); } } ); @@ -138,6 +137,7 @@ public void setChangePasswordController(ChangePasswordController changePasswordC } public void setLogoutController(LogoutController logoutController) { - // TODO: save the logout controller in the instance variable. + + this.logoutController = logoutController; } } From cbf53f5d32d2a5af0dcfd37c1affa889b27cb626 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 10 Nov 2024 18:33:54 -0500 Subject: [PATCH 013/113] Adjust UI of homepage(Especially sign up). --- src/main/java/app/AppBuilder.java | 2 +- .../interface_adapter/home_view/HomeController.java | 7 +++++++ .../interface_adapter/home_view/HomePresenter.java | 10 ++++++++++ .../java/use_case/home_view/HomeInputBoundary.java | 5 +++++ .../java/use_case/home_view/HomeInteractor.java | 5 +++++ .../java/use_case/home_view/HomeOutputBoundary.java | 5 +++++ src/main/java/view/HomeView.java | 13 +++++++++++++ 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index aa7c64357..34426adaa 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -138,7 +138,7 @@ public AppBuilder addWatchListView() { */ public AppBuilder addHomeUseCase() { final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, - loginViewModel, viewManagerModel); + loginViewModel, signupViewModel, viewManagerModel); final HomeInputBoundary homeInteractor = new HomeInteractor(userDataAccessObject, homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); diff --git a/src/main/java/interface_adapter/home_view/HomeController.java b/src/main/java/interface_adapter/home_view/HomeController.java index 507006d15..adb253b45 100644 --- a/src/main/java/interface_adapter/home_view/HomeController.java +++ b/src/main/java/interface_adapter/home_view/HomeController.java @@ -43,4 +43,11 @@ public void switchToWatchList() { public void switchToLoginView() { homeUseCaseInteractor.switchToLoginView(); } + + /** + * Executes the "switch to SignupView" Use Case. + */ + public void switchToSignupView() { + homeUseCaseInteractor.switchToSignupView(); + } } diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index fc807a5df..e412ad147 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -2,6 +2,7 @@ import interface_adapter.ViewManagerModel; import interface_adapter.login.LoginViewModel; +import interface_adapter.signup.SignupViewModel; import use_case.home_view.HomeOutputBoundary; import use_case.home_view.HomeOutputData; import view.WatchListView; @@ -13,15 +14,18 @@ public class HomePresenter implements HomeOutputBoundary { private final HomeViewModel homeViewModel; private final LoginViewModel loginViewModel; + private final SignupViewModel signupViewModel; private final ViewManagerModel viewManagerModel; private static final String WATCHLIST_VIEW_NAME = "WatchListView"; public HomePresenter(HomeViewModel homeViewModel, LoginViewModel loginViewModel, + SignupViewModel signupViewModel, ViewManagerModel viewManagerModel) { this.homeViewModel = homeViewModel; this.loginViewModel = loginViewModel; + this.signupViewModel = signupViewModel; this.viewManagerModel = viewManagerModel; } @@ -53,4 +57,10 @@ public void switchToLoginView() { viewManagerModel.setState(loginViewModel.getViewName()); viewManagerModel.firePropertyChanged(); } + + @Override + public void switchToSignupView() { + viewManagerModel.setState(loginViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } } diff --git a/src/main/java/use_case/home_view/HomeInputBoundary.java b/src/main/java/use_case/home_view/HomeInputBoundary.java index 8b1be2c16..99a691f1d 100644 --- a/src/main/java/use_case/home_view/HomeInputBoundary.java +++ b/src/main/java/use_case/home_view/HomeInputBoundary.java @@ -22,4 +22,9 @@ public interface HomeInputBoundary { * Executes the switch to login view use case. */ void switchToLoginView(); + + /** + * Executes the switch to Signup view use case. + */ + void switchToSignupView(); } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 3259dba11..67ed0299f 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -39,4 +39,9 @@ public void switchToWatchList() { public void switchToLoginView() { homePresenter.switchToLoginView(); } + + @Override + public void switchToSignupView() { + homePresenter.switchToSignupView(); + } } diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index 61cdc0516..b043feab7 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -31,4 +31,9 @@ public interface HomeOutputBoundary { * Switches to the Login View. */ void switchToLoginView(); + + /** + * Switches to the Signup View. + */ + void switchToSignupView(); } diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index b09410741..b1574ca32 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -45,6 +45,7 @@ public class HomeView extends JPanel implements ActionListener, PropertyChangeLi // Login/Logout component private final JButton loginButton = new JButton("Login"); + private final JButton signupButton = new JButton("Sign up"); public HomeView(HomeViewModel homeViewModel) { this.homeViewModel = homeViewModel; @@ -99,6 +100,17 @@ public void actionPerformed(ActionEvent evt) { } }); + // Config signup components style + signupButton.setAlignmentX(Component.CENTER_ALIGNMENT); + final JPanel signupPanel = new JPanel(); + signupPanel.setLayout(new BoxLayout(signupPanel, BoxLayout.LINE_AXIS)); + signupPanel.add(signupButton); + signupButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + homeController.switchToSignupView(); + } + }); + // Config frame style this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); @@ -108,6 +120,7 @@ public void actionPerformed(ActionEvent evt) { this.add(portfolioPanel); this.add(watchListPanel); this.add(loginPanel); + this.add(signupPanel); } /** From f247a436845734760a55f903f2ade5f700e880f9 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 10 Nov 2024 18:46:13 -0500 Subject: [PATCH 014/113] Improve the logout function. --- src/main/java/app/Main.java | 2 +- .../logout/LogoutController.java | 10 ++++--- .../logout/LogoutPresenter.java | 30 +++++++++++-------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 6c513b18f..6bfcf1026 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -12,7 +12,6 @@ public class Main { */ public static void main(String[] args) { final AppBuilder appBuilder = new AppBuilder(); - // TODO: add the Logout Use Case to the app using the appBuilder final JFrame application = appBuilder .addHomeView() .addWatchListView() @@ -22,6 +21,7 @@ public static void main(String[] args) { .addHomeUseCase() .addSignupUseCase() .addLoginUseCase() + .addLogoutUseCase() .addChangePasswordUseCase() .build(); diff --git a/src/main/java/interface_adapter/logout/LogoutController.java b/src/main/java/interface_adapter/logout/LogoutController.java index e184a3bba..6099df67f 100644 --- a/src/main/java/interface_adapter/logout/LogoutController.java +++ b/src/main/java/interface_adapter/logout/LogoutController.java @@ -1,6 +1,7 @@ package interface_adapter.logout; import use_case.logout.LogoutInputBoundary; +import use_case.logout.LogoutInputData; /** * The controller for the Logout Use Case. @@ -10,7 +11,8 @@ public class LogoutController { private LogoutInputBoundary logoutUseCaseInteractor; public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) { - // TODO: Save the interactor in the instance variable. + + this.logoutUseCaseInteractor = logoutUseCaseInteractor; } /** @@ -18,8 +20,8 @@ public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) { * @param username the username of the user logging in */ public void execute(String username) { - // TODO: run the use case interactor for the logout use case - // 1. instantiate the `LogoutInputData`, which should contain the username. - // 2. tell the Interactor to execute. + + final LogoutInputData logoutInputData = new LogoutInputData(username); + logoutUseCaseInteractor.execute(logoutInputData); } } diff --git a/src/main/java/interface_adapter/logout/LogoutPresenter.java b/src/main/java/interface_adapter/logout/LogoutPresenter.java index 78ef306a1..2c393fd38 100644 --- a/src/main/java/interface_adapter/logout/LogoutPresenter.java +++ b/src/main/java/interface_adapter/logout/LogoutPresenter.java @@ -1,7 +1,9 @@ package interface_adapter.logout; import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; +import interface_adapter.login.LoginState; import interface_adapter.login.LoginViewModel; import use_case.logout.LogoutOutputBoundary; import use_case.logout.LogoutOutputData; @@ -16,9 +18,12 @@ public class LogoutPresenter implements LogoutOutputBoundary { private LoginViewModel loginViewModel; public LogoutPresenter(ViewManagerModel viewManagerModel, - LoggedInViewModel loggedInViewModel, + LoggedInViewModel loggedInViewModel, LoginViewModel loginViewModel) { - // TODO: assign to the three instance variables. + + this.loggedInViewModel = loggedInViewModel; + this.viewManagerModel = viewManagerModel; + this.loginViewModel = loginViewModel; } @Override @@ -29,17 +34,16 @@ public void prepareSuccessView(LogoutOutputData response) { // We also need to set the username in the LoggedInState to // the empty string. - // TODO: have prepareSuccessView update the LoggedInState - // 1. get the LoggedInState out of the appropriate View Model, - // 2. set the username in the state to the empty string - // 3. set the state in the LoggedInViewModel to the updated state - // 4. firePropertyChanged so that the View that is listening is updated. - - // TODO: have prepareSuccessView update the LoginState - // 5. get the LoginState out of the appropriate View Model, - // 6. set the username and password in the state to the empty string - // 7. set the state in the LoginViewModel to the updated state - // 8. firePropertyChanged so that the View that is listening is updated. + final LoggedInState loggedInState = loggedInViewModel.getState(); + loggedInState.setUsername(""); + this.loggedInViewModel.setState(loggedInState); + this.loggedInViewModel.firePropertyChanged(); + + final LoginState loginState = loginViewModel.getState(); + loginState.setUsername(""); + loginState.setPassword(""); + this.loginViewModel.setState(loginState); + this.loginViewModel.firePropertyChanged(); // This code tells the View Manager to switch to the LoginView. this.viewManagerModel.setState(loginViewModel.getViewName()); From 6a192d7ce579b44e2726c8eaeabd6d8891f37eb3 Mon Sep 17 00:00:00 2001 From: misaka Date: Sun, 10 Nov 2024 19:15:23 -0500 Subject: [PATCH 015/113] Add cancel button for signup and login --- src/main/java/app/AppBuilder.java | 4 ++-- .../watchlist_view/WatchListViewModel.java | 2 +- src/main/java/view/LoginView.java | 14 +++++++++++--- src/main/java/view/SignupView.java | 13 +++++++++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 34426adaa..9f139a5d1 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -98,7 +98,7 @@ public AppBuilder addHomeView() { */ public AppBuilder addSignupView() { signupViewModel = new SignupViewModel(); - signupView = new SignupView(signupViewModel); + signupView = new SignupView(signupViewModel,viewManagerModel); cardPanel.add(signupView, signupView.getViewName()); return this; } @@ -109,7 +109,7 @@ public AppBuilder addSignupView() { */ public AppBuilder addLoginView() { loginViewModel = new LoginViewModel(); - loginView = new LoginView(loginViewModel); + loginView = new LoginView(loginViewModel, viewManagerModel); cardPanel.add(loginView, loginView.getViewName()); return this; } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java index 77e525022..7caf274fc 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java @@ -4,7 +4,7 @@ import java.util.List; /** - * The View Model for the Home View. + * The View Model for the WatchList View. */ public class WatchListViewModel { private List stockList; diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index 4d0c271c3..79d5b1b14 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -15,6 +15,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import interface_adapter.ViewManagerModel; import interface_adapter.login.LoginController; import interface_adapter.login.LoginState; import interface_adapter.login.LoginViewModel; @@ -35,10 +36,11 @@ public class LoginView extends JPanel implements ActionListener, PropertyChangeL private final JButton logIn; private final JButton cancel; + private final ViewManagerModel viewManagerModel; private LoginController loginController; - public LoginView(LoginViewModel loginViewModel) { - + public LoginView(LoginViewModel loginViewModel, ViewManagerModel viewManagerModel) { + this.viewManagerModel = viewManagerModel; this.loginViewModel = loginViewModel; this.loginViewModel.addPropertyChangeListener(this); @@ -72,7 +74,13 @@ public void actionPerformed(ActionEvent evt) { } ); - cancel.addActionListener(this); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); + } + }); usernameInputField.getDocument().addDocumentListener(new DocumentListener() { diff --git a/src/main/java/view/SignupView.java b/src/main/java/view/SignupView.java index f98570d62..9641a3c08 100644 --- a/src/main/java/view/SignupView.java +++ b/src/main/java/view/SignupView.java @@ -16,6 +16,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import interface_adapter.ViewManagerModel; import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupState; import interface_adapter.signup.SignupViewModel; @@ -30,13 +31,15 @@ public class SignupView extends JPanel implements ActionListener, PropertyChange private final JTextField usernameInputField = new JTextField(15); private final JPasswordField passwordInputField = new JPasswordField(15); private final JPasswordField repeatPasswordInputField = new JPasswordField(15); + private final ViewManagerModel viewManagerModel; private SignupController signupController; private final JButton signUp; private final JButton cancel; private final JButton toLogin; - public SignupView(SignupViewModel signupViewModel) { + public SignupView(SignupViewModel signupViewModel, ViewManagerModel viewManagerModel) { + this.viewManagerModel = viewManagerModel; this.signupViewModel = signupViewModel; signupViewModel.addPropertyChangeListener(this); @@ -83,7 +86,13 @@ public void actionPerformed(ActionEvent evt) { } ); - cancel.addActionListener(this); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); + } + }); addUsernameListener(); addPasswordListener(); From 0851346bcd8c244c02596bfab8e697378bacf3b6 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Mon, 11 Nov 2024 10:01:14 -0500 Subject: [PATCH 016/113] Add AbstractViewWithBackButton view --- .../java/view/AbstractViewWithBackButton.java | 37 +++++++++++++++++++ src/main/java/view/HomeView.java | 7 +++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/main/java/view/AbstractViewWithBackButton.java diff --git a/src/main/java/view/AbstractViewWithBackButton.java b/src/main/java/view/AbstractViewWithBackButton.java new file mode 100644 index 000000000..555144067 --- /dev/null +++ b/src/main/java/view/AbstractViewWithBackButton.java @@ -0,0 +1,37 @@ +package view; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * The Parent View for the all views that need back button. + */ + +abstract class AbstractViewWithBackButton extends JPanel { + private final JButton backButton = new JButton("Back"); + + AbstractViewWithBackButton() { + // Configure back button + final JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + backButton.setFont(new Font("SansSerif", Font.PLAIN, 20)); + backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + backButton.setFocusPainted(false); + backButton.setContentAreaFilled(false); + topPanel.add(backButton, BorderLayout.WEST); + + backButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Call the abstract method to be implemented by subclasses + backButtonAction(); + } + }); + + // Add panel(s) + this.add(topPanel); + } + + abstract void backButtonAction(); +} diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index b1574ca32..8f2529a2c 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -14,7 +14,7 @@ /** * The View for the Home Page. */ -public class HomeView extends JPanel implements ActionListener, PropertyChangeListener { +public class HomeView extends AbstractViewWithBackButton implements ActionListener, PropertyChangeListener { private static final String RIGHTARROW = "->"; @@ -136,6 +136,11 @@ public void propertyChange(PropertyChangeEvent evt) { } + @Override + void backButtonAction() { + System.out.println("Back button clicked"); + } + public String getViewName() { return viewName; } From 9761d0fce8198d911101cd9814e994274b431d3e Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 11 Nov 2024 12:46:10 -0500 Subject: [PATCH 017/113] first demo --- src/main/java/view/StockView.java | 89 +++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/main/java/view/StockView.java diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java new file mode 100644 index 000000000..40b1d23f2 --- /dev/null +++ b/src/main/java/view/StockView.java @@ -0,0 +1,89 @@ +package view; + +import entity.Stock; +import interface_adapter.ViewManagerModel; +import javax.swing.*; +import java.awt.*; + +/** + * The View for displaying detailed information about a single stock. + */ +public class StockView extends AbstractViewWithBackButton { + + private final ViewManagerModel viewManagerModel; + private JLabel stockSymbolLabel; + private JLabel stockPriceLabel; + private JLabel openPriceLabel; + private JLabel closePriceLabel; + private JLabel lowPriceLabel; + private JLabel highPriceLabel; + private JLabel volumeLabel; + + public StockView(Stock stock, ViewManagerModel viewManagerModel) { + this.viewManagerModel = viewManagerModel; + setLayout(new BorderLayout()); + setBackground(Color.WHITE); + + // 创建内容面板并添加股票信息 + JPanel contentPanel = new JPanel(); + contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); + contentPanel.setBackground(Color.WHITE); + + // 初始化组件并更新股票信息 + initializeComponents(contentPanel); + updateStock(stock); + + add(contentPanel, BorderLayout.CENTER); + } + + private void initializeComponents(JPanel contentPanel) { + + stockSymbolLabel = createLabel("", 24, Font.BOLD); + stockPriceLabel = createLabel("", 18, Font.PLAIN); + + JPanel stockInfoPanel = new JPanel(); + stockInfoPanel.setLayout(new BoxLayout(stockInfoPanel, BoxLayout.Y_AXIS)); + stockInfoPanel.setBackground(Color.WHITE); + stockInfoPanel.add(stockSymbolLabel); + stockInfoPanel.add(stockPriceLabel); + + contentPanel.add(stockInfoPanel); + contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); + + // 详细信息标签 + openPriceLabel = createLabel("", 16, Font.PLAIN); + closePriceLabel = createLabel("", 16, Font.PLAIN); + lowPriceLabel = createLabel("", 16, Font.PLAIN); + highPriceLabel = createLabel("", 16, Font.PLAIN); + volumeLabel = createLabel("", 16, Font.PLAIN); + + contentPanel.add(openPriceLabel); + contentPanel.add(closePriceLabel); + contentPanel.add(lowPriceLabel); + contentPanel.add(highPriceLabel); + contentPanel.add(volumeLabel); + } + + public void updateStock(Stock stock) { + stockSymbolLabel.setText(stock.getSymbol()); + stockPriceLabel.setText("Current Price: $" + stock.getClosePrice()); + openPriceLabel.setText("Open Price: $" + stock.getOpenPrice()); + closePriceLabel.setText("Close Price: $" + stock.getClosePrice()); + lowPriceLabel.setText("Low: $" + stock.getLow()); + highPriceLabel.setText("High: $" + stock.getHigh()); + volumeLabel.setText("Volume: " + stock.getVolume() + " M"); + } + + private JLabel createLabel(String text, int fontSize, int fontStyle) { + JLabel label = new JLabel(text); + label.setFont(new Font("SansSerif", fontStyle, fontSize)); + label.setAlignmentX(Component.LEFT_ALIGNMENT); + return label; + } + + @Override + void backButtonAction() { + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); + } +} \ No newline at end of file From 04ba0eba525ea6e149d61ef51ad39ca6fef871f8 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 11 Nov 2024 12:51:42 -0500 Subject: [PATCH 018/113] part of stock view done --- .idea/misc.xml | 3 +-- src/main/java/app/AppBuilder.java | 1 + .../java/interface_adapter/home_view/HomePresenter.java | 9 +++++++-- src/main/java/use_case/home_view/HomeInputBoundary.java | 1 + src/main/java/use_case/home_view/HomeInteractor.java | 1 + src/main/java/use_case/home_view/HomeOutputBoundary.java | 1 + 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index f6a5e8a7f..f0f82878c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,6 +8,5 @@ - ->>>>>>> ac4333e79be2fda7912a17e0b6ff5d3db6737ace + \ No newline at end of file diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 9f139a5d1..8825e27cc 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -74,6 +74,7 @@ public class AppBuilder { private LoggedInViewModel loggedInViewModel; private LoggedInView loggedInView; private LoginView loginView; + private StockView stockView; private WatchListView watchListView; private WatchListViewModel watchListViewModel; diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index e412ad147..e3ee5b8a2 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -31,8 +31,8 @@ public HomePresenter(HomeViewModel homeViewModel, @Override public void prepareSuccessView(HomeOutputData searchOutputData) { - // Present stock view - System.out.println("Show stock view after searching stock"); + viewManagerModel.setState("StockView"); + viewManagerModel.firePropertyChanged(); } @Override @@ -63,4 +63,9 @@ public void switchToSignupView() { viewManagerModel.setState(loginViewModel.getViewName()); viewManagerModel.firePropertyChanged(); } + + public void switchToStockView() { + viewManagerModel.setState("StockView"); + viewManagerModel.firePropertyChanged(); + } } diff --git a/src/main/java/use_case/home_view/HomeInputBoundary.java b/src/main/java/use_case/home_view/HomeInputBoundary.java index 99a691f1d..9542715c7 100644 --- a/src/main/java/use_case/home_view/HomeInputBoundary.java +++ b/src/main/java/use_case/home_view/HomeInputBoundary.java @@ -27,4 +27,5 @@ public interface HomeInputBoundary { * Executes the switch to Signup view use case. */ void switchToSignupView(); + } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 67ed0299f..5ff0d0351 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -44,4 +44,5 @@ public void switchToLoginView() { public void switchToSignupView() { homePresenter.switchToSignupView(); } + } diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index b043feab7..dab5455c4 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -36,4 +36,5 @@ public interface HomeOutputBoundary { * Switches to the Signup View. */ void switchToSignupView(); + } From d7913975a57d9160c416167e86874fca86a48b2d Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 13 Nov 2024 19:04:55 -0500 Subject: [PATCH 019/113] first demo --- src/main/java/view/StockView.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 40b1d23f2..7db4bd1bc 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -24,12 +24,10 @@ public StockView(Stock stock, ViewManagerModel viewManagerModel) { setLayout(new BorderLayout()); setBackground(Color.WHITE); - // 创建内容面板并添加股票信息 JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); - // 初始化组件并更新股票信息 initializeComponents(contentPanel); updateStock(stock); @@ -50,7 +48,6 @@ private void initializeComponents(JPanel contentPanel) { contentPanel.add(stockInfoPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - // 详细信息标签 openPriceLabel = createLabel("", 16, Font.PLAIN); closePriceLabel = createLabel("", 16, Font.PLAIN); lowPriceLabel = createLabel("", 16, Font.PLAIN); @@ -79,6 +76,7 @@ private JLabel createLabel(String text, int fontSize, int fontStyle) { label.setFont(new Font("SansSerif", fontStyle, fontSize)); label.setAlignmentX(Component.LEFT_ALIGNMENT); return label; + } @Override From 319a8b37b2573ac178ab98fbc4905b75e2522870 Mon Sep 17 00:00:00 2001 From: misaka Date: Thu, 14 Nov 2024 16:38:46 -0500 Subject: [PATCH 020/113] Finish GetStock method to get today stock information. source from api.marketstack.com --- src/main/java/app/AppBuilder.java | 8 ++++-- .../data_access/DBUserDataAccessObject.java | 26 ++++++++++++++++++- src/main/java/entity/StockFactory.java | 2 +- .../watchlist/WatchListInputData.java | 16 ++++++++++++ src/main/java/view/WatchListView.java | 6 ++--- 5 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 src/main/java/use_case/watchlist/WatchListInputData.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 9f139a5d1..a9a033d41 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -119,15 +119,19 @@ public AppBuilder addLoginView() { * @return this builder */ public AppBuilder addLoggedInView() { - loggedInViewModel = new LoggedInViewModel(); + loggedInViewModel = new LoggedInViewModel(); loggedInView = new LoggedInView(loggedInViewModel); cardPanel.add(loggedInView, loggedInView.getViewName()); return this; } + /** + * Adds the WatchList View to the application. + * @return this builder + */ public AppBuilder addWatchListView() { watchListViewModel = new WatchListViewModel(); - watchListView = new WatchListView(watchListViewModel,viewManagerModel); + watchListView = new WatchListView(watchListViewModel, viewManagerModel); cardPanel.add(watchListView, watchListView.getViewName()); return this; } diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index b79b02755..31a78e5c5 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -4,6 +4,7 @@ import entity.Stock; import entity.StockFactory; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -173,7 +174,30 @@ public void changePassword(User user) { @Override public Stock getStock(String symbol) { - return stockFactory.create("NVDA", OPEN, CLOSE, VOLUME, HIGH, LOW); + final OkHttpClient client = new OkHttpClient().newBuilder().build(); + final Request request = new Request.Builder() + .url(String.format("https://api.marketstack.com/v1/eod?access_key=3847d86b56ca461a0da759024332c06a&symbols=%s", symbol)) + .get() + .build(); + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new RuntimeException("Request Failed: " + response); + + final String responseBody = response.body().string(); + final JSONObject json = new JSONObject(responseBody); + final JSONArray dataArray = json.getJSONArray("data"); + final JSONObject stockData = dataArray.getJSONObject(0); + + final double open = stockData.getDouble("open"); + final double high = stockData.getDouble("high"); + final double low = stockData.getDouble("low"); + final double close = stockData.getDouble("close"); + final double volume = stockData.getDouble("volume"); + + return stockFactory.create(symbol, open, close, volume, high, low); + } + catch (IOException | JSONException ex) { + throw new RuntimeException(ex); + } } @Override diff --git a/src/main/java/entity/StockFactory.java b/src/main/java/entity/StockFactory.java index 2eb99b3af..61b5f1dc3 100644 --- a/src/main/java/entity/StockFactory.java +++ b/src/main/java/entity/StockFactory.java @@ -15,5 +15,5 @@ public interface StockFactory { * @param low the lowest price of the stock * @return the new user */ - Stock create(String symbol, int openPrice, int closePrice, int volume, int high, int low); + Stock create(String symbol, double openPrice, double closePrice, double volume, double high, double low); } diff --git a/src/main/java/use_case/watchlist/WatchListInputData.java b/src/main/java/use_case/watchlist/WatchListInputData.java new file mode 100644 index 000000000..4e621f1dc --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchListInputData.java @@ -0,0 +1,16 @@ +package use_case.watchlist; + +import java.util.ArrayList; + +/** + * The List of Stock code in watchlist. + */ + +public class WatchListInputData { + private ArrayList watchlist; + + public WatchListInputData() { + this.watchlist = new ArrayList(); + } + +} diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 53a7bdd5d..eb744ce1e 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -19,11 +19,11 @@ public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerM setBackground(Color.WHITE); // return button - JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + final JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); topPanel.setBackground(Color.WHITE); // create return button - JButton backButton = new JButton("←"); + final JButton backButton = new JButton("←"); backButton.setFont(new Font("SansSerif", Font.PLAIN, 20)); backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); backButton.setFocusPainted(false); @@ -42,7 +42,7 @@ public void actionPerformed(ActionEvent e) { add(topPanel, BorderLayout.NORTH); // stock information - JPanel contentPanel = new JPanel(); + final JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); From ff91b1f97440033939df69cf6ce2965f06f5f175 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Thu, 14 Nov 2024 21:04:21 -0500 Subject: [PATCH 021/113] Modify watchlist database --- .../data_access/DBUserDataAccessObject.java | 6 +++- .../data_access/FileUserDataAccessObject.java | 6 +++- .../InMemoryUserDataAccessObject.java | 28 +++++++++++++++++++ src/main/java/entity/CommonUser.java | 18 +++++++++++- src/main/java/entity/CommonUserFactory.java | 9 ++++-- src/main/java/entity/User.java | 14 ++++++++++ src/main/java/entity/UserFactory.java | 7 ++++- .../ChangePasswordInteractor.java | 6 +++- .../use_case/signup/SignupInteractor.java | 7 ++++- 9 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index b79b02755..341da5abd 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -1,6 +1,7 @@ package data_access; import java.io.IOException; +import java.util.ArrayList; import entity.Stock; import entity.StockFactory; @@ -68,8 +69,11 @@ public User get(String username) { final JSONObject userJSONObject = responseBody.getJSONObject("user"); final String name = userJSONObject.getString(USERNAME); final String password = userJSONObject.getString(PASSWORD); + final ArrayList emptyWatchList = new ArrayList<>(); + final ArrayList emptyStockList = new ArrayList<>(); + final User user = userFactory.create(name, password, emptyWatchList, emptyStockList); - return userFactory.create(name, password); + return user; } else { throw new RuntimeException(responseBody.getString(MESSAGE)); diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index d301a3241..8eb869dfe 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -6,10 +6,12 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import entity.Stock; import entity.User; import entity.UserFactory; import use_case.change_password.ChangePasswordUserDataAccessInterface; @@ -53,7 +55,9 @@ public FileUserDataAccessObject(String csvPath, UserFactory userFactory) throws final String[] col = row.split(","); final String username = String.valueOf(col[headers.get("username")]); final String password = String.valueOf(col[headers.get("password")]); - final User user = userFactory.create(username, password); + final ArrayList emptyWatchList = new ArrayList<>(); + final ArrayList emptyStockList = new ArrayList<>(); + final User user = userFactory.create(username, password, emptyWatchList, emptyStockList); accounts.put(username, user); } } diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java index 71f00862c..41583cacb 100644 --- a/src/main/java/data_access/InMemoryUserDataAccessObject.java +++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java @@ -1,8 +1,15 @@ package data_access; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import org.json.JSONObject; + +import entity.Stock; import entity.User; import use_case.change_password.ChangePasswordUserDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; @@ -19,6 +26,10 @@ public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterfa LogoutUserDataAccessInterface { private final Map users = new HashMap<>(); + private final ArrayList watchList = new ArrayList<>(); + private final ArrayList portfolioList = new ArrayList(); + + private final String watchListFilePath = "watchlist.txt"; private String currentUsername; @@ -52,4 +63,21 @@ public void setCurrentUsername(String name) { public String getCurrentUsername() { return this.currentUsername; } + + // Watch list related APIs + public void getWatchList() { +// // Using FileReader and BufferedReader to read the file line by line +// try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { +// String line; +// while ((line = reader.readLine()) != null) { +// System.out.println(line); +// } +// } +// catch (IOException e) { +// e.printStackTrace(); +// } + } + public void addToWatchList(String stock) { + watchList.add(stock); + } } diff --git a/src/main/java/entity/CommonUser.java b/src/main/java/entity/CommonUser.java index ba25fd20a..88b00e40d 100644 --- a/src/main/java/entity/CommonUser.java +++ b/src/main/java/entity/CommonUser.java @@ -1,5 +1,8 @@ package entity; +import java.lang.reflect.Array; +import java.util.ArrayList; + /** * A simple implementation of the User interface. */ @@ -7,10 +10,14 @@ public class CommonUser implements User { private final String name; private final String password; + private final ArrayList watchList; + private final ArrayList portfolioList; - public CommonUser(String name, String password) { + public CommonUser(String name, String password, ArrayList watchList, ArrayList portfolioList) { this.name = name; this.password = password; + this.watchList = watchList; + this.portfolioList = portfolioList; } @Override @@ -23,4 +30,13 @@ public String getPassword() { return password; } + @Override + public ArrayList getWatchList() { + return watchList; + } + + @Override + public ArrayList getPortfolioList() { + return portfolioList; + } } diff --git a/src/main/java/entity/CommonUserFactory.java b/src/main/java/entity/CommonUserFactory.java index ebede69e3..b363bdad4 100644 --- a/src/main/java/entity/CommonUserFactory.java +++ b/src/main/java/entity/CommonUserFactory.java @@ -1,12 +1,17 @@ package entity; +import java.util.ArrayList; + /** * Factory for creating CommonUser objects. */ public class CommonUserFactory implements UserFactory { @Override - public User create(String name, String password) { - return new CommonUser(name, password); + public User create(String name, + String password, + ArrayList watchList, + ArrayList portfolioList) { + return new CommonUser(name, password, watchList, portfolioList); } } diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java index 0ad073902..f51323df0 100644 --- a/src/main/java/entity/User.java +++ b/src/main/java/entity/User.java @@ -1,5 +1,7 @@ package entity; +import java.util.ArrayList; + /** * The representation of a user in our program. */ @@ -17,4 +19,16 @@ public interface User { */ String getPassword(); + /** + * Returns the watch list of the user. + * @return the watch list of the user. + */ + ArrayList getWatchList(); + + /** + * Returns the portfolio of the user. + * @return the portfolio of the user. + */ + ArrayList getPortfolioList(); + } diff --git a/src/main/java/entity/UserFactory.java b/src/main/java/entity/UserFactory.java index c7a508708..55445875e 100644 --- a/src/main/java/entity/UserFactory.java +++ b/src/main/java/entity/UserFactory.java @@ -1,5 +1,7 @@ package entity; +import java.util.ArrayList; + /** * Factory for creating users. */ @@ -8,8 +10,11 @@ public interface UserFactory { * Creates a new User. * @param name the name of the new user * @param password the password of the new user + * @param watchList the watch list of the new user + * @param portfolioList the portfolio list of the new user * @return the new user */ - User create(String name, String password); + User create(String name, String password, + ArrayList watchList, ArrayList portfolioList); } diff --git a/src/main/java/use_case/change_password/ChangePasswordInteractor.java b/src/main/java/use_case/change_password/ChangePasswordInteractor.java index df91196c1..03dabcd7d 100644 --- a/src/main/java/use_case/change_password/ChangePasswordInteractor.java +++ b/src/main/java/use_case/change_password/ChangePasswordInteractor.java @@ -3,6 +3,8 @@ import entity.User; import entity.UserFactory; +import java.util.ArrayList; + /** * The Change Password Interactor. */ @@ -22,7 +24,9 @@ public ChangePasswordInteractor(ChangePasswordUserDataAccessInterface changePass @Override public void execute(ChangePasswordInputData changePasswordInputData) { final User user = userFactory.create(changePasswordInputData.getUsername(), - changePasswordInputData.getPassword()); + changePasswordInputData.getPassword(), + new ArrayList<>(), + new ArrayList<>()); userDataAccessObject.changePassword(user); final ChangePasswordOutputData changePasswordOutputData = new ChangePasswordOutputData(user.getName(), diff --git a/src/main/java/use_case/signup/SignupInteractor.java b/src/main/java/use_case/signup/SignupInteractor.java index 3fd6560c7..f44f039fa 100644 --- a/src/main/java/use_case/signup/SignupInteractor.java +++ b/src/main/java/use_case/signup/SignupInteractor.java @@ -3,6 +3,8 @@ import entity.User; import entity.UserFactory; +import java.util.ArrayList; + /** * The Signup Interactor. */ @@ -28,7 +30,10 @@ else if (!signupInputData.getPassword().equals(signupInputData.getRepeatPassword userPresenter.prepareFailView("Passwords don't match."); } else { - final User user = userFactory.create(signupInputData.getUsername(), signupInputData.getPassword()); + final User user = userFactory.create(signupInputData.getUsername(), + signupInputData.getPassword(), + new ArrayList<>(), + new ArrayList<>()); userDataAccessObject.save(user); final SignupOutputData signupOutputData = new SignupOutputData(user.getName(), false); From d6104c011209bdd75acef5cd21a0d68e1359705d Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Thu, 14 Nov 2024 21:10:36 -0500 Subject: [PATCH 022/113] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4f0d0991a..f2498c615 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ out/ !**/src/test/**/out/ .idea .idea/easycode.ignore +homework-5.iml target/ @@ -30,4 +31,4 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store From d5f55b2e99903c90ef3a67df3b0eea3d1d567cf6 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Thu, 14 Nov 2024 21:27:28 -0500 Subject: [PATCH 023/113] fix stock entity data types --- src/main/java/entity/CommonStock.java | 22 ++++++++++---------- src/main/java/entity/CommonStockFactory.java | 2 +- src/main/java/entity/Stock.java | 10 ++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/entity/CommonStock.java b/src/main/java/entity/CommonStock.java index e54e6b443..d3faa02df 100644 --- a/src/main/java/entity/CommonStock.java +++ b/src/main/java/entity/CommonStock.java @@ -6,13 +6,13 @@ public class CommonStock implements Stock { private final String symbol; - private final int openPrice; - private final int closePrice; - private final int volume; - private final int high; - private final int low; + private final double openPrice; + private final double closePrice; + private final double volume; + private final double high; + private final double low; - public CommonStock(String symbol, int openPrice, int closePrice, int volume, int high, int low) { + public CommonStock(String symbol, double openPrice, double closePrice, double volume, double high, double low) { this.symbol = symbol; this.openPrice = openPrice; this.closePrice = closePrice; @@ -27,27 +27,27 @@ public String getSymbol() { } @Override - public int getOpenPrice() { + public double getOpenPrice() { return openPrice; } @Override - public int getClosePrice() { + public double getClosePrice() { return closePrice; } @Override - public int getVolume() { + public double getVolume() { return volume; } @Override - public int getHigh() { + public double getHigh() { return high; } @Override - public int getLow() { + public double getLow() { return low; } } diff --git a/src/main/java/entity/CommonStockFactory.java b/src/main/java/entity/CommonStockFactory.java index c62e87b56..160fdd4e1 100644 --- a/src/main/java/entity/CommonStockFactory.java +++ b/src/main/java/entity/CommonStockFactory.java @@ -6,7 +6,7 @@ public class CommonStockFactory implements StockFactory { @Override - public Stock create(String symbol, int openPrice, int closePrice, int volume, int high, int low) { + public Stock create(String symbol, double openPrice, double closePrice, double volume, double high, double low) { return new CommonStock(symbol, openPrice, closePrice, volume, high, low); } } diff --git a/src/main/java/entity/Stock.java b/src/main/java/entity/Stock.java index f588e6cde..7a480f4a7 100644 --- a/src/main/java/entity/Stock.java +++ b/src/main/java/entity/Stock.java @@ -15,29 +15,29 @@ public interface Stock { * Returns the stock open price of the stock. * @return the stock open price of the stock. */ - int getOpenPrice(); + double getOpenPrice(); /** * Returns the stock close price of the stock. * @return the stock close price of the stock. */ - int getClosePrice(); + double getClosePrice(); /** * Returns the stock volume of the stock. * @return the stock volume of the stock. */ - int getVolume(); + double getVolume(); /** * Returns the stock highest price of the stock. * @return the stock highest price of the stock. */ - int getHigh(); + double getHigh(); /** * Returns the stock lowest price of the stock. * @return the stock lowest price of the stock. */ - int getLow(); + double getLow(); } From d29f782de94c739496ee90c34dbd8c7ba3473232 Mon Sep 17 00:00:00 2001 From: Ryan Jin Date: Fri, 15 Nov 2024 00:55:59 -0500 Subject: [PATCH 024/113] Portfolio view finished design --- src/main/java/app/AppBuilder.java | 17 +- src/main/java/app/Main.java | 1 + .../home_view/HomeController.java | 2 +- .../home_view/HomePresenter.java | 9 +- .../portfolio/PortfolioViewModel.java | 25 +++ src/main/java/view/PortfolioView.java | 203 ++++++++++++++++++ 6 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 src/main/java/interface_adapter/portfolio/PortfolioViewModel.java create mode 100644 src/main/java/view/PortfolioView.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 9f139a5d1..7801a9851 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -24,10 +24,12 @@ import interface_adapter.login.LoginViewModel; import interface_adapter.logout.LogoutController; import interface_adapter.logout.LogoutPresenter; +import interface_adapter.portfolio.PortfolioViewModel; import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupPresenter; import interface_adapter.signup.SignupViewModel; import interface_adapter.watchlist_view.WatchListViewModel; +import interface_adapter.portfolio.PortfolioViewModel; import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; @@ -76,6 +78,8 @@ public class AppBuilder { private LoginView loginView; private WatchListView watchListView; private WatchListViewModel watchListViewModel; + private PortfolioView portfolioView; + private PortfolioViewModel portfolioViewModel; public AppBuilder() { cardPanel.setLayout(cardLayout); @@ -132,13 +136,24 @@ public AppBuilder addWatchListView() { return this; } + /** + * Adds the Portfolio View to the application. + * @return this builder + */ + public AppBuilder addPortfolioView() { + portfolioViewModel = new PortfolioViewModel(); + portfolioView = new PortfolioView(portfolioViewModel, viewManagerModel); + cardPanel.add(portfolioView, portfolioView.getViewName()); + return this; + } + /** * Adds the Home Use Case to the application. * @return this builder */ public AppBuilder addHomeUseCase() { final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, - loginViewModel, signupViewModel, viewManagerModel); + loginViewModel, signupViewModel, viewManagerModel, portfolioViewModel); final HomeInputBoundary homeInteractor = new HomeInteractor(userDataAccessObject, homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 6bfcf1026..03d143f75 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -23,6 +23,7 @@ public static void main(String[] args) { .addLoginUseCase() .addLogoutUseCase() .addChangePasswordUseCase() + .addPortfolioView() .build(); application.pack(); diff --git a/src/main/java/interface_adapter/home_view/HomeController.java b/src/main/java/interface_adapter/home_view/HomeController.java index adb253b45..e2d754d60 100644 --- a/src/main/java/interface_adapter/home_view/HomeController.java +++ b/src/main/java/interface_adapter/home_view/HomeController.java @@ -24,7 +24,7 @@ public void search(String symbol) { } /** - * Executes the "switch to LoginView" Use Case. + * Executes the "switch to Portfolio" Use Case. */ public void switchToPortfolio() { homeUseCaseInteractor.switchToPortfolio(); diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index e412ad147..5dcdc6a6e 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -2,6 +2,7 @@ import interface_adapter.ViewManagerModel; import interface_adapter.login.LoginViewModel; +import interface_adapter.portfolio.PortfolioViewModel; import interface_adapter.signup.SignupViewModel; import use_case.home_view.HomeOutputBoundary; import use_case.home_view.HomeOutputData; @@ -16,17 +17,20 @@ public class HomePresenter implements HomeOutputBoundary { private final LoginViewModel loginViewModel; private final SignupViewModel signupViewModel; private final ViewManagerModel viewManagerModel; + private final PortfolioViewModel portfolioViewModel; private static final String WATCHLIST_VIEW_NAME = "WatchListView"; public HomePresenter(HomeViewModel homeViewModel, LoginViewModel loginViewModel, SignupViewModel signupViewModel, - ViewManagerModel viewManagerModel) { + ViewManagerModel viewManagerModel, + PortfolioViewModel portfolioViewModel) { this.homeViewModel = homeViewModel; this.loginViewModel = loginViewModel; this.signupViewModel = signupViewModel; this.viewManagerModel = viewManagerModel; + this.portfolioViewModel = portfolioViewModel; } @Override @@ -43,7 +47,8 @@ public void prepareFailView(String errorMessage) { @Override public void switchToPortfolio() { - System.out.println("Show portfolio view"); + viewManagerModel.setState("PortfolioView"); + viewManagerModel.firePropertyChanged(); } @Override diff --git a/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java b/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java new file mode 100644 index 000000000..c8cc374a3 --- /dev/null +++ b/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java @@ -0,0 +1,25 @@ +package interface_adapter.portfolio; + +import java.util.ArrayList; +import java.util.List; + +/** + * The View Model for the Portfolio View. + */ + +public class PortfolioViewModel { + private List stockList; + + public PortfolioViewModel() { + this.stockList = new ArrayList<>(); + } + + public List getStockList() { + return stockList; + } + + public void setStockList(List stockList) { + this.stockList = stockList; + } + +} \ No newline at end of file diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java new file mode 100644 index 000000000..aa71da696 --- /dev/null +++ b/src/main/java/view/PortfolioView.java @@ -0,0 +1,203 @@ +package view; + +import interface_adapter.ViewManagerModel; +import interface_adapter.portfolio.PortfolioViewModel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.border.Border; +import javax.swing.BorderFactory; + + +public class PortfolioView extends JPanel { + + private final ViewManagerModel viewManagerModel; + + public PortfolioView(PortfolioViewModel viewModel, ViewManagerModel viewManagerModel) { + this.viewManagerModel = viewManagerModel; + setLayout(new BorderLayout()); + setBackground(Color.WHITE); + + // panel setup + final JPanel statisticsPanel = new JPanel(); + statisticsPanel.setLayout(new BoxLayout(statisticsPanel, BoxLayout.Y_AXIS)); + statisticsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 20, 10)); + statisticsPanel.setBackground(Color.WHITE); + + // Container for return button and current amount + final JPanel topRowPanel = new JPanel(); + topRowPanel.setLayout(new BoxLayout(topRowPanel, BoxLayout.Y_AXIS)); + topRowPanel.setBackground(Color.WHITE); + + // Return button + final JButton backButton = new JButton("←"); + backButton.setFont(new Font("SansSerif", Font.PLAIN, 20)); + backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + backButton.setFocusPainted(false); + backButton.setContentAreaFilled(false); + + backButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + + // Sample action, replace with your own logic + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); + } + }); + + // Current amount + final JLabel currentAmount = new JLabel("$12345"); + currentAmount.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + currentAmount.setFont(new Font("SansSerif", Font.PLAIN, 20)); + + // Add return button and current amount to the top row + topRowPanel.add(backButton); + topRowPanel.add(Box.createRigidArea(new Dimension(0, 2))); + topRowPanel.add(currentAmount); + + // Create a sub-panel with BorderLayout + final JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBackground(Color.WHITE); + + // Add top row panel to the left side (WEST) + mainPanel.add(topRowPanel, BorderLayout.WEST); + + // Dummy value on the right side (EAST) + final JLabel dummyValue = new JLabel(""); + dummyValue.setFont(new Font("SansSerif", Font.PLAIN, 16)); + mainPanel.add(dummyValue, BorderLayout.EAST); + + // Add topRowPanel to statisticsPanel + statisticsPanel.add(mainPanel); + + // portfolio amount changes + percentChangeLayout(statisticsPanel, "Today", "+$44.09", "+24.10%"); + percentChangeLayout(statisticsPanel, "All Time", "+$84.09", "+40.10%"); + add(statisticsPanel, BorderLayout.NORTH); + + // Create the content panel + final JPanel contentPanel = new JPanel(); + contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); + contentPanel.setBackground(Color.WHITE); + + // Create a sub-panel with BorderLayout + final JPanel titlePanel = new JPanel(new BorderLayout()); + titlePanel.setBackground(Color.WHITE); + + // Portfolio title on the left + final JLabel portfolioTitle = new JLabel("Portfolio"); + portfolioTitle.setFont(new Font("SansSerif", Font.BOLD, 24)); + portfolioTitle.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); + titlePanel.add(portfolioTitle, BorderLayout.WEST); + + // Dummy text on the right + final JLabel dummyLabel = new JLabel(""); + dummyLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + dummyLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + titlePanel.add(dummyLabel, BorderLayout.EAST); + + // Add the sub-panel to the main content panel + contentPanel.add(titlePanel); + + addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09", "+24.10%"); + addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); + addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); + + add(contentPanel, BorderLayout.CENTER); + } + + private void percentChangeLayout(JPanel statisticsPanel, String title, String totalChange, String totalPercentage) { + final JPanel displayPanel = new JPanel(new BorderLayout()); + displayPanel.setBackground(Color.WHITE); + displayPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + + // Left part: Title + final JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); + leftPanel.setBackground(Color.WHITE); + + final JLabel titleLabel = new JLabel(title); + titleLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + + leftPanel.add(titleLabel); + + // Right part: up and down information + final JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.setBackground(Color.WHITE); + rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); + + final JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); + totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + + rightPanel.add(totalChangeLabel); + + // Add all to displayPanel + displayPanel.add(leftPanel, BorderLayout.WEST); + displayPanel.add(rightPanel, BorderLayout.EAST); + + // Add all information + statisticsPanel.add(displayPanel); + } + + private void addStockItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage, String totalChange, String totalPercentage) { + final JPanel stockPanel = new JPanel(new BorderLayout()); + stockPanel.setBackground(Color.WHITE); + // Create a matte border and an empty border + final Border matteBorder = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY); + final Border emptyBorder = BorderFactory.createEmptyBorder(0, 30, 0, 0); + + // Combine them using CompoundBorder + final Border combinedBorder = BorderFactory.createCompoundBorder(emptyBorder, matteBorder); + + // Set the combined border + stockPanel.setBorder(combinedBorder); + + // Left part: Stock code and price + final JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); + leftPanel.setBackground(Color.WHITE); + + final JLabel stockCodeLabel = new JLabel(code); + stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + stockCodeLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + final JLabel stockPriceLabel = new JLabel(price); + stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + + leftPanel.add(stockCodeLabel); + leftPanel.add(stockPriceLabel); + + // Right part: up and down information + final JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.setBackground(Color.WHITE); + rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); + + final JLabel dailyChangeLabel = new JLabel(dailyChange + "(" + dailyPercentage + ")"); + dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + dailyChangeLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + + final JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); + totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + totalChangeLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + + rightPanel.add(dailyChangeLabel); + rightPanel.add(totalChangeLabel); + + // Add all to stockPanel + stockPanel.add(leftPanel, BorderLayout.WEST); + stockPanel.add(rightPanel, BorderLayout.EAST); + + // Add all information + contentPanel.add(stockPanel); + } + + public String getViewName() { + return "PortfolioView"; + } +} + From 5050e1c06433270d458cf94b31f6ea93b20c40dd Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sat, 16 Nov 2024 14:30:21 -0500 Subject: [PATCH 025/113] add get/set watchlist method for DataAccess --- .../data_access/DBUserDataAccessObject.java | 13 +- .../data_access/FileUserDataAccessObject.java | 112 ++++++------------ .../InMemoryUserDataAccessObject.java | 21 ---- src/main/java/data_access/watchlist.txt | 1 + .../java/entity/CommonSimulatedHolding.java | 4 + .../entity/CommonSimulatedHoldingFactory.java | 4 + src/main/java/entity/SimulatedHolding.java | 4 + .../java/entity/SimulatedHoldingFactory.java | 4 + .../WatchListDataAccessInterface.java | 4 + .../WatchListModifyDataAccessInterface.java | 4 + 10 files changed, 63 insertions(+), 108 deletions(-) create mode 100644 src/main/java/data_access/watchlist.txt create mode 100644 src/main/java/entity/CommonSimulatedHolding.java create mode 100644 src/main/java/entity/CommonSimulatedHoldingFactory.java create mode 100644 src/main/java/entity/SimulatedHolding.java create mode 100644 src/main/java/entity/SimulatedHoldingFactory.java create mode 100644 src/main/java/use_case/watchlist/WatchListDataAccessInterface.java create mode 100644 src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index 4c70cd9d7..f2125a761 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -3,14 +3,11 @@ import java.io.IOException; import java.util.ArrayList; -import entity.Stock; -import entity.StockFactory; +import entity.*; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import entity.User; -import entity.UserFactory; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -32,12 +29,6 @@ public class DBUserDataAccessObject implements SignupUserDataAccessInterface, HomeDataAccessInterface { private static final int SUCCESS_CODE = 200; - private static final int CLOSE = 138; - private static final int OPEN = 132; - private static final int VOLUME = 100502000; - private static final int HIGH = 140; - private static final int LOW = 125; - private static final String CONTENT_TYPE_LABEL = "Content-Type"; private static final String CONTENT_TYPE_JSON = "application/json"; private static final String STATUS_CODE_LABEL = "status_code"; @@ -71,7 +62,7 @@ public User get(String username) { final String name = userJSONObject.getString(USERNAME); final String password = userJSONObject.getString(PASSWORD); final ArrayList emptyWatchList = new ArrayList<>(); - final ArrayList emptyStockList = new ArrayList<>(); + final ArrayList emptyStockList = new ArrayList<>(); final User user = userFactory.create(name, password, emptyWatchList, emptyStockList); return user; diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 8eb869dfe..915f96ccd 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -6,80 +6,61 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; import entity.Stock; +import entity.StockFactory; import entity.User; import entity.UserFactory; import use_case.change_password.ChangePasswordUserDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; import use_case.signup.SignupUserDataAccessInterface; +import use_case.watchlist.WatchListDataAccessInterface; +import use_case.watchlist.WatchListModifyDataAccessInterface; /** * DAO for user data implemented using a File to persist the data. */ -public class FileUserDataAccessObject implements SignupUserDataAccessInterface, - LoginUserDataAccessInterface, - ChangePasswordUserDataAccessInterface { +public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface { - private static final String HEADER = "username,password"; + private final ArrayList watchList = new ArrayList<>(); + private final ArrayList portfolioList = new ArrayList(); - private final File csvFile; - private final Map headers = new LinkedHashMap<>(); - private final Map accounts = new HashMap<>(); - private String currentUsername; + private final String mainFilePath = "src/main/java/data_access/"; + private final String watchListFilePath = "watchlist.txt"; - public FileUserDataAccessObject(String csvPath, UserFactory userFactory) throws IOException { + private final StockFactory stockFactory; - csvFile = new File(csvPath); - headers.put("username", 0); - headers.put("password", 1); - - if (csvFile.length() == 0) { - save(); - } - else { - - try (BufferedReader reader = new BufferedReader(new FileReader(csvFile))) { - final String header = reader.readLine(); - - if (!header.equals(HEADER)) { - throw new RuntimeException(String.format("header should be%n: %s%but was:%n%s", HEADER, header)); - } + public FileUserDataAccessObject(StockFactory stockFactory) throws IOException { + this.stockFactory = stockFactory; + } - String row; - while ((row = reader.readLine()) != null) { - final String[] col = row.split(","); - final String username = String.valueOf(col[headers.get("username")]); - final String password = String.valueOf(col[headers.get("password")]); - final ArrayList emptyWatchList = new ArrayList<>(); - final ArrayList emptyStockList = new ArrayList<>(); - final User user = userFactory.create(username, password, emptyWatchList, emptyStockList); - accounts.put(username, user); - } + // Watch list related APIs + @Override + public ArrayList getWatchList() { + // Using FileReader and BufferedReader to read the file + try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + watchListFilePath))) { + final String line = reader.readLine(); + if (line != null) { + Collections.addAll(watchList, line.split(",")); } + + return watchList; + } + catch (IOException ex) { + throw new RuntimeException(ex); } } - private void save() { + @Override + public void saveWatchList() { final BufferedWriter writer; try { - writer = new BufferedWriter(new FileWriter(csvFile)); - writer.write(String.join(",", headers.keySet())); + writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath)); + writer.write(String.join(",", watchList)); writer.newLine(); - for (User user : accounts.values()) { - final String line = String.format("%s,%s", - user.getName(), user.getPassword()); - writer.write(line); - writer.newLine(); - } - writer.close(); - } catch (IOException ex) { throw new RuntimeException(ex); @@ -87,35 +68,14 @@ private void save() { } @Override - public void save(User user) { - accounts.put(user.getName(), user); - this.save(); - } - - @Override - public User get(String username) { - return accounts.get(username); - } - - @Override - public void setCurrentUsername(String name) { - this.currentUsername = name; - } - - @Override - public String getCurrentUsername() { - return this.currentUsername; - } - - @Override - public boolean existsByName(String identifier) { - return accounts.containsKey(identifier); + public void addToWatchList(String symbol) { + watchList.add(symbol); + saveWatchList(); } @Override - public void changePassword(User user) { - // Replace the User object in the map - accounts.put(user.getName(), user); - save(); + public void removeFromWatchList(String symbol) { + watchList.remove(symbol); + saveWatchList(); } } diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java index 41583cacb..9a6566129 100644 --- a/src/main/java/data_access/InMemoryUserDataAccessObject.java +++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java @@ -26,10 +26,6 @@ public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterfa LogoutUserDataAccessInterface { private final Map users = new HashMap<>(); - private final ArrayList watchList = new ArrayList<>(); - private final ArrayList portfolioList = new ArrayList(); - - private final String watchListFilePath = "watchlist.txt"; private String currentUsername; @@ -63,21 +59,4 @@ public void setCurrentUsername(String name) { public String getCurrentUsername() { return this.currentUsername; } - - // Watch list related APIs - public void getWatchList() { -// // Using FileReader and BufferedReader to read the file line by line -// try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { -// String line; -// while ((line = reader.readLine()) != null) { -// System.out.println(line); -// } -// } -// catch (IOException e) { -// e.printStackTrace(); -// } - } - public void addToWatchList(String stock) { - watchList.add(stock); - } } diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt new file mode 100644 index 000000000..9834f10f0 --- /dev/null +++ b/src/main/java/data_access/watchlist.txt @@ -0,0 +1 @@ +NVDA,AAPL,AAPL diff --git a/src/main/java/entity/CommonSimulatedHolding.java b/src/main/java/entity/CommonSimulatedHolding.java new file mode 100644 index 000000000..5a6a9c55d --- /dev/null +++ b/src/main/java/entity/CommonSimulatedHolding.java @@ -0,0 +1,4 @@ +package entity; + +public class CommonSimulatedHolding { +} diff --git a/src/main/java/entity/CommonSimulatedHoldingFactory.java b/src/main/java/entity/CommonSimulatedHoldingFactory.java new file mode 100644 index 000000000..41d2a110e --- /dev/null +++ b/src/main/java/entity/CommonSimulatedHoldingFactory.java @@ -0,0 +1,4 @@ +package entity; + +public class CommonSimulatedHoldingFactory { +} diff --git a/src/main/java/entity/SimulatedHolding.java b/src/main/java/entity/SimulatedHolding.java new file mode 100644 index 000000000..7db02265c --- /dev/null +++ b/src/main/java/entity/SimulatedHolding.java @@ -0,0 +1,4 @@ +package entity; + +public interface SimulatedHolding { +} diff --git a/src/main/java/entity/SimulatedHoldingFactory.java b/src/main/java/entity/SimulatedHoldingFactory.java new file mode 100644 index 000000000..656c3a2b8 --- /dev/null +++ b/src/main/java/entity/SimulatedHoldingFactory.java @@ -0,0 +1,4 @@ +package entity; + +public interface SimulatedHoldingFactory { +} diff --git a/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java b/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java new file mode 100644 index 000000000..6d7bb2f87 --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java @@ -0,0 +1,4 @@ +package use_case.watchlist; + +public interface WatchListDataAccessInterface { +} diff --git a/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java b/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java new file mode 100644 index 000000000..0044e5a12 --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java @@ -0,0 +1,4 @@ +package use_case.watchlist; + +public interface WatchListModifyDataAccessInterface { +} From 9953e873da8237a959e74dce3a93b7e2f534fdb5 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sat, 16 Nov 2024 14:35:19 -0500 Subject: [PATCH 026/113] fix bugs for watchListDataAccessObject --- src/main/java/data_access/DBUserDataAccessObject.java | 1 - src/main/java/data_access/FileUserDataAccessObject.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index f2125a761..5ba54d1c6 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -41,7 +41,6 @@ public class DBUserDataAccessObject implements SignupUserDataAccessInterface, public DBUserDataAccessObject(UserFactory userFactory, StockFactory stockFactory) { this.userFactory = userFactory; this.stockFactory = stockFactory; - // No need to do anything to reinitialize a user list! The data is the cloud that may be miles away. } @Override diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 915f96ccd..bd9421e25 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -48,6 +48,7 @@ public ArrayList getWatchList() { return watchList; } catch (IOException ex) { + // Normally, this means the file doesn't exist throw new RuntimeException(ex); } } From 17f3c74effe82e6358ff6ae7d3fb00acb20a4e55 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sat, 16 Nov 2024 15:04:36 -0500 Subject: [PATCH 027/113] add portfolio data getter/setter --- .../data_access/FileUserDataAccessObject.java | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index bd9421e25..0bcd0d7a9 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -8,10 +8,7 @@ import java.io.IOException; import java.util.*; -import entity.Stock; -import entity.StockFactory; -import entity.User; -import entity.UserFactory; +import entity.*; import use_case.change_password.ChangePasswordUserDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; import use_case.signup.SignupUserDataAccessInterface; @@ -24,15 +21,18 @@ public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface { private final ArrayList watchList = new ArrayList<>(); - private final ArrayList portfolioList = new ArrayList(); + private final ArrayList portfolioList = new ArrayList<>(); private final String mainFilePath = "src/main/java/data_access/"; private final String watchListFilePath = "watchlist.txt"; + private final String portfolioFilePath = "portfolio.txt"; private final StockFactory stockFactory; + private final SimulatedHoldingFactory simulatedHoldingFactory; - public FileUserDataAccessObject(StockFactory stockFactory) throws IOException { + public FileUserDataAccessObject(StockFactory stockFactory, SimulatedHoldingFactory simulatedHoldingFactory) throws IOException { this.stockFactory = stockFactory; + this.simulatedHoldingFactory = simulatedHoldingFactory; } // Watch list related APIs @@ -79,4 +79,56 @@ public void removeFromWatchList(String symbol) { watchList.remove(symbol); saveWatchList(); } + + // Portfolio list related APIs + public ArrayList getPortfolioList() { + // Using FileReader and BufferedReader to read the file + try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + portfolioFilePath))) { + String line; + while ((line = reader.readLine()) != null) { + final String[] data = line.split(","); + final String symbol = data[0]; + final double price = Double.parseDouble(data[1]); + final int amount = Integer.parseInt(data[2]); + final SimulatedHolding simulatedHolding = simulatedHoldingFactory.create(symbol, price, amount); + + portfolioList.add(simulatedHolding); + } + + return portfolioList; + } + catch (IOException ex) { + // Normally, this means the file doesn't exist + throw new RuntimeException(ex); + } + } + + public void savePortfolioList() { + final BufferedWriter writer; + try { + writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath)); + for (SimulatedHolding simulatedHolding : portfolioList) { + writer.write(simulatedHolding.getSymbol() + ","); + writer.write(simulatedHolding.getPurchasePrice() + ","); + writer.write(simulatedHolding.getPurchaseAmount() + ","); + writer.newLine(); + } + + writer.close(); + } + catch (IOException ex) { + // Normally, this means the file doesn't exist + throw new RuntimeException(ex); + } + } + + public void addToPortfolioList(SimulatedHolding simulatedHolding) { + portfolioList.add(simulatedHolding); + savePortfolioList(); + } + + public void removeFromPortfolioList(SimulatedHolding simulatedHolding) { + portfolioList.remove(simulatedHolding); + savePortfolioList(); + } } From dd1466e9030477d42998dfa071eef2e91c1fd375 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sat, 16 Nov 2024 15:07:39 -0500 Subject: [PATCH 028/113] push missing files --- src/main/java/app/AppBuilder.java | 2 ++ .../java/entity/CommonSimulatedHolding.java | 29 ++++++++++++++++++- .../entity/CommonSimulatedHoldingFactory.java | 9 +++++- src/main/java/entity/CommonUser.java | 6 ++-- src/main/java/entity/CommonUserFactory.java | 2 +- src/main/java/entity/SimulatedHolding.java | 18 ++++++++++++ .../java/entity/SimulatedHoldingFactory.java | 10 +++++++ src/main/java/entity/User.java | 2 +- src/main/java/entity/UserFactory.java | 2 +- .../home_view/HomePresenter.java | 1 + .../home_view/HomeOutputBoundary.java | 2 +- .../WatchListDataAccessInterface.java | 13 +++++++++ .../WatchListModifyDataAccessInterface.java | 11 +++++++ 13 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index a9a033d41..d0302e0b5 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -7,6 +7,7 @@ import javax.swing.WindowConstants; import data_access.DBUserDataAccessObject; +import data_access.FileUserDataAccessObject; import data_access.InMemoryUserDataAccessObject; import entity.CommonStockFactory; import entity.CommonUserFactory; @@ -89,6 +90,7 @@ public AppBuilder addHomeView() { homeViewModel = new HomeViewModel(); homeView = new HomeView(homeViewModel); cardPanel.add(homeView, homeView.getViewName()); + return this; } diff --git a/src/main/java/entity/CommonSimulatedHolding.java b/src/main/java/entity/CommonSimulatedHolding.java index 5a6a9c55d..364108d1c 100644 --- a/src/main/java/entity/CommonSimulatedHolding.java +++ b/src/main/java/entity/CommonSimulatedHolding.java @@ -1,4 +1,31 @@ package entity; -public class CommonSimulatedHolding { +/** + * A simple implementation of the Simulated Holding interface. + */ +public class CommonSimulatedHolding implements SimulatedHolding { + private String symbol; + private double price; + private int amount; + + public CommonSimulatedHolding(String symbol, double price, int amount) { + this.symbol = symbol; + this.price = price; + this.amount = amount; + } + + @Override + public String getSymbol() { + return symbol; + } + + @Override + public double getPurchasePrice() { + return price; + } + + @Override + public int getPurchaseAmount() { + return amount; + } } diff --git a/src/main/java/entity/CommonSimulatedHoldingFactory.java b/src/main/java/entity/CommonSimulatedHoldingFactory.java index 41d2a110e..cd1a45c60 100644 --- a/src/main/java/entity/CommonSimulatedHoldingFactory.java +++ b/src/main/java/entity/CommonSimulatedHoldingFactory.java @@ -1,4 +1,11 @@ package entity; -public class CommonSimulatedHoldingFactory { +/** + * Factory for creating CommonUser objects. + */ +public class CommonSimulatedHoldingFactory implements SimulatedHoldingFactory { + @Override + public SimulatedHolding create(String symbol, double price, int amount) { + return new CommonSimulatedHolding(symbol, price, amount); + } } diff --git a/src/main/java/entity/CommonUser.java b/src/main/java/entity/CommonUser.java index 88b00e40d..5f54e22cb 100644 --- a/src/main/java/entity/CommonUser.java +++ b/src/main/java/entity/CommonUser.java @@ -11,9 +11,9 @@ public class CommonUser implements User { private final String name; private final String password; private final ArrayList watchList; - private final ArrayList portfolioList; + private final ArrayList portfolioList; - public CommonUser(String name, String password, ArrayList watchList, ArrayList portfolioList) { + public CommonUser(String name, String password, ArrayList watchList, ArrayList portfolioList) { this.name = name; this.password = password; this.watchList = watchList; @@ -36,7 +36,7 @@ public ArrayList getWatchList() { } @Override - public ArrayList getPortfolioList() { + public ArrayList getPortfolioList() { return portfolioList; } } diff --git a/src/main/java/entity/CommonUserFactory.java b/src/main/java/entity/CommonUserFactory.java index b363bdad4..d9c528717 100644 --- a/src/main/java/entity/CommonUserFactory.java +++ b/src/main/java/entity/CommonUserFactory.java @@ -11,7 +11,7 @@ public class CommonUserFactory implements UserFactory { public User create(String name, String password, ArrayList watchList, - ArrayList portfolioList) { + ArrayList portfolioList) { return new CommonUser(name, password, watchList, portfolioList); } } diff --git a/src/main/java/entity/SimulatedHolding.java b/src/main/java/entity/SimulatedHolding.java index 7db02265c..18f1b659f 100644 --- a/src/main/java/entity/SimulatedHolding.java +++ b/src/main/java/entity/SimulatedHolding.java @@ -1,4 +1,22 @@ package entity; public interface SimulatedHolding { + /** + * Returns the stock symbol/ticker of the stock. + * @return the stock symbol/ticker of the stock. + */ + String getSymbol(); + + /** + * Returns the purchase price of the stock. + * @return the purchase price of the stock. + */ + double getPurchasePrice(); + + /** + * Returns the amount the stock that user purchased. + * @return the amount the stock that user purchased. + */ + int getPurchaseAmount(); + } diff --git a/src/main/java/entity/SimulatedHoldingFactory.java b/src/main/java/entity/SimulatedHoldingFactory.java index 656c3a2b8..af05d343d 100644 --- a/src/main/java/entity/SimulatedHoldingFactory.java +++ b/src/main/java/entity/SimulatedHoldingFactory.java @@ -1,4 +1,14 @@ package entity; +import java.util.ArrayList; + public interface SimulatedHoldingFactory { + /** + * Creates a new User. + * @param symbol the symbol of stock + * @param price the purchase price of stock + * @param amount the amount that user purchased + * @return the new simulated holding + */ + SimulatedHolding create(String symbol, double price, int amount); } diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java index f51323df0..c504731e6 100644 --- a/src/main/java/entity/User.java +++ b/src/main/java/entity/User.java @@ -29,6 +29,6 @@ public interface User { * Returns the portfolio of the user. * @return the portfolio of the user. */ - ArrayList getPortfolioList(); + ArrayList getPortfolioList(); } diff --git a/src/main/java/entity/UserFactory.java b/src/main/java/entity/UserFactory.java index 55445875e..a853547b0 100644 --- a/src/main/java/entity/UserFactory.java +++ b/src/main/java/entity/UserFactory.java @@ -15,6 +15,6 @@ public interface UserFactory { * @return the new user */ User create(String name, String password, - ArrayList watchList, ArrayList portfolioList); + ArrayList watchList, ArrayList portfolioList); } diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index e412ad147..522467e28 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -43,6 +43,7 @@ public void prepareFailView(String errorMessage) { @Override public void switchToPortfolio() { + // Present stock view System.out.println("Show portfolio view"); } diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index b043feab7..c4900b35b 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -18,7 +18,7 @@ public interface HomeOutputBoundary { void prepareFailView(String errorMessage); /** - * Switches to the portfolio View. + * Switches to the watch list View. */ void switchToPortfolio(); diff --git a/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java b/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java index 6d7bb2f87..63bc1ce31 100644 --- a/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java +++ b/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java @@ -1,4 +1,17 @@ package use_case.watchlist; +import java.util.ArrayList; + public interface WatchListDataAccessInterface { + + /** + * Returns the user's watchlist. + * @return the user's watchlist + */ + ArrayList getWatchList(); + + /** + * Saves the watchlist. + */ + void saveWatchList(); } diff --git a/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java b/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java index 0044e5a12..2baec22d7 100644 --- a/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java +++ b/src/main/java/use_case/watchlist/WatchListModifyDataAccessInterface.java @@ -1,4 +1,15 @@ package use_case.watchlist; public interface WatchListModifyDataAccessInterface { + /** + * Add stock to watchlist. + * @param symbol the stock's stock to add + */ + void addToWatchList(String symbol); + + /** + * Remove stock from watchlist. + * @param symbol the stock's stock to add + */ + void removeFromWatchList(String symbol); } From 9370a634afa7ca4b90068aeff3def1af129cf6ef Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sat, 16 Nov 2024 16:18:21 -0500 Subject: [PATCH 029/113] Pass data from HomeView to StockView --- src/main/java/app/AppBuilder.java | 36 ++++++++++++------ src/main/java/app/Main.java | 1 + .../data_access/DBUserDataAccessObject.java | 1 + .../data_access/FileUserDataAccessObject.java | 2 +- .../home_view/HomePresenter.java | 15 ++++++-- .../home_view/HomeState.java | 9 +++++ .../portfolio/PortfolioState.java | 20 ++++++++++ .../portfolio/PortfolioViewModel.java | 19 +++------- .../stock_view/StockViewModel.java | 14 +++++++ .../stock_view/StockViewState.java | 18 +++++++++ src/main/java/view/HomeView.java | 37 ++++++++++++++++++- src/main/java/view/StockView.java | 23 +++++++++--- 12 files changed, 158 insertions(+), 37 deletions(-) create mode 100644 src/main/java/interface_adapter/portfolio/PortfolioState.java create mode 100644 src/main/java/interface_adapter/stock_view/StockViewModel.java create mode 100644 src/main/java/interface_adapter/stock_view/StockViewState.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index c68393f6c..4986aae21 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -9,10 +9,7 @@ import data_access.DBUserDataAccessObject; import data_access.FileUserDataAccessObject; import data_access.InMemoryUserDataAccessObject; -import entity.CommonStockFactory; -import entity.CommonUserFactory; -import entity.StockFactory; -import entity.UserFactory; +import entity.*; import interface_adapter.ViewManagerModel; import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; @@ -29,6 +26,7 @@ import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupPresenter; import interface_adapter.signup.SignupViewModel; +import interface_adapter.stock_view.StockViewModel; import interface_adapter.watchlist_view.WatchListViewModel; import interface_adapter.portfolio.PortfolioViewModel; import use_case.change_password.ChangePasswordInputBoundary; @@ -63,11 +61,13 @@ public class AppBuilder { // thought question: is the hard dependency below a problem? private final UserFactory userFactory = new CommonUserFactory(); private final StockFactory stockFactory = new CommonStockFactory(); + private final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); private final ViewManagerModel viewManagerModel = new ViewManagerModel(); private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel); // thought question: is the hard dependency below a problem? - private final DBUserDataAccessObject userDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); + private final DBUserDataAccessObject dbUserDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); + private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(stockFactory, simulatedHoldingFactory); private HomeView homeView; private HomeViewModel homeViewModel; @@ -77,11 +77,12 @@ public class AppBuilder { private LoggedInViewModel loggedInViewModel; private LoggedInView loggedInView; private LoginView loginView; - private StockView stockView; private WatchListView watchListView; private WatchListViewModel watchListViewModel; private PortfolioView portfolioView; private PortfolioViewModel portfolioViewModel; + private StockView stockView; + private StockViewModel stockViewModel; public AppBuilder() { cardPanel.setLayout(cardLayout); @@ -154,14 +155,25 @@ public AppBuilder addPortfolioView() { return this; } + /** + * Adds the Stock View to the application. + * @return this builder + */ + public AppBuilder addStockView() { + stockViewModel = new StockViewModel(); + stockView = new StockView(viewManagerModel); + cardPanel.add(stockView, stockView.getViewName()); + return this; + } + /** * Adds the Home Use Case to the application. * @return this builder */ public AppBuilder addHomeUseCase() { final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, - loginViewModel, signupViewModel, viewManagerModel, portfolioViewModel); - final HomeInputBoundary homeInteractor = new HomeInteractor(userDataAccessObject, homeOutputBoundary); + loginViewModel, signupViewModel, viewManagerModel, portfolioViewModel, stockViewModel); + final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); homeView.setHomeController(controller); @@ -176,7 +188,7 @@ public AppBuilder addSignupUseCase() { final SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel, signupViewModel, loginViewModel); final SignupInputBoundary userSignupInteractor = new SignupInteractor( - userDataAccessObject, signupOutputBoundary, userFactory); + dbUserDataAccessObject, signupOutputBoundary, userFactory); final SignupController controller = new SignupController(userSignupInteractor); signupView.setSignupController(controller); @@ -191,7 +203,7 @@ public AppBuilder addLoginUseCase() { final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel, loggedInViewModel, loginViewModel); final LoginInputBoundary loginInteractor = new LoginInteractor( - userDataAccessObject, loginOutputBoundary); + dbUserDataAccessObject, loginOutputBoundary); final LoginController loginController = new LoginController(loginInteractor); loginView.setLoginController(loginController); @@ -207,7 +219,7 @@ public AppBuilder addChangePasswordUseCase() { new ChangePasswordPresenter(loggedInViewModel); final ChangePasswordInputBoundary changePasswordInteractor = - new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory); + new ChangePasswordInteractor(dbUserDataAccessObject, changePasswordOutputBoundary, userFactory); final ChangePasswordController changePasswordController = new ChangePasswordController(changePasswordInteractor); @@ -224,7 +236,7 @@ public AppBuilder addLogoutUseCase() { loggedInViewModel, loginViewModel); final LogoutInputBoundary logoutInteractor = - new LogoutInteractor(userDataAccessObject, logoutOutputBoundary); + new LogoutInteractor(dbUserDataAccessObject, logoutOutputBoundary); final LogoutController logoutController = new LogoutController(logoutInteractor); loggedInView.setLogoutController(logoutController); diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 03d143f75..74b067518 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -24,6 +24,7 @@ public static void main(String[] args) { .addLogoutUseCase() .addChangePasswordUseCase() .addPortfolioView() + .addStockView() .build(); application.pack(); diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index 5ba54d1c6..a1d83372e 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -166,6 +166,7 @@ public void changePassword(User user) { } } + // Stock related APIs @Override public Stock getStock(String symbol) { final OkHttpClient client = new OkHttpClient().newBuilder().build(); diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 0bcd0d7a9..59e2e7643 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -30,7 +30,7 @@ public class FileUserDataAccessObject implements WatchListDataAccessInterface, W private final StockFactory stockFactory; private final SimulatedHoldingFactory simulatedHoldingFactory; - public FileUserDataAccessObject(StockFactory stockFactory, SimulatedHoldingFactory simulatedHoldingFactory) throws IOException { + public FileUserDataAccessObject(StockFactory stockFactory, SimulatedHoldingFactory simulatedHoldingFactory) { this.stockFactory = stockFactory; this.simulatedHoldingFactory = simulatedHoldingFactory; } diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index ca487068e..728dcb5c6 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -4,6 +4,8 @@ import interface_adapter.login.LoginViewModel; import interface_adapter.portfolio.PortfolioViewModel; import interface_adapter.signup.SignupViewModel; +import interface_adapter.stock_view.StockViewModel; +import interface_adapter.stock_view.StockViewState; import use_case.home_view.HomeOutputBoundary; import use_case.home_view.HomeOutputData; import view.WatchListView; @@ -18,23 +20,30 @@ public class HomePresenter implements HomeOutputBoundary { private final SignupViewModel signupViewModel; private final ViewManagerModel viewManagerModel; private final PortfolioViewModel portfolioViewModel; - - private static final String WATCHLIST_VIEW_NAME = "WatchListView"; + private final StockViewModel stockViewModel; public HomePresenter(HomeViewModel homeViewModel, LoginViewModel loginViewModel, SignupViewModel signupViewModel, ViewManagerModel viewManagerModel, - PortfolioViewModel portfolioViewModel) { + PortfolioViewModel portfolioViewModel, + StockViewModel stockViewModel) { + this.homeViewModel = homeViewModel; this.loginViewModel = loginViewModel; this.signupViewModel = signupViewModel; this.viewManagerModel = viewManagerModel; this.portfolioViewModel = portfolioViewModel; + this.stockViewModel = stockViewModel; } @Override public void prepareSuccessView(HomeOutputData searchOutputData) { + final StockViewState stockViewState = new StockViewState(); + stockViewState.setStock(searchOutputData.getStock()); + this.stockViewModel.setState(stockViewState); + this.stockViewModel.firePropertyChanged(); + viewManagerModel.setState("StockView"); viewManagerModel.firePropertyChanged(); } diff --git a/src/main/java/interface_adapter/home_view/HomeState.java b/src/main/java/interface_adapter/home_view/HomeState.java index 6eccaa4db..ae0cf298e 100644 --- a/src/main/java/interface_adapter/home_view/HomeState.java +++ b/src/main/java/interface_adapter/home_view/HomeState.java @@ -4,4 +4,13 @@ * The state for the Home View Model. */ public class HomeState { + private String symbol; + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } } diff --git a/src/main/java/interface_adapter/portfolio/PortfolioState.java b/src/main/java/interface_adapter/portfolio/PortfolioState.java new file mode 100644 index 000000000..db5cc23a5 --- /dev/null +++ b/src/main/java/interface_adapter/portfolio/PortfolioState.java @@ -0,0 +1,20 @@ +package interface_adapter.portfolio; + +import entity.SimulatedHolding; + +import java.util.ArrayList; + +/** + * The state for the Signup View Model. + */ +public class PortfolioState { + private ArrayList simulatedHoldings; + + public ArrayList getSimulatedHoldings() { + return simulatedHoldings; + } + + public void setSimulatedHoldings(ArrayList simulatedHoldings) { + this.simulatedHoldings = simulatedHoldings; + } +} diff --git a/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java b/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java index c8cc374a3..ac781e2b2 100644 --- a/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java +++ b/src/main/java/interface_adapter/portfolio/PortfolioViewModel.java @@ -1,5 +1,8 @@ package interface_adapter.portfolio; +import interface_adapter.ViewModel; +import interface_adapter.signup.SignupState; + import java.util.ArrayList; import java.util.List; @@ -7,19 +10,9 @@ * The View Model for the Portfolio View. */ -public class PortfolioViewModel { - private List stockList; - +public class PortfolioViewModel extends ViewModel { public PortfolioViewModel() { - this.stockList = new ArrayList<>(); - } - - public List getStockList() { - return stockList; + super("porfolio view"); + setState(new PortfolioState()); } - - public void setStockList(List stockList) { - this.stockList = stockList; - } - } \ No newline at end of file diff --git a/src/main/java/interface_adapter/stock_view/StockViewModel.java b/src/main/java/interface_adapter/stock_view/StockViewModel.java new file mode 100644 index 000000000..5336688e5 --- /dev/null +++ b/src/main/java/interface_adapter/stock_view/StockViewModel.java @@ -0,0 +1,14 @@ +package interface_adapter.stock_view; + +import interface_adapter.ViewModel; + +/** + * The ViewModel for the Stock View. + */ +public class StockViewModel extends ViewModel { + + public StockViewModel() { + super("stock view"); + setState(new StockViewState()); + } +} diff --git a/src/main/java/interface_adapter/stock_view/StockViewState.java b/src/main/java/interface_adapter/stock_view/StockViewState.java new file mode 100644 index 000000000..116ee75d2 --- /dev/null +++ b/src/main/java/interface_adapter/stock_view/StockViewState.java @@ -0,0 +1,18 @@ +package interface_adapter.stock_view; + +import entity.Stock; + +/** + * The state for the Stock View Model. + */ +public class StockViewState { + private Stock stock; + + public Stock getStock() { + return stock; + } + + public void setStock(Stock newStock) { + this.stock = newStock; + } +} diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 8f2529a2c..b7de3f98d 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -1,7 +1,9 @@ package view; import interface_adapter.home_view.HomeController; +import interface_adapter.home_view.HomeState; import interface_adapter.home_view.HomeViewModel; +import interface_adapter.login.LoginState; import java.awt.*; import java.awt.event.ActionEvent; @@ -10,6 +12,8 @@ import java.beans.PropertyChangeListener; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; /** * The View for the Home Page. @@ -52,9 +56,33 @@ public HomeView(HomeViewModel homeViewModel) { this.homeViewModel.addPropertyChangeListener(this); // Config search components style - searchTextField.setText("Search"); + searchTextField.setText("AAPL"); final JPanel searchPanel = new JPanel(new FlowLayout()); searchPanel.add(searchTextField); + // Add textField listener + searchTextField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final HomeState currentState = homeViewModel.getState(); + currentState.setSymbol(new String(searchTextField.getText())); + homeViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); // Config stock view components style final JPanel stockViewPanel = new JPanel(); @@ -63,7 +91,12 @@ public HomeView(HomeViewModel homeViewModel) { stockViewPanel.add(stockButton); stockButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - homeController.search("NVDA"); + if (evt.getSource().equals(stockButton)) { + final HomeState currentState = homeViewModel.getState(); + currentState.setSymbol("AAPL"); + + homeController.search(currentState.getSymbol()); + } } }); diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 7db4bd1bc..6e9e57d80 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -1,7 +1,11 @@ package view; +import entity.CommonStockFactory; import entity.Stock; +import entity.StockFactory; import interface_adapter.ViewManagerModel; +import interface_adapter.stock_view.StockViewModel; + import javax.swing.*; import java.awt.*; @@ -19,17 +23,20 @@ public class StockView extends AbstractViewWithBackButton { private JLabel highPriceLabel; private JLabel volumeLabel; - public StockView(Stock stock, ViewManagerModel viewManagerModel) { + public StockView(ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; setLayout(new BorderLayout()); setBackground(Color.WHITE); - JPanel contentPanel = new JPanel(); + final JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); initializeComponents(contentPanel); - updateStock(stock); + final StockFactory stockFactory = new CommonStockFactory(); + final Stock stock = stockFactory.create("NVDA", 132.2, 130.5, + 100000000, 140.32, 128.9); + updateStockData(stock); add(contentPanel, BorderLayout.CENTER); } @@ -39,7 +46,7 @@ private void initializeComponents(JPanel contentPanel) { stockSymbolLabel = createLabel("", 24, Font.BOLD); stockPriceLabel = createLabel("", 18, Font.PLAIN); - JPanel stockInfoPanel = new JPanel(); + final JPanel stockInfoPanel = new JPanel(); stockInfoPanel.setLayout(new BoxLayout(stockInfoPanel, BoxLayout.Y_AXIS)); stockInfoPanel.setBackground(Color.WHITE); stockInfoPanel.add(stockSymbolLabel); @@ -61,7 +68,7 @@ private void initializeComponents(JPanel contentPanel) { contentPanel.add(volumeLabel); } - public void updateStock(Stock stock) { + public void updateStockData(Stock stock) { stockSymbolLabel.setText(stock.getSymbol()); stockPriceLabel.setText("Current Price: $" + stock.getClosePrice()); openPriceLabel.setText("Open Price: $" + stock.getOpenPrice()); @@ -72,13 +79,17 @@ public void updateStock(Stock stock) { } private JLabel createLabel(String text, int fontSize, int fontStyle) { - JLabel label = new JLabel(text); + final JLabel label = new JLabel(text); label.setFont(new Font("SansSerif", fontStyle, fontSize)); label.setAlignmentX(Component.LEFT_ALIGNMENT); return label; } + public String getViewName() { + return "StockView"; + } + @Override void backButtonAction() { viewManagerModel.setState("home view"); From 1db2cfef529028d34f5ff13341cbcf7ae8f599a5 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 17 Nov 2024 17:16:31 -0500 Subject: [PATCH 030/113] Sign in --- src/main/java/app/AppBuilder.java | 2 +- .../login/LoginController.java | 7 +++++++ .../login/LoginPresenter.java | 11 ++++++++++- .../use_case/login/LoginInputBoundary.java | 5 +++++ .../java/use_case/login/LoginInteractor.java | 5 +++++ .../use_case/login/LoginOutputBoundary.java | 5 +++++ src/main/java/view/HomeView.java | 12 ------------ src/main/java/view/LoginView.java | 18 +++++++++++++++--- 8 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 4986aae21..b9da903ea 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -201,7 +201,7 @@ public AppBuilder addSignupUseCase() { */ public AppBuilder addLoginUseCase() { final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel, - loggedInViewModel, loginViewModel); + loggedInViewModel, loginViewModel, signupViewModel); final LoginInputBoundary loginInteractor = new LoginInteractor( dbUserDataAccessObject, loginOutputBoundary); diff --git a/src/main/java/interface_adapter/login/LoginController.java b/src/main/java/interface_adapter/login/LoginController.java index 57e950666..5ec3363ee 100644 --- a/src/main/java/interface_adapter/login/LoginController.java +++ b/src/main/java/interface_adapter/login/LoginController.java @@ -25,4 +25,11 @@ public void execute(String username, String password) { loginUseCaseInteractor.execute(loginInputData); } + + /** + * Executes the "switch to SignUpView" Use Case. + */ + public void switchToSignUpView() { + loginUseCaseInteractor.switchToSignUpView(); + } } diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java index 66560d51a..de8df2262 100644 --- a/src/main/java/interface_adapter/login/LoginPresenter.java +++ b/src/main/java/interface_adapter/login/LoginPresenter.java @@ -3,6 +3,7 @@ import interface_adapter.ViewManagerModel; import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; +import interface_adapter.signup.SignupViewModel; import use_case.login.LoginOutputBoundary; import use_case.login.LoginOutputData; @@ -14,13 +15,15 @@ public class LoginPresenter implements LoginOutputBoundary { private final LoginViewModel loginViewModel; private final LoggedInViewModel loggedInViewModel; private final ViewManagerModel viewManagerModel; + private final SignupViewModel signUpViewModel; public LoginPresenter(ViewManagerModel viewManagerModel, LoggedInViewModel loggedInViewModel, - LoginViewModel loginViewModel) { + LoginViewModel loginViewModel, SignupViewModel signUpViewModel) { this.viewManagerModel = viewManagerModel; this.loggedInViewModel = loggedInViewModel; this.loginViewModel = loginViewModel; + this.signUpViewModel = signUpViewModel; } @Override @@ -42,4 +45,10 @@ public void prepareFailView(String error) { loginState.setLoginError(error); loginViewModel.firePropertyChanged(); } + + @Override + public void switchToSignUpView() { + viewManagerModel.setState(signUpViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } } diff --git a/src/main/java/use_case/login/LoginInputBoundary.java b/src/main/java/use_case/login/LoginInputBoundary.java index faf72dc96..12819e5d7 100644 --- a/src/main/java/use_case/login/LoginInputBoundary.java +++ b/src/main/java/use_case/login/LoginInputBoundary.java @@ -10,4 +10,9 @@ public interface LoginInputBoundary { * @param loginInputData the input data */ void execute(LoginInputData loginInputData); + + /** + * Executes the switch to SignUp view use case. + */ + void switchToSignUpView(); } diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java index 5b36ddcd8..aa71dd3dc 100644 --- a/src/main/java/use_case/login/LoginInteractor.java +++ b/src/main/java/use_case/login/LoginInteractor.java @@ -37,4 +37,9 @@ public void execute(LoginInputData loginInputData) { } } } + + @Override + public void switchToSignUpView() { + loginPresenter.switchToSignUpView(); + } } diff --git a/src/main/java/use_case/login/LoginOutputBoundary.java b/src/main/java/use_case/login/LoginOutputBoundary.java index 08bc4731f..81c6934f3 100644 --- a/src/main/java/use_case/login/LoginOutputBoundary.java +++ b/src/main/java/use_case/login/LoginOutputBoundary.java @@ -15,4 +15,9 @@ public interface LoginOutputBoundary { * @param errorMessage the explanation of the failure */ void prepareFailView(String errorMessage); + + /** + * Switches to the SignUp View. + */ + void switchToSignUpView(); } diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index b7de3f98d..560fa585d 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -133,17 +133,6 @@ public void actionPerformed(ActionEvent evt) { } }); - // Config signup components style - signupButton.setAlignmentX(Component.CENTER_ALIGNMENT); - final JPanel signupPanel = new JPanel(); - signupPanel.setLayout(new BoxLayout(signupPanel, BoxLayout.LINE_AXIS)); - signupPanel.add(signupButton); - signupButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - homeController.switchToSignupView(); - } - }); - // Config frame style this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); @@ -153,7 +142,6 @@ public void actionPerformed(ActionEvent evt) { this.add(portfolioPanel); this.add(watchListPanel); this.add(loginPanel); - this.add(signupPanel); } /** diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index 79d5b1b14..9bcd3657b 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -34,6 +34,7 @@ public class LoginView extends JPanel implements ActionListener, PropertyChangeL private final JPasswordField passwordInputField = new JPasswordField(15); private final JLabel passwordErrorField = new JLabel(); + private final JButton signUp; private final JButton logIn; private final JButton cancel; private final ViewManagerModel viewManagerModel; @@ -53,12 +54,23 @@ public LoginView(LoginViewModel loginViewModel, ViewManagerModel viewManagerMode new JLabel("Password"), passwordInputField); final JPanel buttons = new JPanel(); - logIn = new JButton("log in"); - + signUp = new JButton("Sign Up"); + buttons.add(signUp); + + logIn = new JButton("Log In"); buttons.add(logIn); - cancel = new JButton("cancel"); + + cancel = new JButton("Cancel"); buttons.add(cancel); + signUp.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + loginController.switchToSignUpView(); + } + } + ); + logIn.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt) { From 7bd06da1ab2193398931f0a1b29f0134b312d55e Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sun, 17 Nov 2024 19:52:37 -0500 Subject: [PATCH 031/113] fix main.java bug --- src/main/java/app/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 74b067518..a748c79e6 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -18,13 +18,13 @@ public static void main(String[] args) { .addLoginView() .addSignupView() .addLoggedInView() + .addStockView() .addHomeUseCase() .addSignupUseCase() .addLoginUseCase() .addLogoutUseCase() .addChangePasswordUseCase() .addPortfolioView() - .addStockView() .build(); application.pack(); From bd7fc5bafc4b02c84046d930b8e6f2dd99f47953 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 17 Nov 2024 20:19:35 -0500 Subject: [PATCH 032/113] Account function. --- src/main/java/app/AppBuilder.java | 2 +- .../change_password/IsLoggedIn.java | 19 +++++++++++++++++++ .../login/LoginPresenter.java | 12 ++++++++++-- .../java/use_case/login/LoginInteractor.java | 1 + src/main/java/view/HomeView.java | 18 +++++++++++++++--- src/main/java/view/LoginView.java | 1 + 6 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 src/main/java/interface_adapter/change_password/IsLoggedIn.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index b9da903ea..c58269d24 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -201,7 +201,7 @@ public AppBuilder addSignupUseCase() { */ public AppBuilder addLoginUseCase() { final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel, - loggedInViewModel, loginViewModel, signupViewModel); + loggedInViewModel, loginViewModel, signupViewModel, homeViewModel); final LoginInputBoundary loginInteractor = new LoginInteractor( dbUserDataAccessObject, loginOutputBoundary); diff --git a/src/main/java/interface_adapter/change_password/IsLoggedIn.java b/src/main/java/interface_adapter/change_password/IsLoggedIn.java new file mode 100644 index 000000000..2f1ddeca1 --- /dev/null +++ b/src/main/java/interface_adapter/change_password/IsLoggedIn.java @@ -0,0 +1,19 @@ +package interface_adapter.change_password; + +/** + * Check whether the user is logged in. + */ +public class IsLoggedIn { + private static boolean isLoggedIn; + + public IsLoggedIn() { + } + + public static boolean isLoggedIn() { + return isLoggedIn; + } + + public static void setLoggedIn(boolean whetherLoggedIn) { + IsLoggedIn.isLoggedIn = whetherLoggedIn; + } +} diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java index de8df2262..2f67ce57d 100644 --- a/src/main/java/interface_adapter/login/LoginPresenter.java +++ b/src/main/java/interface_adapter/login/LoginPresenter.java @@ -3,6 +3,8 @@ import interface_adapter.ViewManagerModel; import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; +import interface_adapter.change_password.IsLoggedIn; +import interface_adapter.home_view.HomeViewModel; import interface_adapter.signup.SignupViewModel; import use_case.login.LoginOutputBoundary; import use_case.login.LoginOutputData; @@ -16,14 +18,18 @@ public class LoginPresenter implements LoginOutputBoundary { private final LoggedInViewModel loggedInViewModel; private final ViewManagerModel viewManagerModel; private final SignupViewModel signUpViewModel; + private final HomeViewModel homeViewModel; public LoginPresenter(ViewManagerModel viewManagerModel, LoggedInViewModel loggedInViewModel, - LoginViewModel loginViewModel, SignupViewModel signUpViewModel) { + LoginViewModel loginViewModel, + SignupViewModel signUpViewModel, + HomeViewModel homeViewModel) { this.viewManagerModel = viewManagerModel; this.loggedInViewModel = loggedInViewModel; this.loginViewModel = loginViewModel; this.signUpViewModel = signUpViewModel; + this.homeViewModel = homeViewModel; } @Override @@ -32,11 +38,13 @@ public void prepareSuccessView(LoginOutputData response) { final LoggedInState loggedInState = loggedInViewModel.getState(); loggedInState.setUsername(response.getUsername()); + this.loggedInViewModel.setState(loggedInState); this.loggedInViewModel.firePropertyChanged(); - this.viewManagerModel.setState(loggedInViewModel.getViewName()); + this.viewManagerModel.setState(homeViewModel.getViewName()); this.viewManagerModel.firePropertyChanged(); + IsLoggedIn.setLoggedIn(true); } @Override diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java index aa71dd3dc..977740587 100644 --- a/src/main/java/use_case/login/LoginInteractor.java +++ b/src/main/java/use_case/login/LoginInteractor.java @@ -1,6 +1,7 @@ package use_case.login; import entity.User; +import interface_adapter.change_password.IsLoggedIn; /** * The Login Interactor. diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 560fa585d..4dda7d3a1 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -1,5 +1,6 @@ package view; +import interface_adapter.change_password.IsLoggedIn; import interface_adapter.home_view.HomeController; import interface_adapter.home_view.HomeState; import interface_adapter.home_view.HomeViewModel; @@ -48,10 +49,16 @@ public class HomeView extends AbstractViewWithBackButton implements ActionListen private final JButton thirdStockButton = new JButton(RIGHTARROW); // Login/Logout component - private final JButton loginButton = new JButton("Login"); - private final JButton signupButton = new JButton("Sign up"); + private JButton loginButton = new JButton(); + private final JButton signupButton = new JButton("Sign Up"); public HomeView(HomeViewModel homeViewModel) { + if (IsLoggedIn.isLoggedIn()) { + loginButton.setText("Log Out"); + } + else { + loginButton.setText("Log In"); + } this.homeViewModel = homeViewModel; this.homeViewModel.addPropertyChangeListener(this); @@ -129,7 +136,12 @@ public void actionPerformed(ActionEvent evt) { loginPanel.add(loginButton); loginButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - homeController.switchToLoginView(); + if (IsLoggedIn.isLoggedIn()) { + IsLoggedIn.setLoggedIn(false); + } + else { + homeController.switchToLoginView(); + } } }); diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index 9bcd3657b..216aaab4a 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -16,6 +16,7 @@ import javax.swing.event.DocumentListener; import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.IsLoggedIn; import interface_adapter.login.LoginController; import interface_adapter.login.LoginState; import interface_adapter.login.LoginViewModel; From 19d1b8e508adae99cdef16a3f7eace3090722518 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sun, 17 Nov 2024 20:23:46 -0500 Subject: [PATCH 033/113] add fake data to prevent api usage --- .../use_case/home_view/HomeInteractor.java | 10 ++++- src/main/java/view/HomeView.java | 45 +++++++------------ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 5ff0d0351..800704b79 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -1,6 +1,8 @@ package use_case.home_view; +import entity.CommonStockFactory; import entity.Stock; +import entity.StockFactory; /** * The Home View Interactor. @@ -18,8 +20,12 @@ public HomeInteractor(HomeDataAccessInterface homeDataAccessObject, @Override public void search(HomeInputData searchInputData) { - final String stockSymbol = searchInputData.getStockSymbol(); - final Stock stock = homeDataAccessObject.getStock(stockSymbol); +// final String stockSymbol = searchInputData.getStockSymbol(); +// final Stock stock = homeDataAccessObject.getStock(stockSymbol); + + // Fake data to save usage limit + final StockFactory stockFactory = new CommonStockFactory(); + final Stock stock = stockFactory.create("NVDA", 128.2, 322.1, 100002322, 500.1, 100.23); final HomeOutputData searchOutputData = new HomeOutputData(stock, false); homePresenter.prepareSuccessView(searchOutputData); diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 560fa585d..20c776382 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -3,7 +3,6 @@ import interface_adapter.home_view.HomeController; import interface_adapter.home_view.HomeState; import interface_adapter.home_view.HomeViewModel; -import interface_adapter.login.LoginState; import java.awt.*; import java.awt.event.ActionEvent; @@ -18,7 +17,7 @@ /** * The View for the Home Page. */ -public class HomeView extends AbstractViewWithBackButton implements ActionListener, PropertyChangeListener { +public class HomeView extends JPanel implements ActionListener, PropertyChangeListener { private static final String RIGHTARROW = "->"; @@ -27,11 +26,11 @@ public class HomeView extends AbstractViewWithBackButton implements ActionListen private HomeController homeController; // Search components - private final JTextField searchTextField = new JTextField(30); + private final JTextField searchTextField = new JTextField(20); // Stock view components private final JLabel stockLabel = new JLabel("Stock View"); - private final JButton stockButton = new JButton(RIGHTARROW); + private final JButton searchButton = new JButton(RIGHTARROW); // Portfolio components private final JLabel portfolioLabel = new JLabel("Portfolio"); @@ -56,9 +55,21 @@ public HomeView(HomeViewModel homeViewModel) { this.homeViewModel.addPropertyChangeListener(this); // Config search components style - searchTextField.setText("AAPL"); - final JPanel searchPanel = new JPanel(new FlowLayout()); + searchTextField.setText("NVDA"); + final JPanel searchPanel = new JPanel(); searchPanel.add(searchTextField); + searchPanel.add(searchButton); + // Add search stock button listener + searchButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(searchButton)) { + final HomeState currentState = homeViewModel.getState(); + currentState.setSymbol("NVDA"); + + homeController.search(currentState.getSymbol()); + } + } + }); // Add textField listener searchTextField.getDocument().addDocumentListener(new DocumentListener() { @@ -84,22 +95,6 @@ public void changedUpdate(DocumentEvent e) { } }); - // Config stock view components style - final JPanel stockViewPanel = new JPanel(); - stockViewPanel.setLayout(new BoxLayout(stockViewPanel, BoxLayout.LINE_AXIS)); - stockViewPanel.add(stockLabel); - stockViewPanel.add(stockButton); - stockButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - if (evt.getSource().equals(stockButton)) { - final HomeState currentState = homeViewModel.getState(); - currentState.setSymbol("AAPL"); - - homeController.search(currentState.getSymbol()); - } - } - }); - // Config portfolio components style final JPanel portfolioPanel = new JPanel(); portfolioPanel.setLayout(new BoxLayout(portfolioPanel, BoxLayout.LINE_AXIS)); @@ -138,7 +133,6 @@ public void actionPerformed(ActionEvent evt) { // Add all components this.add(searchPanel); - this.add(stockViewPanel); this.add(portfolioPanel); this.add(watchListPanel); this.add(loginPanel); @@ -157,11 +151,6 @@ public void propertyChange(PropertyChangeEvent evt) { } - @Override - void backButtonAction() { - System.out.println("Back button clicked"); - } - public String getViewName() { return viewName; } From 41368212b6e554f09dc26f4b343929a829ca30aa Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Sun, 17 Nov 2024 20:31:49 -0500 Subject: [PATCH 034/113] fix stock view UI bugs --- src/main/java/view/StockView.java | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 6e9e57d80..7d569ae88 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -4,7 +4,6 @@ import entity.Stock; import entity.StockFactory; import interface_adapter.ViewManagerModel; -import interface_adapter.stock_view.StockViewModel; import javax.swing.*; import java.awt.*; @@ -25,7 +24,8 @@ public class StockView extends AbstractViewWithBackButton { public StockView(ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; - setLayout(new BorderLayout()); + + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBackground(Color.WHITE); final JPanel contentPanel = new JPanel(); @@ -33,12 +33,15 @@ public StockView(ViewManagerModel viewManagerModel) { contentPanel.setBackground(Color.WHITE); initializeComponents(contentPanel); + final StockFactory stockFactory = new CommonStockFactory(); final Stock stock = stockFactory.create("NVDA", 132.2, 130.5, 100000000, 140.32, 128.9); updateStockData(stock); - add(contentPanel, BorderLayout.CENTER); + add(Box.createVerticalGlue()); + add(contentPanel); + add(Box.createVerticalGlue()); } private void initializeComponents(JPanel contentPanel) { @@ -52,20 +55,22 @@ private void initializeComponents(JPanel contentPanel) { stockInfoPanel.add(stockSymbolLabel); stockInfoPanel.add(stockPriceLabel); - contentPanel.add(stockInfoPanel); - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - openPriceLabel = createLabel("", 16, Font.PLAIN); closePriceLabel = createLabel("", 16, Font.PLAIN); lowPriceLabel = createLabel("", 16, Font.PLAIN); highPriceLabel = createLabel("", 16, Font.PLAIN); volumeLabel = createLabel("", 16, Font.PLAIN); + contentPanel.add(stockInfoPanel); + contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); contentPanel.add(openPriceLabel); contentPanel.add(closePriceLabel); contentPanel.add(lowPriceLabel); contentPanel.add(highPriceLabel); contentPanel.add(volumeLabel); + + contentPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + stockInfoPanel.setAlignmentX(Component.LEFT_ALIGNMENT); } public void updateStockData(Stock stock) { @@ -76,6 +81,15 @@ public void updateStockData(Stock stock) { lowPriceLabel.setText("Low: $" + stock.getLow()); highPriceLabel.setText("High: $" + stock.getHigh()); volumeLabel.setText("Volume: " + stock.getVolume() + " M"); + + double volume = stock.getVolume(); + if (volume >= 1_000_000) { + volumeLabel.setText("Volume: " + (volume / 1_000_000) + " M"); + } else if (volume >= 1_000) { + volumeLabel.setText("Volume: " + (volume / 1_000) + " K"); + } else { + volumeLabel.setText("Volume: " + volume); + } } private JLabel createLabel(String text, int fontSize, int fontStyle) { @@ -83,7 +97,6 @@ private JLabel createLabel(String text, int fontSize, int fontStyle) { label.setFont(new Font("SansSerif", fontStyle, fontSize)); label.setAlignmentX(Component.LEFT_ALIGNMENT); return label; - } public String getViewName() { From ce15a87aebc9e628a16cc64dd8fc9066c2b95f43 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Sun, 17 Nov 2024 20:43:02 -0500 Subject: [PATCH 035/113] Update README.md --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 45bd0b6f4..73931fe61 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,31 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed `access_key`: 3847d86b56ca461a0da759024332c06a +## Project Blueprint +image +image +image +image +image + +## Week 2 Progress +`Home Page` +image + +`Stock View` +image + +`Portfolio` +image + +`Watchlist` +image + + + + + + + + + From 0dec0aab5de1b19f172caaf91715d4b9b1577ef1 Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 17 Nov 2024 21:03:56 -0500 Subject: [PATCH 036/113] back and value --- src/main/java/view/StockView.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 7d569ae88..d7ec437ef 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -25,9 +25,11 @@ public class StockView extends AbstractViewWithBackButton { public StockView(ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; + // 设置垂直布局 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBackground(Color.WHITE); + // 内容面板 final JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); @@ -39,13 +41,14 @@ public StockView(ViewManagerModel viewManagerModel) { 100000000, 140.32, 128.9); updateStockData(stock); + // 添加内容面板 add(Box.createVerticalGlue()); add(contentPanel); add(Box.createVerticalGlue()); } private void initializeComponents(JPanel contentPanel) { - + // 股票基础信息 stockSymbolLabel = createLabel("", 24, Font.BOLD); stockPriceLabel = createLabel("", 18, Font.PLAIN); @@ -55,12 +58,14 @@ private void initializeComponents(JPanel contentPanel) { stockInfoPanel.add(stockSymbolLabel); stockInfoPanel.add(stockPriceLabel); + // 股票详细信息 openPriceLabel = createLabel("", 16, Font.PLAIN); closePriceLabel = createLabel("", 16, Font.PLAIN); lowPriceLabel = createLabel("", 16, Font.PLAIN); highPriceLabel = createLabel("", 16, Font.PLAIN); volumeLabel = createLabel("", 16, Font.PLAIN); + // 添加到内容面板 contentPanel.add(stockInfoPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); contentPanel.add(openPriceLabel); @@ -69,6 +74,7 @@ private void initializeComponents(JPanel contentPanel) { contentPanel.add(highPriceLabel); contentPanel.add(volumeLabel); + // 设置左对齐,消除空白 contentPanel.setAlignmentX(Component.LEFT_ALIGNMENT); stockInfoPanel.setAlignmentX(Component.LEFT_ALIGNMENT); } From ebe33bc30350be57077e87bf344d983cb186e713 Mon Sep 17 00:00:00 2001 From: zifanguo2004 Date: Sun, 17 Nov 2024 21:12:13 -0500 Subject: [PATCH 037/113] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 73931fe61..554351c86 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed image `Stock View` -image +image `Portfolio` image @@ -71,3 +71,4 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed + From e4f8fd2f1565c3f49a0d957423956b1ee87c2ae8 Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 17 Nov 2024 21:21:48 -0500 Subject: [PATCH 038/113] star and back --- src/main/java/view/StockView.java | 42 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index d7ec437ef..037236d16 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -25,11 +25,9 @@ public class StockView extends AbstractViewWithBackButton { public StockView(ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; - // 设置垂直布局 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBackground(Color.WHITE); - // 内容面板 final JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); @@ -41,14 +39,14 @@ public StockView(ViewManagerModel viewManagerModel) { 100000000, 140.32, 128.9); updateStockData(stock); - // 添加内容面板 add(Box.createVerticalGlue()); add(contentPanel); + add(Box.createRigidArea(new Dimension(0, 20))); + add(createActionButtonsPanel()); add(Box.createVerticalGlue()); } private void initializeComponents(JPanel contentPanel) { - // 股票基础信息 stockSymbolLabel = createLabel("", 24, Font.BOLD); stockPriceLabel = createLabel("", 18, Font.PLAIN); @@ -58,14 +56,12 @@ private void initializeComponents(JPanel contentPanel) { stockInfoPanel.add(stockSymbolLabel); stockInfoPanel.add(stockPriceLabel); - // 股票详细信息 openPriceLabel = createLabel("", 16, Font.PLAIN); closePriceLabel = createLabel("", 16, Font.PLAIN); lowPriceLabel = createLabel("", 16, Font.PLAIN); highPriceLabel = createLabel("", 16, Font.PLAIN); volumeLabel = createLabel("", 16, Font.PLAIN); - // 添加到内容面板 contentPanel.add(stockInfoPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); contentPanel.add(openPriceLabel); @@ -74,11 +70,43 @@ private void initializeComponents(JPanel contentPanel) { contentPanel.add(highPriceLabel); contentPanel.add(volumeLabel); - // 设置左对齐,消除空白 contentPanel.setAlignmentX(Component.LEFT_ALIGNMENT); stockInfoPanel.setAlignmentX(Component.LEFT_ALIGNMENT); } + private JPanel createActionButtonsPanel() { + JPanel actionPanel = new JPanel(); + actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS)); + actionPanel.setBackground(Color.WHITE); + actionPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + JButton buyButton = new JButton("BUY"); + buyButton.setFont(new Font("SansSerif", Font.BOLD, 18)); + buyButton.setAlignmentX(Component.CENTER_ALIGNMENT); + buyButton.setFocusPainted(false); + + buyButton.addActionListener(e -> { + JOptionPane.showMessageDialog(this, "Buy action triggered!"); + }); + + JButton favoriteButton = new JButton("★"); + favoriteButton.setFont(new Font("SansSerif", Font.BOLD, 18)); + favoriteButton.setAlignmentX(Component.CENTER_ALIGNMENT); + favoriteButton.setFocusPainted(false); + + favoriteButton.addActionListener(e -> { + JOptionPane.showMessageDialog(this, "Added to favorites!"); + }); + + actionPanel.add(Box.createHorizontalGlue()); + actionPanel.add(buyButton); + actionPanel.add(Box.createRigidArea(new Dimension(20, 0))); // 两个按钮间距 + actionPanel.add(favoriteButton); + actionPanel.add(Box.createHorizontalGlue()); + + return actionPanel; + } + public void updateStockData(Stock stock) { stockSymbolLabel.setText(stock.getSymbol()); stockPriceLabel.setText("Current Price: $" + stock.getClosePrice()); From 065c765af158fca8a1aaa28f5483630b6ce6ee46 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 18 Nov 2024 10:31:14 -0500 Subject: [PATCH 039/113] panel --- src/main/java/app/AppBuilder.java | 2 +- src/main/java/view/StockView.java | 55 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index c58269d24..552bb23aa 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -161,7 +161,7 @@ public AppBuilder addPortfolioView() { */ public AppBuilder addStockView() { stockViewModel = new StockViewModel(); - stockView = new StockView(viewManagerModel); + stockView = new StockView(stockViewModel, viewManagerModel); cardPanel.add(stockView, stockView.getViewName()); return this; } diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 037236d16..e2cd8eddf 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -1,19 +1,21 @@ package view; -import entity.CommonStockFactory; import entity.Stock; -import entity.StockFactory; import interface_adapter.ViewManagerModel; +import interface_adapter.stock_view.StockViewModel; import javax.swing.*; import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; /** * The View for displaying detailed information about a single stock. */ -public class StockView extends AbstractViewWithBackButton { +public class StockView extends AbstractViewWithBackButton implements PropertyChangeListener { private final ViewManagerModel viewManagerModel; + private final StockViewModel stockViewModel; private JLabel stockSymbolLabel; private JLabel stockPriceLabel; private JLabel openPriceLabel; @@ -22,8 +24,11 @@ public class StockView extends AbstractViewWithBackButton { private JLabel highPriceLabel; private JLabel volumeLabel; - public StockView(ViewManagerModel viewManagerModel) { + public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; + this.stockViewModel = stockViewModel; + + this.stockViewModel.addPropertyChangeListener(this); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBackground(Color.WHITE); @@ -34,11 +39,6 @@ public StockView(ViewManagerModel viewManagerModel) { initializeComponents(contentPanel); - final StockFactory stockFactory = new CommonStockFactory(); - final Stock stock = stockFactory.create("NVDA", 132.2, 130.5, - 100000000, 140.32, 128.9); - updateStockData(stock); - add(Box.createVerticalGlue()); add(contentPanel); add(Box.createRigidArea(new Dimension(0, 20))); @@ -82,25 +82,17 @@ private JPanel createActionButtonsPanel() { JButton buyButton = new JButton("BUY"); buyButton.setFont(new Font("SansSerif", Font.BOLD, 18)); - buyButton.setAlignmentX(Component.CENTER_ALIGNMENT); buyButton.setFocusPainted(false); - - buyButton.addActionListener(e -> { - JOptionPane.showMessageDialog(this, "Buy action triggered!"); - }); + buyButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "Buy action triggered!")); JButton favoriteButton = new JButton("★"); favoriteButton.setFont(new Font("SansSerif", Font.BOLD, 18)); - favoriteButton.setAlignmentX(Component.CENTER_ALIGNMENT); favoriteButton.setFocusPainted(false); - - favoriteButton.addActionListener(e -> { - JOptionPane.showMessageDialog(this, "Added to favorites!"); - }); + favoriteButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "Added to favorites!")); actionPanel.add(Box.createHorizontalGlue()); actionPanel.add(buyButton); - actionPanel.add(Box.createRigidArea(new Dimension(20, 0))); // 两个按钮间距 + actionPanel.add(Box.createRigidArea(new Dimension(20, 0))); actionPanel.add(favoriteButton); actionPanel.add(Box.createHorizontalGlue()); @@ -114,16 +106,12 @@ public void updateStockData(Stock stock) { closePriceLabel.setText("Close Price: $" + stock.getClosePrice()); lowPriceLabel.setText("Low: $" + stock.getLow()); highPriceLabel.setText("High: $" + stock.getHigh()); - volumeLabel.setText("Volume: " + stock.getVolume() + " M"); double volume = stock.getVolume(); - if (volume >= 1_000_000) { - volumeLabel.setText("Volume: " + (volume / 1_000_000) + " M"); - } else if (volume >= 1_000) { - volumeLabel.setText("Volume: " + (volume / 1_000) + " K"); - } else { - volumeLabel.setText("Volume: " + volume); - } + String volumeText = (volume >= 1_000_000) ? (volume / 1_000_000) + " M" : + (volume >= 1_000) ? (volume / 1_000) + " K" : + String.valueOf(volume); + volumeLabel.setText("Volume: " + volumeText); } private JLabel createLabel(String text, int fontSize, int fontStyle) { @@ -142,4 +130,15 @@ void backButtonAction() { viewManagerModel.setState("home view"); viewManagerModel.firePropertyChanged(); } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + // React to StockViewModel's state changes + if ("state".equals(evt.getPropertyName())) { + Stock updatedStock = stockViewModel.getState().getStock(); + if (updatedStock != null) { + updateStockData(updatedStock); + } + } + } } \ No newline at end of file From e5a1dc79213253ff763de31d4b9563f335eb7ddb Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Mon, 18 Nov 2024 10:36:22 -0500 Subject: [PATCH 040/113] update search test case --- .../home_view/HomeViewInteratorTest.java | 81 +++++++++++++++++++ .../use_case/login/LoginInteractorTest.java | 27 ++++++- .../use_case/logout/LogoutInteractorTest.java | 4 +- .../use_case/signup/SignupInteractorTest.java | 3 +- 4 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 src/test/java/use_case/home_view/HomeViewInteratorTest.java diff --git a/src/test/java/use_case/home_view/HomeViewInteratorTest.java b/src/test/java/use_case/home_view/HomeViewInteratorTest.java new file mode 100644 index 000000000..5ce5b9e27 --- /dev/null +++ b/src/test/java/use_case/home_view/HomeViewInteratorTest.java @@ -0,0 +1,81 @@ +package use_case.home_view; + +import data_access.DBUserDataAccessObject; +import data_access.InMemoryUserDataAccessObject; +import entity.*; +import org.junit.Test; +import use_case.login.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class HomeViewInteratorTest { + + @Test + public void successTest() { +// LoginInputData inputData = new LoginInputData("Paul", "password"); +// LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); +// +// // For the success test, we need to add Paul to the data access repository before we log in. +// UserFactory factory = new CommonUserFactory(); +// User user = factory.create("Paul", "password"); +// userRepository.save(user); +// +// // This creates a successPresenter that tests whether the test case is as we expect. +// LoginOutputBoundary successPresenter = new LoginOutputBoundary() { +// @Override +// public void prepareSuccessView(LoginOutputData user) { +// assertEquals("Paul", user.getUsername()); +// } +// +// @Override +// public void prepareFailView(String error) { +// fail("Use case failure is unexpected."); +// } +// }; +// +// LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); +// interactor.execute(inputData); + + final HomeInputData inputData = new HomeInputData("NVDA"); + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final HomeDataAccessInterface homeData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(HomeOutputData searchOutputData) { + assertEquals("NVDA", searchOutputData.getStock().getSymbol()); + } + + @Override + public void prepareFailView(String errorMessage) { + // Do nothing right now + } + + @Override + public void switchToPortfolio() { + // Do nothing right now + } + + @Override + public void switchToWatchList() { + // Do nothing right now + } + + @Override + public void switchToLoginView() { + // Do nothing right now + } + + @Override + public void switchToSignupView() { + // Do nothing right now + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(homeData, successPresenter); + interactor.search(inputData); + } +} diff --git a/src/test/java/use_case/login/LoginInteractorTest.java b/src/test/java/use_case/login/LoginInteractorTest.java index 9fc1bfc89..3f4c442e3 100644 --- a/src/test/java/use_case/login/LoginInteractorTest.java +++ b/src/test/java/use_case/login/LoginInteractorTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.util.ArrayList; import static org.junit.jupiter.api.Assertions.*; @@ -19,7 +20,7 @@ void successTest() { // For the success test, we need to add Paul to the data access repository before we log in. UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password"); + User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); userRepository.save(user); // This creates a successPresenter that tests whether the test case is as we expect. @@ -33,6 +34,11 @@ public void prepareSuccessView(LoginOutputData user) { public void prepareFailView(String error) { fail("Use case failure is unexpected."); } + + @Override + public void switchToSignUpView() { + // Do nothing + } }; LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); @@ -46,7 +52,7 @@ void successUserLoggedInTest() { // For the success test, we need to add Paul to the data access repository before we log in. UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password"); + User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); userRepository.save(user); // This creates a successPresenter that tests whether the test case is as we expect. @@ -60,6 +66,11 @@ public void prepareSuccessView(LoginOutputData user) { public void prepareFailView(String error) { fail("Use case failure is unexpected."); } + + @Override + public void switchToSignUpView() { + // Do nothing + } }; LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); @@ -76,7 +87,7 @@ void failurePasswordMismatchTest() { // For this failure test, we need to add Paul to the data access repository before we log in, and // the passwords should not match. UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password"); + User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); userRepository.save(user); // This creates a presenter that tests whether the test case is as we expect. @@ -91,6 +102,11 @@ public void prepareSuccessView(LoginOutputData user) { public void prepareFailView(String error) { assertEquals("Incorrect password for \"Paul\".", error); } + + @Override + public void switchToSignUpView() { + // Do nothing + } }; LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter); @@ -116,6 +132,11 @@ public void prepareSuccessView(LoginOutputData user) { public void prepareFailView(String error) { assertEquals("Paul: Account does not exist.", error); } + + @Override + public void switchToSignUpView() { + // Do nothing + } }; LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter); diff --git a/src/test/java/use_case/logout/LogoutInteractorTest.java b/src/test/java/use_case/logout/LogoutInteractorTest.java index b005a402c..5906b27cd 100644 --- a/src/test/java/use_case/logout/LogoutInteractorTest.java +++ b/src/test/java/use_case/logout/LogoutInteractorTest.java @@ -6,6 +6,8 @@ import entity.UserFactory; import org.junit.jupiter.api.Test; +import java.util.ArrayList; + import static org.junit.jupiter.api.Assertions.*; class LogoutInteractorTest { @@ -17,7 +19,7 @@ void successTest() { // For the success test, we need to add Paul to the data access repository before we log in. UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password"); + User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); userRepository.save(user); userRepository.setCurrentUsername("Paul"); diff --git a/src/test/java/use_case/signup/SignupInteractorTest.java b/src/test/java/use_case/signup/SignupInteractorTest.java index 00f757c1c..224fe78c0 100644 --- a/src/test/java/use_case/signup/SignupInteractorTest.java +++ b/src/test/java/use_case/signup/SignupInteractorTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.util.ArrayList; import static org.junit.jupiter.api.Assertions.*; @@ -76,7 +77,7 @@ void failureUserExistsTest() { // Add Paul to the repo so that when we check later they already exist UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "pwd"); + User user = factory.create("Paul", "pwd", new ArrayList<>(), new ArrayList<>()); userRepository.save(user); // This creates a presenter that tests whether the test case is as we expect. From e4bb66de5ed2f77e36e51fc3444abbebe739a745 Mon Sep 17 00:00:00 2001 From: Ryan Jin Date: Mon, 18 Nov 2024 11:23:36 -0500 Subject: [PATCH 041/113] test --- .idea/misc.xml | 3 +-- src/main/java/view/LoggedInView.java | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index f6a5e8a7f..67e1e6113 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,6 +8,5 @@ - ->>>>>>> ac4333e79be2fda7912a17e0b6ff5d3db6737ace + \ No newline at end of file diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java index f9662ea41..2a3ac190b 100644 --- a/src/main/java/view/LoggedInView.java +++ b/src/main/java/view/LoggedInView.java @@ -49,6 +49,7 @@ public LoggedInView(LoggedInViewModel loggedInViewModel) { final JLabel usernameInfo = new JLabel("Currently logged in: "); username = new JLabel(); + //123 final JPanel buttons = new JPanel(); logOut = new JButton("Log Out"); buttons.add(logOut); From d59a84042e5f48e63b273fa6d9c94e37ed6b52dc Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 18 Nov 2024 13:33:48 -0500 Subject: [PATCH 042/113] add getDailyChange and getDailyPercentage method in Stock --- src/main/java/entity/CommonStock.java | 32 ++++++++++++++++++--------- src/main/java/entity/Stock.java | 22 +++++++++++++----- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/main/java/entity/CommonStock.java b/src/main/java/entity/CommonStock.java index e54e6b443..09221615b 100644 --- a/src/main/java/entity/CommonStock.java +++ b/src/main/java/entity/CommonStock.java @@ -6,13 +6,13 @@ public class CommonStock implements Stock { private final String symbol; - private final int openPrice; - private final int closePrice; - private final int volume; - private final int high; - private final int low; + private final double openPrice; + private final double closePrice; + private final double volume; + private final double high; + private final double low; - public CommonStock(String symbol, int openPrice, int closePrice, int volume, int high, int low) { + public CommonStock(String symbol, double openPrice, double closePrice, double volume, double high, double low) { this.symbol = symbol; this.openPrice = openPrice; this.closePrice = closePrice; @@ -27,27 +27,37 @@ public String getSymbol() { } @Override - public int getOpenPrice() { + public double getOpenPrice() { return openPrice; } @Override - public int getClosePrice() { + public double getClosePrice() { return closePrice; } @Override - public int getVolume() { + public double getVolume() { return volume; } @Override - public int getHigh() { + public double getHigh() { return high; } @Override - public int getLow() { + public double getLow() { return low; } + + @Override + public double getDailyChange() { + return closePrice - openPrice; + } + + @Override + public double getDailyPercentage() { + return (closePrice - openPrice) / closePrice; + } } diff --git a/src/main/java/entity/Stock.java b/src/main/java/entity/Stock.java index f588e6cde..1a2a51da8 100644 --- a/src/main/java/entity/Stock.java +++ b/src/main/java/entity/Stock.java @@ -15,29 +15,41 @@ public interface Stock { * Returns the stock open price of the stock. * @return the stock open price of the stock. */ - int getOpenPrice(); + double getOpenPrice(); /** * Returns the stock close price of the stock. * @return the stock close price of the stock. */ - int getClosePrice(); + double getClosePrice(); /** * Returns the stock volume of the stock. * @return the stock volume of the stock. */ - int getVolume(); + double getVolume(); /** * Returns the stock highest price of the stock. * @return the stock highest price of the stock. */ - int getHigh(); + double getHigh(); /** * Returns the stock lowest price of the stock. * @return the stock lowest price of the stock. */ - int getLow(); + double getLow(); + + /** + * Returns the stock dailyChange price of the stock. + * @return the stock dailyChange price of the stock. + */ + double getDailyChange(); + + /** + * Returns the stock dailyPercentage of price of the stock. + * @return the stock dailyPercentage of price of the stock. + */ + double getDailyPercentage(); } From fca0dbf06472b9663ded8719c536286bd25276b6 Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 18 Nov 2024 13:41:57 -0500 Subject: [PATCH 043/113] add listener to watchlist that can update the watchlist and also can create a watchlist view by marketapi --- src/main/java/app/AppBuilder.java | 58 ++++++++++++++----- .../watchlist_view/WatchListViewModel.java | 26 ++++++--- .../watchlist_view/WatchListViewState.java | 21 +++++++ src/main/java/view/WatchListView.java | 54 ++++++++++++++--- 4 files changed, 129 insertions(+), 30 deletions(-) create mode 100644 src/main/java/interface_adapter/watchlist_view/WatchListViewState.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index a9a033d41..0ffc2dbff 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -1,17 +1,16 @@ package app; import java.awt.CardLayout; +import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.WindowConstants; import data_access.DBUserDataAccessObject; +import data_access.FileUserDataAccessObject; import data_access.InMemoryUserDataAccessObject; -import entity.CommonStockFactory; -import entity.CommonUserFactory; -import entity.StockFactory; -import entity.UserFactory; +import entity.*; import interface_adapter.ViewManagerModel; import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; @@ -24,10 +23,13 @@ import interface_adapter.login.LoginViewModel; import interface_adapter.logout.LogoutController; import interface_adapter.logout.LogoutPresenter; +import interface_adapter.portfolio.PortfolioViewModel; import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupPresenter; import interface_adapter.signup.SignupViewModel; +import interface_adapter.stock_view.StockViewModel; import interface_adapter.watchlist_view.WatchListViewModel; +import interface_adapter.portfolio.PortfolioViewModel; import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; @@ -60,11 +62,13 @@ public class AppBuilder { // thought question: is the hard dependency below a problem? private final UserFactory userFactory = new CommonUserFactory(); private final StockFactory stockFactory = new CommonStockFactory(); + private final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); private final ViewManagerModel viewManagerModel = new ViewManagerModel(); private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel); // thought question: is the hard dependency below a problem? - private final DBUserDataAccessObject userDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); + private final DBUserDataAccessObject dbUserDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); + private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(stockFactory, simulatedHoldingFactory); private HomeView homeView; private HomeViewModel homeViewModel; @@ -76,6 +80,11 @@ public class AppBuilder { private LoginView loginView; private WatchListView watchListView; private WatchListViewModel watchListViewModel; + private PortfolioView portfolioView; + private PortfolioViewModel portfolioViewModel; + private StockView stockView; + private StockViewModel stockViewModel; + private ArrayList watchList = fileUserDataAccessObject.getWatchList(); public AppBuilder() { cardPanel.setLayout(cardLayout); @@ -89,6 +98,7 @@ public AppBuilder addHomeView() { homeViewModel = new HomeViewModel(); homeView = new HomeView(homeViewModel); cardPanel.add(homeView, homeView.getViewName()); + return this; } @@ -131,19 +141,41 @@ public AppBuilder addLoggedInView() { */ public AppBuilder addWatchListView() { watchListViewModel = new WatchListViewModel(); - watchListView = new WatchListView(watchListViewModel, viewManagerModel); + watchListView = new WatchListView(watchListViewModel, viewManagerModel,dbUserDataAccessObject); cardPanel.add(watchListView, watchListView.getViewName()); return this; } + /** + * Adds the Portfolio View to the application. + * @return this builder + */ + public AppBuilder addPortfolioView() { + portfolioViewModel = new PortfolioViewModel(); + portfolioView = new PortfolioView(portfolioViewModel, viewManagerModel); + cardPanel.add(portfolioView, portfolioView.getViewName()); + return this; + } + + /** + * Adds the Stock View to the application. + * @return this builder + */ + public AppBuilder addStockView() { + stockViewModel = new StockViewModel(); + stockView = new StockView(viewManagerModel); + cardPanel.add(stockView, stockView.getViewName()); + return this; + } + /** * Adds the Home Use Case to the application. * @return this builder */ public AppBuilder addHomeUseCase() { final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, - loginViewModel, signupViewModel, viewManagerModel); - final HomeInputBoundary homeInteractor = new HomeInteractor(userDataAccessObject, homeOutputBoundary); + loginViewModel, signupViewModel, viewManagerModel, portfolioViewModel, stockViewModel); + final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); homeView.setHomeController(controller); @@ -158,7 +190,7 @@ public AppBuilder addSignupUseCase() { final SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel, signupViewModel, loginViewModel); final SignupInputBoundary userSignupInteractor = new SignupInteractor( - userDataAccessObject, signupOutputBoundary, userFactory); + dbUserDataAccessObject, signupOutputBoundary, userFactory); final SignupController controller = new SignupController(userSignupInteractor); signupView.setSignupController(controller); @@ -171,9 +203,9 @@ public AppBuilder addSignupUseCase() { */ public AppBuilder addLoginUseCase() { final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel, - loggedInViewModel, loginViewModel); + loggedInViewModel, loginViewModel, signupViewModel, homeViewModel); final LoginInputBoundary loginInteractor = new LoginInteractor( - userDataAccessObject, loginOutputBoundary); + dbUserDataAccessObject, loginOutputBoundary); final LoginController loginController = new LoginController(loginInteractor); loginView.setLoginController(loginController); @@ -189,7 +221,7 @@ public AppBuilder addChangePasswordUseCase() { new ChangePasswordPresenter(loggedInViewModel); final ChangePasswordInputBoundary changePasswordInteractor = - new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory); + new ChangePasswordInteractor(dbUserDataAccessObject, changePasswordOutputBoundary, userFactory); final ChangePasswordController changePasswordController = new ChangePasswordController(changePasswordInteractor); @@ -206,7 +238,7 @@ public AppBuilder addLogoutUseCase() { loggedInViewModel, loginViewModel); final LogoutInputBoundary logoutInteractor = - new LogoutInteractor(userDataAccessObject, logoutOutputBoundary); + new LogoutInteractor(dbUserDataAccessObject, logoutOutputBoundary); final LogoutController logoutController = new LogoutController(logoutInteractor); loggedInView.setLogoutController(logoutController); diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java index 7caf274fc..adaf8a2e7 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java @@ -1,24 +1,32 @@ package interface_adapter.watchlist_view; +import java.beans.PropertyChangeSupport; import java.util.ArrayList; -import java.util.List; /** * The View Model for the WatchList View. */ public class WatchListViewModel { - private List stockList; + private final ArrayList watchList = new ArrayList<>(); + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - public WatchListViewModel() { - this.stockList = new ArrayList<>(); + public ArrayList getWatchList() { + return new ArrayList<>(watchList); } - public List getStockList() { - return stockList; + // add to watchlist + public void addWatchList(String symbol) { + watchList.add(symbol); + pcs.firePropertyChange("watchList", null, new ArrayList<>(watchList)); } - public void setStockList(List stockList) { - this.stockList = stockList; + // remove from watchlist + public void removeWatchList(String symbol) { + watchList.remove(symbol); + pcs.firePropertyChange("watchList", null, new ArrayList<>(watchList)); + } + // return State of Stock, return ture if stock is already in the watchlist, false else + public boolean checkStockState(String symbol) { + return watchList.contains(symbol); } - } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java new file mode 100644 index 000000000..bc49f62f3 --- /dev/null +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java @@ -0,0 +1,21 @@ +package interface_adapter.stock_view; + +import entity.Stock; + +import java.util.ArrayList; + +/** + * The state for the Stock View Model. + */ +public class WatchListViewState { + private Stock stock; + private ArrayList watchlist; + + public Stock getStock() { + return stock; + } + + public void setStock(Stock newStock) { + this.stock = newStock; + } +} diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index eb744ce1e..c9c041a5d 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -1,20 +1,30 @@ package view; +import data_access.DBUserDataAccessObject; +import entity.Stock; import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.LoggedInState; +import interface_adapter.login.LoginState; import interface_adapter.watchlist_view.WatchListViewModel; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; -public class WatchListView extends JPanel { +public class WatchListView extends JPanel implements PropertyChangeListener { private final ViewManagerModel viewManagerModel; + private final DBUserDataAccessObject dbUserDataAccessObject; + private final JPanel contentPanel = new JPanel(); - public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel) { + public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel, DBUserDataAccessObject dbUserDataAccessObject) { this.viewManagerModel = viewManagerModel; + this.dbUserDataAccessObject = dbUserDataAccessObject; setLayout(new BorderLayout()); setBackground(Color.WHITE); @@ -42,7 +52,6 @@ public void actionPerformed(ActionEvent e) { add(topPanel, BorderLayout.NORTH); // stock information - final JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); @@ -51,9 +60,10 @@ public void actionPerformed(ActionEvent e) { addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); add(contentPanel, BorderLayout.CENTER); + } - private void addStockItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage, String totalChange, String totalPercentage) { + private void addStockItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage, String volume) { final JPanel stockPanel = new JPanel(new BorderLayout()); stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); stockPanel.setBackground(Color.WHITE); @@ -77,13 +87,13 @@ private void addStockItem(JPanel contentPanel, String code, String price, String rightPanel.setBackground(Color.WHITE); rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); - final JLabel dailyChangeLabel = new JLabel(dailyChange + "(" + dailyPercentage + ")"); + final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - final JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); - totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + final JLabel volumeLabel = new JLabel("Volume: " + volume); + volumeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); rightPanel.add(dailyChangeLabel); - rightPanel.add(totalChangeLabel); + rightPanel.add(volumeLabel); // Add all to stockPanel stockPanel.add(leftPanel, BorderLayout.WEST); @@ -93,8 +103,36 @@ private void addStockItem(JPanel contentPanel, String code, String price, String contentPanel.add(stockPanel); } + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("watchList".equals(evt.getPropertyName())) { + final ArrayList updatedWatchList = (ArrayList) evt.getNewValue(); + updateWatchList(updatedWatchList); + } + } + + private void updateWatchList(ArrayList updatedWatchList) { + contentPanel.removeAll(); + createWatchListview(updatedWatchList); + contentPanel.revalidate(); + contentPanel.repaint(); + } + + private void createWatchListview(ArrayList WatchList) { + for (String stockcode: WatchList) { + final Stock stock = dbUserDataAccessObject.getStock(stockcode); + final String price = Double.toString(stock.getClosePrice()); + final String dailyChange = Double.toString(stock.getDailyChange()); + final String dailyPercentage = Double.toString((stock.getDailyChange() / stock.getOpenPrice()) * 100) + "%"; + final String volume = Double.toString(stock.getVolume()); + + addStockItem(contentPanel, stock.getSymbol(), "$" + price, dailyChange, dailyPercentage, volume); + } + } + public String getViewName() { return "WatchListView"; } + } From 4f4845ec2cba3a2f6e75c87714d2859cc5633026 Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:14:08 -0500 Subject: [PATCH 044/113] Update CommonStock.java --- src/main/java/entity/CommonStock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/entity/CommonStock.java b/src/main/java/entity/CommonStock.java index 09221615b..9f9d9c868 100644 --- a/src/main/java/entity/CommonStock.java +++ b/src/main/java/entity/CommonStock.java @@ -58,6 +58,6 @@ public double getDailyChange() { @Override public double getDailyPercentage() { - return (closePrice - openPrice) / closePrice; + return (closePrice - openPrice) / openPrice * 100; } } From a47f8d50749ad2bd9196399c182b7b1436d9b35f Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Mon, 18 Nov 2024 17:44:42 -0500 Subject: [PATCH 045/113] Display and update watch list in home page --- src/main/java/app/AppBuilder.java | 2 +- src/main/java/data_access/watchlist.txt | 2 +- .../home_view/HomeController.java | 14 +- .../home_view/HomePresenter.java | 16 ++- .../home_view/HomeState.java | 13 ++ .../use_case/home_view/HomeInputBoundary.java | 10 +- .../use_case/home_view/HomeInteractor.java | 49 ++++++- .../home_view/HomeOutputBoundary.java | 12 +- ...omeInputData.java => SearchInputData.java} | 4 +- ...eOutputData.java => SearchOutputData.java} | 4 +- src/main/java/view/HomeView.java | 124 ++++++++++++++---- .../home_view/HomeViewInteratorTest.java | 12 +- 12 files changed, 209 insertions(+), 53 deletions(-) rename src/main/java/use_case/home_view/{HomeInputData.java => SearchInputData.java} (71%) rename src/main/java/use_case/home_view/{HomeOutputData.java => SearchOutputData.java} (72%) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index c58269d24..4d1a66fde 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -173,7 +173,7 @@ public AppBuilder addStockView() { public AppBuilder addHomeUseCase() { final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, loginViewModel, signupViewModel, viewManagerModel, portfolioViewModel, stockViewModel); - final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, homeOutputBoundary); + final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, fileUserDataAccessObject, homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); homeView.setHomeController(controller); diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index 9834f10f0..eda60370a 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -NVDA,AAPL,AAPL +NVDA,AAPL,META diff --git a/src/main/java/interface_adapter/home_view/HomeController.java b/src/main/java/interface_adapter/home_view/HomeController.java index e2d754d60..875baf233 100644 --- a/src/main/java/interface_adapter/home_view/HomeController.java +++ b/src/main/java/interface_adapter/home_view/HomeController.java @@ -1,7 +1,10 @@ package interface_adapter.home_view; +import entity.Stock; import use_case.home_view.HomeInputBoundary; -import use_case.home_view.HomeInputData; +import use_case.home_view.SearchInputData; + +import java.util.ArrayList; /** * The controller for the Login Use Case. @@ -19,7 +22,7 @@ public HomeController(HomeInputBoundary homeUseCaseInteractor) { * @param symbol the username of the user logging in */ public void search(String symbol) { - final HomeInputData searchInputData = new HomeInputData(symbol); + final SearchInputData searchInputData = new SearchInputData(symbol); homeUseCaseInteractor.search(searchInputData); } @@ -50,4 +53,11 @@ public void switchToLoginView() { public void switchToSignupView() { homeUseCaseInteractor.switchToSignupView(); } + + /** + * Load watch list data. + */ + public void getWatchList() { + homeUseCaseInteractor.getWatchListData(); + } } diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index 728dcb5c6..9eeafbae1 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -1,5 +1,6 @@ package interface_adapter.home_view; +import entity.Stock; import interface_adapter.ViewManagerModel; import interface_adapter.login.LoginViewModel; import interface_adapter.portfolio.PortfolioViewModel; @@ -7,8 +8,9 @@ import interface_adapter.stock_view.StockViewModel; import interface_adapter.stock_view.StockViewState; import use_case.home_view.HomeOutputBoundary; -import use_case.home_view.HomeOutputData; -import view.WatchListView; +import use_case.home_view.SearchOutputData; + +import java.util.ArrayList; /** * The Presenter for the Search Use Case. @@ -38,7 +40,7 @@ public HomePresenter(HomeViewModel homeViewModel, } @Override - public void prepareSuccessView(HomeOutputData searchOutputData) { + public void prepareSuccessView(SearchOutputData searchOutputData) { final StockViewState stockViewState = new StockViewState(); stockViewState.setStock(searchOutputData.getStock()); this.stockViewModel.setState(stockViewState); @@ -77,4 +79,12 @@ public void switchToSignupView() { viewManagerModel.setState(loginViewModel.getViewName()); viewManagerModel.firePropertyChanged(); } + + @Override + public void getWatchListData(ArrayList watchList) { + final HomeState homeState = new HomeState(); + homeState.setWatchList(watchList); + this.homeViewModel.setState(homeState); + this.homeViewModel.firePropertyChanged(); + } } diff --git a/src/main/java/interface_adapter/home_view/HomeState.java b/src/main/java/interface_adapter/home_view/HomeState.java index ae0cf298e..43eb5bc77 100644 --- a/src/main/java/interface_adapter/home_view/HomeState.java +++ b/src/main/java/interface_adapter/home_view/HomeState.java @@ -1,10 +1,15 @@ package interface_adapter.home_view; +import entity.Stock; + +import java.util.ArrayList; + /** * The state for the Home View Model. */ public class HomeState { private String symbol; + private ArrayList watchList; public String getSymbol() { return symbol; @@ -13,4 +18,12 @@ public String getSymbol() { public void setSymbol(String symbol) { this.symbol = symbol; } + + public ArrayList getWatchList() { + return watchList; + } + + public void setWatchList(ArrayList watchList) { + this.watchList = watchList; + } } diff --git a/src/main/java/use_case/home_view/HomeInputBoundary.java b/src/main/java/use_case/home_view/HomeInputBoundary.java index 9542715c7..5fe2c54df 100644 --- a/src/main/java/use_case/home_view/HomeInputBoundary.java +++ b/src/main/java/use_case/home_view/HomeInputBoundary.java @@ -1,12 +1,16 @@ package use_case.home_view; +import entity.Stock; + +import java.util.ArrayList; + public interface HomeInputBoundary { /** * Executes the search use case. * @param searchInputData the input data */ - void search(HomeInputData searchInputData); + void search(SearchInputData searchInputData); /** * Executes the switch to portfolio view use case. @@ -28,4 +32,8 @@ public interface HomeInputBoundary { */ void switchToSignupView(); + /** + * Executes the switch to Signup view use case. + */ + void getWatchListData(); } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 800704b79..3e8d00467 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -3,23 +3,29 @@ import entity.CommonStockFactory; import entity.Stock; import entity.StockFactory; +import use_case.watchlist.WatchListDataAccessInterface; + +import java.util.ArrayList; +import java.util.Random; /** * The Home View Interactor. */ public class HomeInteractor implements HomeInputBoundary { - private final HomeDataAccessInterface homeDataAccessObject; + private final HomeDataAccessInterface homeDataAccessInterface; + private final WatchListDataAccessInterface watchListDataAccessInterface; private final HomeOutputBoundary homePresenter; - public HomeInteractor(HomeDataAccessInterface homeDataAccessObject, + public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, WatchListDataAccessInterface watchListDataAccessInterface, HomeOutputBoundary homeInteractor) { - this.homeDataAccessObject = homeDataAccessObject; + this.homeDataAccessInterface = homeDataAccessInterface; + this.watchListDataAccessInterface = watchListDataAccessInterface; this.homePresenter = homeInteractor; } @Override - public void search(HomeInputData searchInputData) { + public void search(SearchInputData searchInputData) { // final String stockSymbol = searchInputData.getStockSymbol(); // final Stock stock = homeDataAccessObject.getStock(stockSymbol); @@ -27,7 +33,7 @@ public void search(HomeInputData searchInputData) { final StockFactory stockFactory = new CommonStockFactory(); final Stock stock = stockFactory.create("NVDA", 128.2, 322.1, 100002322, 500.1, 100.23); - final HomeOutputData searchOutputData = new HomeOutputData(stock, false); + final SearchOutputData searchOutputData = new SearchOutputData(stock, false); homePresenter.prepareSuccessView(searchOutputData); } @@ -51,4 +57,37 @@ public void switchToSignupView() { homePresenter.switchToSignupView(); } + @Override + public void getWatchListData() { + final ArrayList watchListData = watchListDataAccessInterface.getWatchList(); + final ArrayList watchList = new ArrayList<>(); + + final StockFactory stockFactory = new CommonStockFactory(); + final Stock aapl = stockFactory.create("AAPL", 552.2, 130.3, 100002322, 500.1, 100.23); + final Stock nvda = stockFactory.create("NVDA", 128.2, 148.2, 100002322, 500.1, 100.23); + final Stock meta = stockFactory.create("META", 11.2, 559.2, 100002322, 500.1, 100.23); + final Stock cost = stockFactory.create("COST", 123.4, 234.5, 100002322, 500.1, 100.23); + final Stock amd = stockFactory.create("AMD", 234.5, 345.6, 100002322, 500.1, 100.23); + final Stock tsm = stockFactory.create("TSM", 345.6, 456.7, 100002322, 500.1, 100.23); + + final Random rand = new Random(); + final int randNum = rand.nextInt(2); + if (randNum == 0) { + watchList.add(aapl); + watchList.add(nvda); + watchList.add(meta); + } + else { + watchList.add(cost); + watchList.add(amd); + watchList.add(tsm); + } + +// for (String symbol : watchListData) { +// final Stock stockData = homeDataAccessInterface.getStock(symbol); +// watchList.add(stockData); +// } + + homePresenter.getWatchListData(watchList); + } } diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index 12c32332f..1315ac108 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -1,5 +1,9 @@ package use_case.home_view; +import entity.Stock; + +import java.util.ArrayList; + /** * The output boundary for the Home Use Case. */ @@ -9,7 +13,7 @@ public interface HomeOutputBoundary { * Prepares the success view for the Search Use Case. * @param searchOutputData the output data */ - void prepareSuccessView(HomeOutputData searchOutputData); + void prepareSuccessView(SearchOutputData searchOutputData); /** * Prepares the failure view for the Search Use Case. @@ -37,4 +41,10 @@ public interface HomeOutputBoundary { */ void switchToSignupView(); + /** + * Preload watchlist data for Home View. + * @param watchList watchList data + */ + void getWatchListData(ArrayList watchList); + } diff --git a/src/main/java/use_case/home_view/HomeInputData.java b/src/main/java/use_case/home_view/SearchInputData.java similarity index 71% rename from src/main/java/use_case/home_view/HomeInputData.java rename to src/main/java/use_case/home_view/SearchInputData.java index 017a62fbe..072feb612 100644 --- a/src/main/java/use_case/home_view/HomeInputData.java +++ b/src/main/java/use_case/home_view/SearchInputData.java @@ -3,11 +3,11 @@ /** * The Input Data for the Search Use Case. */ -public class HomeInputData { +public class SearchInputData { private final String stockSymbol; - public HomeInputData(String stockSymbol) { + public SearchInputData(String stockSymbol) { this.stockSymbol = stockSymbol; } diff --git a/src/main/java/use_case/home_view/HomeOutputData.java b/src/main/java/use_case/home_view/SearchOutputData.java similarity index 72% rename from src/main/java/use_case/home_view/HomeOutputData.java rename to src/main/java/use_case/home_view/SearchOutputData.java index dbfdf7e5a..366448a11 100644 --- a/src/main/java/use_case/home_view/HomeOutputData.java +++ b/src/main/java/use_case/home_view/SearchOutputData.java @@ -5,12 +5,12 @@ /** * Output Data for the Search Use Case. */ -public class HomeOutputData { +public class SearchOutputData { private final Stock stock; private final boolean useCaseFailed; - public HomeOutputData(Stock stock, boolean useCaseFailed) { + public SearchOutputData(Stock stock, boolean useCaseFailed) { this.stock = stock; this.useCaseFailed = useCaseFailed; } diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 5e447fa03..f36099346 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -1,15 +1,18 @@ package view; +import entity.Stock; import interface_adapter.change_password.IsLoggedIn; import interface_adapter.home_view.HomeController; import interface_adapter.home_view.HomeState; import interface_adapter.home_view.HomeViewModel; +import interface_adapter.login.LoginState; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.ArrayList; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -18,7 +21,7 @@ /** * The View for the Home Page. */ -public class HomeView extends JPanel implements ActionListener, PropertyChangeListener { +public class HomeView extends JPanel implements PropertyChangeListener { private static final String RIGHTARROW = "->"; @@ -40,24 +43,12 @@ public class HomeView extends JPanel implements ActionListener, PropertyChangeLi // Watch list components private final JLabel watchListLabel = new JLabel("Watchlist"); private final JButton watchListButton = new JButton(RIGHTARROW); - private final JLabel firstStockLabel = new JLabel(" - AAPL"); - private final JButton firstStockButton = new JButton(RIGHTARROW); - private final JLabel secondStockLabel = new JLabel(" - NVDA"); - private final JButton secondStockButton = new JButton(RIGHTARROW); - private final JLabel thirdStockLabel = new JLabel(" - META"); - private final JButton thirdStockButton = new JButton(RIGHTARROW); + private final JPanel watchListContentPanel = new JPanel(); // Login/Logout component private JButton loginButton = new JButton(); - private final JButton signupButton = new JButton("Sign Up"); public HomeView(HomeViewModel homeViewModel) { - if (IsLoggedIn.isLoggedIn()) { - loginButton.setText("Log Out"); - } - else { - loginButton.setText("Log In"); - } this.homeViewModel = homeViewModel; this.homeViewModel.addPropertyChangeListener(this); @@ -123,20 +114,26 @@ public void actionPerformed(ActionEvent evt) { homeController.switchToWatchList(); } }); + // Watch list stock information + watchListContentPanel.setLayout(new BoxLayout(watchListContentPanel, BoxLayout.Y_AXIS)); + watchListContentPanel.setBackground(Color.WHITE); // Config login components style loginButton.setAlignmentX(Component.CENTER_ALIGNMENT); final JPanel loginPanel = new JPanel(); loginPanel.setLayout(new BoxLayout(loginPanel, BoxLayout.LINE_AXIS)); loginPanel.add(loginButton); + loginButton.setText("Log Out"); loginButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - if (IsLoggedIn.isLoggedIn()) { - IsLoggedIn.setLoggedIn(false); - } - else { - homeController.switchToLoginView(); - } +// if (IsLoggedIn.isLoggedIn()) { +// showLogoutDialog(); +// } +// else { +// homeController.switchToLoginView(); +// } + // Preload watchList data + homeController.getWatchList(); } }); @@ -147,20 +144,70 @@ public void actionPerformed(ActionEvent evt) { this.add(searchPanel); this.add(portfolioPanel); this.add(watchListPanel); + this.add(watchListContentPanel); this.add(loginPanel); } - /** - * React to a button click that results in evt. - * @param evt the ActionEvent to react to - */ - public void actionPerformed(ActionEvent evt) { - System.out.println("Click " + evt.getActionCommand()); - } - @Override public void propertyChange(PropertyChangeEvent evt) { + final HomeState state = (HomeState) evt.getNewValue(); + final ArrayList watchList = state.getWatchList(); + watchListContentPanel.removeAll(); + updateWatchListComponents(watchList); + watchListContentPanel.revalidate(); + watchListContentPanel.repaint(); + } + + public void updateWatchListComponents(ArrayList watchList) { + for (Stock stock : watchList) { + this.addWatchListItem(watchListContentPanel, stock.getSymbol(), String.valueOf(stock.getClosePrice())); + } + } + + private void addWatchListItem(JPanel contentPanel, String code, String price) { + final JPanel stockPanel = new JPanel(new BorderLayout()); + stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); + stockPanel.setBackground(Color.WHITE); + + // Left part: Stock code and price + final JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); + leftPanel.setBackground(Color.WHITE); + + final JLabel stockCodeLabel = new JLabel(code); + stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + final JLabel stockPriceLabel = new JLabel(price); + stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + + leftPanel.add(stockCodeLabel); + leftPanel.add(stockPriceLabel); + + // Right part: up and down information + final JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.setBackground(Color.WHITE); + rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); + + final JButton viewStockButton = new JButton(RIGHTARROW); + viewStockButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(viewStockButton)) { + final HomeState currentState = homeViewModel.getState(); + currentState.setSymbol(code); + + homeController.search(currentState.getSymbol()); + } + } + }); + rightPanel.add(viewStockButton); + + // Add all to stockPanel + stockPanel.add(leftPanel, BorderLayout.WEST); + stockPanel.add(rightPanel, BorderLayout.EAST); + + // Add all information + contentPanel.add(stockPanel); } public String getViewName() { @@ -169,5 +216,26 @@ public String getViewName() { public void setHomeController(HomeController homeController) { this.homeController = homeController; + + // Preload watchList data + homeController.getWatchList(); + } + + public void changeLoginButtonText() { + if (IsLoggedIn.isLoggedIn()) { + loginButton.setText("Log Out"); + } + else { + loginButton.setText("Log In"); + } + } + + public void showLogoutDialog() { + final int response = JOptionPane.showConfirmDialog(this, "Do you want to logout?", "Confirm", JOptionPane.YES_NO_OPTION); + + if (response == JOptionPane.YES_OPTION) { + IsLoggedIn.setLoggedIn(false); + changeLoginButtonText(); + } } } diff --git a/src/test/java/use_case/home_view/HomeViewInteratorTest.java b/src/test/java/use_case/home_view/HomeViewInteratorTest.java index 5ce5b9e27..2619aba5b 100644 --- a/src/test/java/use_case/home_view/HomeViewInteratorTest.java +++ b/src/test/java/use_case/home_view/HomeViewInteratorTest.java @@ -1,10 +1,8 @@ package use_case.home_view; -import data_access.DBUserDataAccessObject; -import data_access.InMemoryUserDataAccessObject; +import data_access.FileUserDataAccessObject; import entity.*; import org.junit.Test; -import use_case.login.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -37,15 +35,15 @@ public void successTest() { // LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); // interactor.execute(inputData); - final HomeInputData inputData = new HomeInputData("NVDA"); - final UserFactory userFactory = new CommonUserFactory(); + final SearchInputData inputData = new SearchInputData("NVDA"); final StockFactory stockFactory = new CommonStockFactory(); - final HomeDataAccessInterface homeData = new DBUserDataAccessObject(userFactory, stockFactory); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject homeData = new FileUserDataAccessObject(stockFactory, simulatedHoldingFactory); // This creates a successPresenter that tests whether the test case is as we expect. HomeOutputBoundary successPresenter = new HomeOutputBoundary() { @Override - public void prepareSuccessView(HomeOutputData searchOutputData) { + public void prepareSuccessView(SearchOutputData searchOutputData) { assertEquals("NVDA", searchOutputData.getStock().getSymbol()); } From e349fc86700c5fdded55c785d1c9aa9097a22b17 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Tue, 19 Nov 2024 11:56:44 -0500 Subject: [PATCH 046/113] Improve home page UI, add error message label and related functions --- .../home_view/HomePresenter.java | 17 +++- .../home_view/HomeState.java | 17 +++- .../use_case/home_view/HomeInteractor.java | 12 ++- src/main/java/view/HomeView.java | 94 ++++++++++++++----- 4 files changed, 108 insertions(+), 32 deletions(-) diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index 9eeafbae1..b23ddf1b6 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -41,19 +41,22 @@ public HomePresenter(HomeViewModel homeViewModel, @Override public void prepareSuccessView(SearchOutputData searchOutputData) { + // Show stock view final StockViewState stockViewState = new StockViewState(); stockViewState.setStock(searchOutputData.getStock()); this.stockViewModel.setState(stockViewState); this.stockViewModel.firePropertyChanged(); + // Clean home view error message + this.sendErrorMessage(""); + viewManagerModel.setState("StockView"); viewManagerModel.firePropertyChanged(); } @Override public void prepareFailView(String errorMessage) { - // Show error dialog - System.out.println("Show dialog view after failed to search stock"); + this.sendErrorMessage(errorMessage); } @Override @@ -85,6 +88,14 @@ public void getWatchListData(ArrayList watchList) { final HomeState homeState = new HomeState(); homeState.setWatchList(watchList); this.homeViewModel.setState(homeState); - this.homeViewModel.firePropertyChanged(); + this.homeViewModel.firePropertyChanged("getWatchList"); + } + + // Helper function + private void sendErrorMessage(String errorMessage) { + final HomeState homeState = this.homeViewModel.getState(); + homeState.setErrorMessage(errorMessage); + this.homeViewModel.setState(homeState); + this.homeViewModel.firePropertyChanged("error"); } } diff --git a/src/main/java/interface_adapter/home_view/HomeState.java b/src/main/java/interface_adapter/home_view/HomeState.java index 43eb5bc77..cb3471185 100644 --- a/src/main/java/interface_adapter/home_view/HomeState.java +++ b/src/main/java/interface_adapter/home_view/HomeState.java @@ -10,20 +10,29 @@ public class HomeState { private String symbol; private ArrayList watchList; + private String errorMessage; public String getSymbol() { return symbol; } - public void setSymbol(String symbol) { - this.symbol = symbol; - } - public ArrayList getWatchList() { return watchList; } + public String getErrorMessage() { + return errorMessage; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + public void setWatchList(ArrayList watchList) { this.watchList = watchList; } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 3e8d00467..6128c4284 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -27,9 +27,15 @@ public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, WatchList @Override public void search(SearchInputData searchInputData) { // final String stockSymbol = searchInputData.getStockSymbol(); -// final Stock stock = homeDataAccessObject.getStock(stockSymbol); - - // Fake data to save usage limit +// try { +// final Stock stock = homeDataAccessInterface.getStock(stockSymbol); +// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); +// homePresenter.prepareSuccessView(searchOutputData); +// } +// catch (Exception err) { +// err.printStackTrace(); +// homePresenter.prepareFailView("Failed to fetch stock data"); +// } final StockFactory stockFactory = new CommonStockFactory(); final Stock stock = stockFactory.create("NVDA", 128.2, 322.1, 100002322, 500.1, 100.23); diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index f36099346..b5f494099 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -24,16 +24,20 @@ public class HomeView extends JPanel implements PropertyChangeListener { private static final String RIGHTARROW = "->"; + private static final String FONTFAMILY = "SansSerif"; private final String viewName = "home view"; private final HomeViewModel homeViewModel; private HomeController homeController; + // Welcome component + private final JLabel welcomeLabel; + // Search components private final JTextField searchTextField = new JTextField(20); + private final JLabel searchErrorMessageLabel = new JLabel(); // Stock view components - private final JLabel stockLabel = new JLabel("Stock View"); private final JButton searchButton = new JButton(RIGHTARROW); // Portfolio components @@ -49,14 +53,33 @@ public class HomeView extends JPanel implements PropertyChangeListener { private JButton loginButton = new JButton(); public HomeView(HomeViewModel homeViewModel) { + // Set up homeViewModel and listen any upcoming events this.homeViewModel = homeViewModel; this.homeViewModel.addPropertyChangeListener(this); + // Config welcome label style + final JPanel welcomePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + welcomePanel.setBackground(Color.WHITE); + welcomeLabel = new JLabel("Welcome"); + updateLabelStyle(welcomeLabel, 24); + welcomePanel.add(welcomeLabel); + // Config search components style - searchTextField.setText("NVDA"); final JPanel searchPanel = new JPanel(); - searchPanel.add(searchTextField); - searchPanel.add(searchButton); + final JPanel searchTextFieldPanel = new JPanel(); + final JPanel searchErrorMessagePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + searchTextField.setText("NVDA"); + searchErrorMessageLabel.setForeground(Color.RED); + searchErrorMessageLabel.setAlignmentX(Component.LEFT_ALIGNMENT); + searchTextFieldPanel.setBackground(Color.WHITE); + searchTextFieldPanel.add(searchTextField); + searchTextFieldPanel.add(searchButton); + searchErrorMessagePanel.setBackground(Color.WHITE); + searchErrorMessagePanel.add(searchErrorMessageLabel); + searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.Y_AXIS)); + searchPanel.setBackground(Color.WHITE); + searchPanel.add(searchTextFieldPanel); + searchPanel.add(searchErrorMessagePanel); // Add search stock button listener searchButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { @@ -95,9 +118,17 @@ public void changedUpdate(DocumentEvent e) { // Config portfolio components style final JPanel portfolioPanel = new JPanel(); - portfolioPanel.setLayout(new BoxLayout(portfolioPanel, BoxLayout.LINE_AXIS)); - portfolioPanel.add(portfolioLabel); - portfolioPanel.add(portfolioButton); + final JPanel portfolioLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + final JPanel portfolioRightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + updateLabelStyle(portfolioLabel, 20); + portfolioLeftPanel.setBackground(Color.WHITE); + portfolioLeftPanel.add(portfolioLabel); + portfolioRightPanel.setBackground(Color.WHITE); + portfolioRightPanel.add(portfolioButton); + portfolioPanel.setBackground(Color.WHITE); + portfolioPanel.setLayout(new BoxLayout(portfolioPanel, BoxLayout.X_AXIS)); + portfolioPanel.add(portfolioLeftPanel); + portfolioPanel.add(portfolioRightPanel); portfolioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { homeController.switchToPortfolio(); @@ -106,9 +137,18 @@ public void actionPerformed(ActionEvent evt) { // Config watch list components style final JPanel watchListPanel = new JPanel(); + final JPanel watchListLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + final JPanel watchListRightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + updateLabelStyle(watchListLabel, 20); + watchListPanel.setBackground(Color.WHITE); + watchListLeftPanel.setBackground(Color.WHITE); + watchListRightPanel.setBackground(Color.WHITE); watchListPanel.setLayout(new BoxLayout(watchListPanel, BoxLayout.LINE_AXIS)); - watchListPanel.add(watchListLabel); - watchListPanel.add(watchListButton); + watchListLeftPanel.add(watchListLabel); + watchListRightPanel.add(watchListButton); + watchListPanel.add(watchListLeftPanel); + watchListPanel.add(watchListRightPanel); + watchListPanel.setBorder(BorderFactory.createEmptyBorder(16, 0, 0, 0)); watchListButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { homeController.switchToWatchList(); @@ -121,7 +161,7 @@ public void actionPerformed(ActionEvent evt) { // Config login components style loginButton.setAlignmentX(Component.CENTER_ALIGNMENT); final JPanel loginPanel = new JPanel(); - loginPanel.setLayout(new BoxLayout(loginPanel, BoxLayout.LINE_AXIS)); + loginButton.setPreferredSize(new Dimension(150, 32)); loginPanel.add(loginButton); loginButton.setText("Log Out"); loginButton.addActionListener(new ActionListener() { @@ -132,8 +172,7 @@ public void actionPerformed(ActionEvent evt) { // else { // homeController.switchToLoginView(); // } - // Preload watchList data - homeController.getWatchList(); + homeController.switchToLoginView(); } }); @@ -141,6 +180,7 @@ public void actionPerformed(ActionEvent evt) { this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); // Add all components + this.add(welcomePanel); this.add(searchPanel); this.add(portfolioPanel); this.add(watchListPanel); @@ -151,11 +191,21 @@ public void actionPerformed(ActionEvent evt) { @Override public void propertyChange(PropertyChangeEvent evt) { final HomeState state = (HomeState) evt.getNewValue(); - final ArrayList watchList = state.getWatchList(); - watchListContentPanel.removeAll(); - updateWatchListComponents(watchList); - watchListContentPanel.revalidate(); - watchListContentPanel.repaint(); + if (evt.getPropertyName().equals("getWatchList")) { + final ArrayList watchList = state.getWatchList(); + watchListContentPanel.removeAll(); + updateWatchListComponents(watchList); + watchListContentPanel.revalidate(); + watchListContentPanel.repaint(); + } + else if (evt.getPropertyName().equals("error")) { + searchErrorMessageLabel.setText(state.getErrorMessage()); + } + + } + + public void updateLabelStyle(JLabel label, int fontSize) { + label.setFont(new Font(FONTFAMILY, Font.BOLD, fontSize)); } public void updateWatchListComponents(ArrayList watchList) { @@ -166,18 +216,18 @@ public void updateWatchListComponents(ArrayList watchList) { private void addWatchListItem(JPanel contentPanel, String code, String price) { final JPanel stockPanel = new JPanel(new BorderLayout()); - stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); stockPanel.setBackground(Color.WHITE); // Left part: Stock code and price final JPanel leftPanel = new JPanel(); + leftPanel.setBorder(BorderFactory.createEmptyBorder(16, 64, 0, 0)); leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); leftPanel.setBackground(Color.WHITE); - final JLabel stockCodeLabel = new JLabel(code); - stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + final JLabel stockCodeLabel = new JLabel("- " + code); + stockCodeLabel.setFont(new Font(FONTFAMILY, Font.BOLD, 16)); final JLabel stockPriceLabel = new JLabel(price); - stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + stockPriceLabel.setFont(new Font(FONTFAMILY, Font.PLAIN, 14)); leftPanel.add(stockCodeLabel); leftPanel.add(stockPriceLabel); @@ -186,7 +236,7 @@ private void addWatchListItem(JPanel contentPanel, String code, String price) { final JPanel rightPanel = new JPanel(); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); rightPanel.setBackground(Color.WHITE); - rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); + rightPanel.setBorder(BorderFactory.createEmptyBorder(24, 0, 0, 5)); final JButton viewStockButton = new JButton(RIGHTARROW); viewStockButton.addActionListener(new ActionListener() { From 38ee5b36016cdf9ffc706b59af054c934c1a1459 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Nov 2024 14:18:31 -0500 Subject: [PATCH 047/113] pass data from home view to watchlist view --- src/main/java/app/AppBuilder.java | 11 ++- .../data_access/FileUserDataAccessObject.java | 94 ++++++++++++------- src/main/java/data_access/watchlist.txt | 2 +- .../home_view/HomePresenter.java | 16 +++- .../watchlist_view/WatchListViewModel.java | 30 ++---- .../watchlist_view/WatchListViewState.java | 8 ++ .../use_case/home_view/HomeInteractor.java | 28 ++---- .../home_view/HomeOutputBoundary.java | 3 +- src/main/java/view/StockView.java | 9 +- src/main/java/view/WatchListView.java | 26 ++--- .../home_view/HomeViewInteratorTest.java | 40 +++----- 11 files changed, 143 insertions(+), 124 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 22549fd5d..6fcc86341 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -68,7 +68,7 @@ public class AppBuilder { // thought question: is the hard dependency below a problem? private final DBUserDataAccessObject dbUserDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); - private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(stockFactory, simulatedHoldingFactory); + private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(simulatedHoldingFactory); private HomeView homeView; private HomeViewModel homeViewModel; @@ -141,7 +141,7 @@ public AppBuilder addLoggedInView() { */ public AppBuilder addWatchListView() { watchListViewModel = new WatchListViewModel(); - watchListView = new WatchListView(watchListViewModel, viewManagerModel,dbUserDataAccessObject); + watchListView = new WatchListView(watchListViewModel, viewManagerModel, dbUserDataAccessObject); cardPanel.add(watchListView, watchListView.getViewName()); return this; } @@ -174,7 +174,12 @@ public AppBuilder addStockView() { */ public AppBuilder addHomeUseCase() { final HomeOutputBoundary homeOutputBoundary = new HomePresenter(homeViewModel, - loginViewModel, signupViewModel, viewManagerModel, portfolioViewModel, stockViewModel); + loginViewModel, + signupViewModel, + viewManagerModel, + portfolioViewModel, + stockViewModel, + watchListViewModel); final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, fileUserDataAccessObject, homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 59e2e7643..70d11ee93 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -27,29 +27,43 @@ public class FileUserDataAccessObject implements WatchListDataAccessInterface, W private final String watchListFilePath = "watchlist.txt"; private final String portfolioFilePath = "portfolio.txt"; - private final StockFactory stockFactory; private final SimulatedHoldingFactory simulatedHoldingFactory; - public FileUserDataAccessObject(StockFactory stockFactory, SimulatedHoldingFactory simulatedHoldingFactory) { - this.stockFactory = stockFactory; + public FileUserDataAccessObject(SimulatedHoldingFactory simulatedHoldingFactory) { this.simulatedHoldingFactory = simulatedHoldingFactory; } // Watch list related APIs @Override public ArrayList getWatchList() { - // Using FileReader and BufferedReader to read the file - try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + watchListFilePath))) { - final String line = reader.readLine(); - if (line != null) { - Collections.addAll(watchList, line.split(",")); + // Check if watchlist.txt file exists + final Boolean isFileExisted = new File(mainFilePath + watchListFilePath).isFile(); + + if (isFileExisted) { + // Using FileReader and BufferedReader to read the file + try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + watchListFilePath))) { + watchList.clear(); + final String line = reader.readLine(); + if (line != null) { + Collections.addAll(watchList, line.split(",")); + } + + return watchList; + } + catch (IOException ex) { + // Normally, this means the file doesn't exist + throw new RuntimeException(ex); } - - return watchList; } - catch (IOException ex) { - // Normally, this means the file doesn't exist - throw new RuntimeException(ex); + else { + // If the watchlist file doesn't exist, we create a new one + this.watchList.add("AAPl"); + this.watchList.add("NVDA"); + this.watchList.add("AMD"); + this.saveWatchList(); + + // Get watchlist data again + return this.getWatchList(); } } @@ -57,9 +71,9 @@ public ArrayList getWatchList() { public void saveWatchList() { final BufferedWriter writer; try { - writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath)); + // Override original file + writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath, false)); writer.write(String.join(",", watchList)); - writer.newLine(); writer.close(); } @@ -71,6 +85,7 @@ public void saveWatchList() { @Override public void addToWatchList(String symbol) { watchList.add(symbol); + watchList.sort(String::compareTo); saveWatchList(); } @@ -82,31 +97,46 @@ public void removeFromWatchList(String symbol) { // Portfolio list related APIs public ArrayList getPortfolioList() { - // Using FileReader and BufferedReader to read the file - try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + portfolioFilePath))) { - String line; - while ((line = reader.readLine()) != null) { - final String[] data = line.split(","); - final String symbol = data[0]; - final double price = Double.parseDouble(data[1]); - final int amount = Integer.parseInt(data[2]); - final SimulatedHolding simulatedHolding = simulatedHoldingFactory.create(symbol, price, amount); - - portfolioList.add(simulatedHolding); + // Check if watchlist.txt file exists + final Boolean isFileExisted = new File(mainFilePath + watchListFilePath).isFile(); + + if (isFileExisted) { + // Using FileReader and BufferedReader to read the file + try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + portfolioFilePath))) { + // Clear all data first + portfolioList.clear(); + + String line; + while ((line = reader.readLine()) != null) { + final String[] data = line.split(","); + final String symbol = data[0]; + final double price = Double.parseDouble(data[1]); + final int amount = Integer.parseInt(data[2]); + final SimulatedHolding simulatedHolding = simulatedHoldingFactory.create(symbol, price, amount); + + portfolioList.add(simulatedHolding); + } + + return portfolioList; + } + catch (IOException ex) { + // Normally, this means the file doesn't exist + throw new RuntimeException(ex); } - - return portfolioList; } - catch (IOException ex) { - // Normally, this means the file doesn't exist - throw new RuntimeException(ex); + else { + // If file doesn't exist, we create one first + savePortfolioList(); + + return this.getPortfolioList(); } } public void savePortfolioList() { final BufferedWriter writer; try { - writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath)); + // Override original file + writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath, false)); for (SimulatedHolding simulatedHolding : portfolioList) { writer.write(simulatedHolding.getSymbol() + ","); writer.write(simulatedHolding.getPurchasePrice() + ","); diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index eda60370a..73d1a68fd 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -NVDA,AAPL,META +AAPl,NVDA,AMD \ No newline at end of file diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index b23ddf1b6..eeb65b148 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -7,8 +7,11 @@ import interface_adapter.signup.SignupViewModel; import interface_adapter.stock_view.StockViewModel; import interface_adapter.stock_view.StockViewState; +import interface_adapter.stock_view.WatchListViewState; +import interface_adapter.watchlist_view.WatchListViewModel; import use_case.home_view.HomeOutputBoundary; import use_case.home_view.SearchOutputData; +import view.WatchListView; import java.util.ArrayList; @@ -23,13 +26,15 @@ public class HomePresenter implements HomeOutputBoundary { private final ViewManagerModel viewManagerModel; private final PortfolioViewModel portfolioViewModel; private final StockViewModel stockViewModel; + private final WatchListViewModel watchListViewModel; public HomePresenter(HomeViewModel homeViewModel, LoginViewModel loginViewModel, SignupViewModel signupViewModel, ViewManagerModel viewManagerModel, PortfolioViewModel portfolioViewModel, - StockViewModel stockViewModel) { + StockViewModel stockViewModel, + WatchListViewModel watchListViewModel) { this.homeViewModel = homeViewModel; this.loginViewModel = loginViewModel; @@ -37,6 +42,7 @@ public HomePresenter(HomeViewModel homeViewModel, this.viewManagerModel = viewManagerModel; this.portfolioViewModel = portfolioViewModel; this.stockViewModel = stockViewModel; + this.watchListViewModel = watchListViewModel; } @Override @@ -66,7 +72,13 @@ public void switchToPortfolio() { } @Override - public void switchToWatchList() { + public void switchToWatchList(ArrayList watchListSymbols) { + // Pass watchList symbols to watchList view + final WatchListViewState watchListViewState = new WatchListViewState(); + watchListViewState.setWatchlist(watchListSymbols); + watchListViewModel.setState(watchListViewState); + watchListViewModel.firePropertyChanged("watchList"); + viewManagerModel.setState("WatchListView"); viewManagerModel.firePropertyChanged(); } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java index adaf8a2e7..95c06c9f8 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java @@ -1,32 +1,18 @@ package interface_adapter.watchlist_view; +import interface_adapter.ViewModel; +import interface_adapter.stock_view.StockViewState; +import interface_adapter.stock_view.WatchListViewState; + import java.beans.PropertyChangeSupport; import java.util.ArrayList; /** * The View Model for the WatchList View. */ -public class WatchListViewModel { - private final ArrayList watchList = new ArrayList<>(); - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - - public ArrayList getWatchList() { - return new ArrayList<>(watchList); - } - - // add to watchlist - public void addWatchList(String symbol) { - watchList.add(symbol); - pcs.firePropertyChange("watchList", null, new ArrayList<>(watchList)); - } - - // remove from watchlist - public void removeWatchList(String symbol) { - watchList.remove(symbol); - pcs.firePropertyChange("watchList", null, new ArrayList<>(watchList)); - } - // return State of Stock, return ture if stock is already in the watchlist, false else - public boolean checkStockState(String symbol) { - return watchList.contains(symbol); +public class WatchListViewModel extends ViewModel { + public WatchListViewModel() { + super("watch list view"); + setState(new WatchListViewState()); } } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java index bc49f62f3..f87d3e6c9 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java @@ -15,7 +15,15 @@ public Stock getStock() { return stock; } + public ArrayList getWatchlist() { + return watchlist; + } + public void setStock(Stock newStock) { this.stock = newStock; } + + public void setWatchlist(ArrayList newWatchlist) { + this.watchlist = newWatchlist; + } } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 6128c4284..aadf7ffe4 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -17,7 +17,8 @@ public class HomeInteractor implements HomeInputBoundary { private final WatchListDataAccessInterface watchListDataAccessInterface; private final HomeOutputBoundary homePresenter; - public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, WatchListDataAccessInterface watchListDataAccessInterface, + public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, + WatchListDataAccessInterface watchListDataAccessInterface, HomeOutputBoundary homeInteractor) { this.homeDataAccessInterface = homeDataAccessInterface; this.watchListDataAccessInterface = watchListDataAccessInterface; @@ -37,7 +38,7 @@ public void search(SearchInputData searchInputData) { // homePresenter.prepareFailView("Failed to fetch stock data"); // } final StockFactory stockFactory = new CommonStockFactory(); - final Stock stock = stockFactory.create("NVDA", 128.2, 322.1, 100002322, 500.1, 100.23); + final Stock stock = stockFactory.create(searchInputData.getStockSymbol(), 128.2, 322.1, 100002322, 500.1, 100.23); final SearchOutputData searchOutputData = new SearchOutputData(stock, false); homePresenter.prepareSuccessView(searchOutputData); @@ -50,7 +51,8 @@ public void switchToPortfolio() { @Override public void switchToWatchList() { - homePresenter.switchToWatchList(); + final ArrayList watchListData = watchListDataAccessInterface.getWatchList(); + homePresenter.switchToWatchList(watchListData); } @Override @@ -72,22 +74,10 @@ public void getWatchListData() { final Stock aapl = stockFactory.create("AAPL", 552.2, 130.3, 100002322, 500.1, 100.23); final Stock nvda = stockFactory.create("NVDA", 128.2, 148.2, 100002322, 500.1, 100.23); final Stock meta = stockFactory.create("META", 11.2, 559.2, 100002322, 500.1, 100.23); - final Stock cost = stockFactory.create("COST", 123.4, 234.5, 100002322, 500.1, 100.23); - final Stock amd = stockFactory.create("AMD", 234.5, 345.6, 100002322, 500.1, 100.23); - final Stock tsm = stockFactory.create("TSM", 345.6, 456.7, 100002322, 500.1, 100.23); - - final Random rand = new Random(); - final int randNum = rand.nextInt(2); - if (randNum == 0) { - watchList.add(aapl); - watchList.add(nvda); - watchList.add(meta); - } - else { - watchList.add(cost); - watchList.add(amd); - watchList.add(tsm); - } + + watchList.add(aapl); + watchList.add(nvda); + watchList.add(meta); // for (String symbol : watchListData) { // final Stock stockData = homeDataAccessInterface.getStock(symbol); diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index 1315ac108..3f2623e5e 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -28,8 +28,9 @@ public interface HomeOutputBoundary { /** * Switches to the watch list View. + * @param watchListSymbols the symbols of whole watch list */ - void switchToWatchList(); + void switchToWatchList(ArrayList watchListSymbols); /** * Switches to the Login View. diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index e2cd8eddf..86620b80c 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -133,12 +133,9 @@ void backButtonAction() { @Override public void propertyChange(PropertyChangeEvent evt) { - // React to StockViewModel's state changes - if ("state".equals(evt.getPropertyName())) { - Stock updatedStock = stockViewModel.getState().getStock(); - if (updatedStock != null) { - updateStockData(updatedStock); - } + Stock updatedStock = stockViewModel.getState().getStock(); + if (updatedStock != null) { + updateStockData(updatedStock); } } } \ No newline at end of file diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index c9c041a5d..75d245ea1 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -5,6 +5,7 @@ import interface_adapter.ViewManagerModel; import interface_adapter.change_password.LoggedInState; import interface_adapter.login.LoginState; +import interface_adapter.stock_view.WatchListViewState; import interface_adapter.watchlist_view.WatchListViewModel; import javax.swing.*; @@ -19,6 +20,7 @@ public class WatchListView extends JPanel implements PropertyChangeListener { private final ViewManagerModel viewManagerModel; + private final WatchListViewModel watchListViewModel; private final DBUserDataAccessObject dbUserDataAccessObject; private final JPanel contentPanel = new JPanel(); @@ -27,6 +29,8 @@ public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerM this.dbUserDataAccessObject = dbUserDataAccessObject; setLayout(new BorderLayout()); setBackground(Color.WHITE); + this.watchListViewModel = viewModel; + this.watchListViewModel.addPropertyChangeListener(this); // return button final JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); @@ -55,9 +59,9 @@ public void actionPerformed(ActionEvent e) { contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); - addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09", "+24.10%"); - addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); - addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); + addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09"); + addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00"); + addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25"); add(contentPanel, BorderLayout.CENTER); @@ -106,8 +110,8 @@ private void addStockItem(JPanel contentPanel, String code, String price, String @Override public void propertyChange(PropertyChangeEvent evt) { if ("watchList".equals(evt.getPropertyName())) { - final ArrayList updatedWatchList = (ArrayList) evt.getNewValue(); - updateWatchList(updatedWatchList); + final WatchListViewState watchListViewState = (WatchListViewState) evt.getNewValue(); + updateWatchList(watchListViewState.getWatchlist()); } } @@ -120,13 +124,13 @@ private void updateWatchList(ArrayList updatedWatchList) { private void createWatchListview(ArrayList WatchList) { for (String stockcode: WatchList) { - final Stock stock = dbUserDataAccessObject.getStock(stockcode); - final String price = Double.toString(stock.getClosePrice()); - final String dailyChange = Double.toString(stock.getDailyChange()); - final String dailyPercentage = Double.toString((stock.getDailyChange() / stock.getOpenPrice()) * 100) + "%"; - final String volume = Double.toString(stock.getVolume()); +// final Stock stock = dbUserDataAccessObject.getStock(stockcode); +// final String price = Double.toString(stock.getClosePrice()); +// final String dailyChange = Double.toString(stock.getDailyChange()); +// final String dailyPercentage = Double.toString((stock.getDailyChange() / stock.getOpenPrice()) * 100) + "%"; +// final String volume = Double.toString(stock.getVolume()); - addStockItem(contentPanel, stock.getSymbol(), "$" + price, dailyChange, dailyPercentage, volume); + addStockItem(contentPanel, stockcode, "$" + "123", "234", "0.45%", "1232323.23"); } } diff --git a/src/test/java/use_case/home_view/HomeViewInteratorTest.java b/src/test/java/use_case/home_view/HomeViewInteratorTest.java index 2619aba5b..685de67c1 100644 --- a/src/test/java/use_case/home_view/HomeViewInteratorTest.java +++ b/src/test/java/use_case/home_view/HomeViewInteratorTest.java @@ -1,9 +1,12 @@ package use_case.home_view; +import data_access.DBUserDataAccessObject; import data_access.FileUserDataAccessObject; import entity.*; import org.junit.Test; +import java.util.ArrayList; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -11,34 +14,12 @@ public class HomeViewInteratorTest { @Test public void successTest() { -// LoginInputData inputData = new LoginInputData("Paul", "password"); -// LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); -// -// // For the success test, we need to add Paul to the data access repository before we log in. -// UserFactory factory = new CommonUserFactory(); -// User user = factory.create("Paul", "password"); -// userRepository.save(user); -// -// // This creates a successPresenter that tests whether the test case is as we expect. -// LoginOutputBoundary successPresenter = new LoginOutputBoundary() { -// @Override -// public void prepareSuccessView(LoginOutputData user) { -// assertEquals("Paul", user.getUsername()); -// } -// -// @Override -// public void prepareFailView(String error) { -// fail("Use case failure is unexpected."); -// } -// }; -// -// LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); -// interactor.execute(inputData); - final SearchInputData inputData = new SearchInputData("NVDA"); + final UserFactory userFactory = new CommonUserFactory(); final StockFactory stockFactory = new CommonStockFactory(); final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); - final FileUserDataAccessObject homeData = new FileUserDataAccessObject(stockFactory, simulatedHoldingFactory); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); // This creates a successPresenter that tests whether the test case is as we expect. HomeOutputBoundary successPresenter = new HomeOutputBoundary() { @@ -58,7 +39,7 @@ public void switchToPortfolio() { } @Override - public void switchToWatchList() { + public void switchToWatchList(ArrayList watchList) { // Do nothing right now } @@ -71,9 +52,14 @@ public void switchToLoginView() { public void switchToSignupView() { // Do nothing right now } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } }; - final HomeInputBoundary interactor = new HomeInteractor(homeData, successPresenter); + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, successPresenter); interactor.search(inputData); } } From c93e5926f2742e8dad55983cc76fa2a5da6662bc Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Tue, 19 Nov 2024 14:27:34 -0500 Subject: [PATCH 048/113] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 554351c86..54ec781e1 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed ## Week 2 Progress `Home Page` -image +image `Stock View` image From 76d641c2ef0c93622a419a12405a4186e19a38c2 Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:31:53 -0500 Subject: [PATCH 049/113] Update WatchListView.java --- src/main/java/view/WatchListView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 75d245ea1..625e9a7d8 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -130,6 +130,7 @@ private void createWatchListview(ArrayList WatchList) { // final String dailyPercentage = Double.toString((stock.getDailyChange() / stock.getOpenPrice()) * 100) + "%"; // final String volume = Double.toString(stock.getVolume()); +// addStockItem(contentPanel, stock.getSymbol(), "$" + price, dailyChange, dailyPercentage, volume); addStockItem(contentPanel, stockcode, "$" + "123", "234", "0.45%", "1232323.23"); } } From 5e85e19286444d18bf6a97b4481cb075c96ec3f5 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Wed, 20 Nov 2024 22:42:38 -0500 Subject: [PATCH 050/113] Buy user case. --- src/main/java/app/AppBuilder.java | 34 ++++ .../buy_view/BuyController.java | 38 +++++ .../buy_view/BuyPresenter.java | 39 +++++ .../interface_adapter/buy_view/BuyState.java | 23 +++ .../buy_view/BuyViewModel.java | 20 +++ .../java/use_case/buy/BuyInputBoundary.java | 18 ++ src/main/java/use_case/buy/BuyInputData.java | 24 +++ src/main/java/use_case/buy/BuyInteractor.java | 35 ++++ .../java/use_case/buy/BuyOutputBoundary.java | 18 ++ src/main/java/use_case/buy/BuyOutputData.java | 11 ++ .../buy/BuyUserDataAccessInterface.java | 7 + src/main/java/view/BuyView.java | 160 ++++++++++++++++++ src/main/java/view/StockView.java | 16 +- 13 files changed, 441 insertions(+), 2 deletions(-) create mode 100644 src/main/java/interface_adapter/buy_view/BuyController.java create mode 100644 src/main/java/interface_adapter/buy_view/BuyPresenter.java create mode 100644 src/main/java/interface_adapter/buy_view/BuyState.java create mode 100644 src/main/java/interface_adapter/buy_view/BuyViewModel.java create mode 100644 src/main/java/use_case/buy/BuyInputBoundary.java create mode 100644 src/main/java/use_case/buy/BuyInputData.java create mode 100644 src/main/java/use_case/buy/BuyInteractor.java create mode 100644 src/main/java/use_case/buy/BuyOutputBoundary.java create mode 100644 src/main/java/use_case/buy/BuyOutputData.java create mode 100644 src/main/java/use_case/buy/BuyUserDataAccessInterface.java create mode 100644 src/main/java/view/BuyView.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 6fcc86341..f13b11bb6 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -12,6 +12,9 @@ import data_access.InMemoryUserDataAccessObject; import entity.*; import interface_adapter.ViewManagerModel; +import interface_adapter.buy_view.BuyController; +import interface_adapter.buy_view.BuyPresenter; +import interface_adapter.buy_view.BuyViewModel; import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; import interface_adapter.change_password.LoggedInViewModel; @@ -30,6 +33,9 @@ import interface_adapter.stock_view.StockViewModel; import interface_adapter.watchlist_view.WatchListViewModel; import interface_adapter.portfolio.PortfolioViewModel; +import use_case.buy.BuyInputBoundary; +import use_case.buy.BuyInteractor; +import use_case.buy.BuyOutputBoundary; import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; @@ -69,6 +75,7 @@ public class AppBuilder { // thought question: is the hard dependency below a problem? private final DBUserDataAccessObject dbUserDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(simulatedHoldingFactory); + //private final Buy private HomeView homeView; private HomeViewModel homeViewModel; @@ -84,6 +91,8 @@ public class AppBuilder { private PortfolioViewModel portfolioViewModel; private StockView stockView; private StockViewModel stockViewModel; + private BuyView buyView; + private BuyViewModel buyViewModel; private ArrayList watchList = fileUserDataAccessObject.getWatchList(); public AppBuilder() { @@ -168,6 +177,17 @@ public AppBuilder addStockView() { return this; } + /** + * Adds the Buy View to the application. + * @return this builder + */ + public AppBuilder addBuyView() { + buyViewModel = new BuyViewModel(); + buyView = new BuyView(buyViewModel, viewManagerModel); + cardPanel.add(buyView, buyView.getViewName()); + return this; + } + /** * Adds the Home Use Case to the application. * @return this builder @@ -250,6 +270,20 @@ public AppBuilder addLogoutUseCase() { return this; } + /** + * Adds the Buy Use Case to the application. + * @return this builder + */ + //public AppBuilder addBuyUseCase() { + // final BuyOutputBoundary buyOutputBoundary = new BuyPresenter(buyViewModel, viewManagerModel, homeViewModel); + // final BuyInputBoundary buyInteractor = new BuyInteractor( + // buyUserDataAccessObject, buyOutputBoundary); + + // final BuyController buyController = new BuyController(buyInteractor); + // buyView.setBuyController(buyController); + // return this; + //} + /** * Creates the JFrame for the application and initially sets the SignupView to be displayed. * @return the application diff --git a/src/main/java/interface_adapter/buy_view/BuyController.java b/src/main/java/interface_adapter/buy_view/BuyController.java new file mode 100644 index 000000000..93b4c8c20 --- /dev/null +++ b/src/main/java/interface_adapter/buy_view/BuyController.java @@ -0,0 +1,38 @@ +package interface_adapter.buy_view; + +import use_case.buy.BuyInputBoundary; +import use_case.buy.BuyInputData; +import use_case.login.LoginInputBoundary; +import use_case.login.LoginInputData; + +/** + * The controller for the Buy Use Case. + */ +public class BuyController { + + private final BuyInputBoundary buyUseCaseInteractor; + + public BuyController(BuyInputBoundary buyUseCaseInteractor) { + this.buyUseCaseInteractor = buyUseCaseInteractor; + } + + /** + * Executes the Buy Use Case. + * @param price the price of the stock + * @param quantity the quantity of the stock + */ + + public void execute(String price, String quantity) { + final BuyInputData buyInputData = new BuyInputData( + price, quantity); + + buyUseCaseInteractor.execute(buyInputData); + } + + /** + * Executes the "switch to HomeView" Use Case. + */ + public void switchToHomeView() { + buyUseCaseInteractor.switchToHomeView(); + } +} diff --git a/src/main/java/interface_adapter/buy_view/BuyPresenter.java b/src/main/java/interface_adapter/buy_view/BuyPresenter.java new file mode 100644 index 000000000..f9aa4e667 --- /dev/null +++ b/src/main/java/interface_adapter/buy_view/BuyPresenter.java @@ -0,0 +1,39 @@ +package interface_adapter.buy_view; + +import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.IsLoggedIn; +import interface_adapter.change_password.LoggedInState; +import interface_adapter.home_view.HomeViewModel; +import use_case.buy.BuyOutputBoundary; +import use_case.buy.BuyOutputData; +import use_case.login.LoginOutputBoundary; +import use_case.login.LoginOutputData; +import view.BuyView; + +public class BuyPresenter implements BuyOutputBoundary { + + private BuyViewModel buyViewModel; + private final ViewManagerModel viewManagerModel; + private final HomeViewModel homeViewModel; + + public BuyPresenter(BuyViewModel buyViewModel, ViewManagerModel viewManagerModel, HomeViewModel homeViewModel) { + this.buyViewModel = buyViewModel; + this.viewManagerModel = viewManagerModel; + this.homeViewModel = homeViewModel; + } + + @Override + public void prepareSuccessView(BuyOutputData response) { + // On success, switch to the home view. + // Store the stock information later. + + this.viewManagerModel.setState(homeViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + + @Override + public void switchToHomeView() { + viewManagerModel.setState(homeViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/buy_view/BuyState.java b/src/main/java/interface_adapter/buy_view/BuyState.java new file mode 100644 index 000000000..920ef269a --- /dev/null +++ b/src/main/java/interface_adapter/buy_view/BuyState.java @@ -0,0 +1,23 @@ +package interface_adapter.buy_view; + +/** + * The state for the Buy View Model. + */ +public class BuyState { + private String price = ""; + private String quantity = ""; + + public String getPrice() { + return price; + } + + public String getQunatity() { + return quantity; + } + + public void setPrice(String price) { + this.price = price; + } + + public void setQuantity(String qunatity) { this.quantity = qunatity; } +} diff --git a/src/main/java/interface_adapter/buy_view/BuyViewModel.java b/src/main/java/interface_adapter/buy_view/BuyViewModel.java new file mode 100644 index 000000000..3d5e66e62 --- /dev/null +++ b/src/main/java/interface_adapter/buy_view/BuyViewModel.java @@ -0,0 +1,20 @@ +package interface_adapter.buy_view; + +import interface_adapter.ViewModel; +import interface_adapter.signup.SignupState; +import interface_adapter.stock_view.StockViewState; + +/** + * The ViewModel for the Buy View. + */ + +public class BuyViewModel extends ViewModel { + + public static final String BUY_BUTTON_LABEL = "Sign up"; + public static final String CANCEL_BUTTON_LABEL = "Cancel"; + + public BuyViewModel() { + super("buy view"); + setState(new BuyState()); + } +} diff --git a/src/main/java/use_case/buy/BuyInputBoundary.java b/src/main/java/use_case/buy/BuyInputBoundary.java new file mode 100644 index 000000000..bedacbb99 --- /dev/null +++ b/src/main/java/use_case/buy/BuyInputBoundary.java @@ -0,0 +1,18 @@ +package use_case.buy; + +/** + * Input Boundary for actions which are related to buying stock. + */ +public interface BuyInputBoundary { + + /** + * Executes the buy use case. + * @param buyInputData the input data + */ + void execute(BuyInputData buyInputData); + + /** + * Executes the switch to home view use case. + */ + void switchToHomeView(); +} diff --git a/src/main/java/use_case/buy/BuyInputData.java b/src/main/java/use_case/buy/BuyInputData.java new file mode 100644 index 000000000..db5801925 --- /dev/null +++ b/src/main/java/use_case/buy/BuyInputData.java @@ -0,0 +1,24 @@ +package use_case.buy; + +/** + * The Input Data for the Buy Use Case. + */ +public class BuyInputData { + + private final String price; + private final String quantity; + + public BuyInputData(String price, String quantity) { + this.price = price; + this.quantity = quantity; + } + + String getPrice() { + return price; + } + + String getQuantity() { + return quantity; + } + +} diff --git a/src/main/java/use_case/buy/BuyInteractor.java b/src/main/java/use_case/buy/BuyInteractor.java new file mode 100644 index 000000000..2cfcee3cc --- /dev/null +++ b/src/main/java/use_case/buy/BuyInteractor.java @@ -0,0 +1,35 @@ +package use_case.buy; + +import entity.Stock; + +/** + * The Login Interactor. + */ +public class BuyInteractor implements BuyInputBoundary { + + private final BuyUserDataAccessInterface buyUserDataAccessObject; + private final BuyOutputBoundary buyPresenter; + + public BuyInteractor(BuyUserDataAccessInterface userDataAccessInterface, BuyOutputBoundary buyOutputBoundary) { + this.buyUserDataAccessObject = userDataAccessInterface; + this.buyPresenter = buyOutputBoundary; + } + + @Override + public void execute(BuyInputData buyInputData) { + final String price = buyInputData.getPrice(); + final String quantity = buyInputData.getQuantity(); + + // More code is needed to save the purchased stock information + // final Stock stock = buyUserDataAccessObject.get(buyInputData.getPrice()); + // buyUserDataAccessObject.setCurrentPrice(stock.getPrice()); + + final BuyOutputData buyOutputData = new BuyOutputData(); + buyPresenter.prepareSuccessView(buyOutputData); + } + + @Override + public void switchToHomeView() { + buyPresenter.switchToHomeView(); + } +} diff --git a/src/main/java/use_case/buy/BuyOutputBoundary.java b/src/main/java/use_case/buy/BuyOutputBoundary.java new file mode 100644 index 000000000..abaad9057 --- /dev/null +++ b/src/main/java/use_case/buy/BuyOutputBoundary.java @@ -0,0 +1,18 @@ +package use_case.buy; + +/** + * The output boundary for the Buy Use Case. + */ +public interface BuyOutputBoundary { + + /** + * Prepares the success view for the Buy Use Case. + * @param outputData the output data + */ + void prepareSuccessView(BuyOutputData outputData); + + /** + * Switches to the Home View. + */ + void switchToHomeView(); +} diff --git a/src/main/java/use_case/buy/BuyOutputData.java b/src/main/java/use_case/buy/BuyOutputData.java new file mode 100644 index 000000000..ac4a10506 --- /dev/null +++ b/src/main/java/use_case/buy/BuyOutputData.java @@ -0,0 +1,11 @@ +package use_case.buy; + +/** + * Output Data for the Buy Use Case. + */ +public class BuyOutputData { + // private final String quantity; + // private final String stockName; + + public BuyOutputData() {} +} diff --git a/src/main/java/use_case/buy/BuyUserDataAccessInterface.java b/src/main/java/use_case/buy/BuyUserDataAccessInterface.java new file mode 100644 index 000000000..f9d0d403b --- /dev/null +++ b/src/main/java/use_case/buy/BuyUserDataAccessInterface.java @@ -0,0 +1,7 @@ +package use_case.buy; + +/** + * DAO for the Buy Use Case. + */ +public interface BuyUserDataAccessInterface { +} diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java new file mode 100644 index 000000000..aa725bfef --- /dev/null +++ b/src/main/java/view/BuyView.java @@ -0,0 +1,160 @@ +package view; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.*; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import interface_adapter.ViewManagerModel; +import interface_adapter.buy_view.BuyController; +import interface_adapter.buy_view.BuyState; +import interface_adapter.buy_view.BuyViewModel; + +/** + * The View for the user buy a stock. + */ +public class BuyView extends JPanel implements ActionListener, PropertyChangeListener { + + private final String viewName = "buy view"; + private final ViewManagerModel viewManagerModel; + private final BuyViewModel buyViewModel; + + private final JTextField quantityInputField = new JTextField(15); + private final JTextField priceInputField = new JTextField(15); + + private final JButton buy; + private final JButton cancel; + + private BuyController buyController; + + public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { + this.viewManagerModel = viewManagerModel; + this.buyViewModel = viewModel; + + setLayout(new BorderLayout()); + setBackground(Color.WHITE); + + final JLabel title = new JLabel("Buy Screen"); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + + final LabelTextPanel priceInfo = new LabelTextPanel( + new JLabel("Price"), priceInputField); + final LabelTextPanel quantityInfo = new LabelTextPanel( + new JLabel("Quantity"), quantityInputField); + + final JPanel buttons = new JPanel(); + buy = new JButton("Buy"); + buttons.add(buy); + + cancel = new JButton("Cancel"); + buttons.add(cancel); + + buy.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + buyController.switchToHomeView(); + } + } + ); + + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); + } + }); + + quantityInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final BuyState currentState = buyViewModel.getState(); + currentState.setQuantity(quantityInputField.getText()); + buyViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + priceInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final BuyState currentState = buyViewModel.getState(); + currentState.setPrice(new String(priceInputField.getText())); + buyViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + + this.add(title); + this.add(priceInfo); + this.add(quantityInfo); + this.add(buttons); + } + + /** + * React to a button click that results in evt. + * @param evt the ActionEvent to react to + */ + public void actionPerformed(ActionEvent evt) { + System.out.println("Click " + evt.getActionCommand()); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + final BuyState state = (BuyState) evt.getNewValue(); + setFields(state); + } + + private void setFields(BuyState state) { + priceInputField.setText(state.getPrice()); + quantityInputField.setText(state.getQunatity()); + } + + public String getViewName() { + return viewName; + } + + public void setBuyController(BuyController buyController) { + this.buyController = buyController; + } +} diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 86620b80c..37493f7aa 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -6,6 +6,9 @@ import javax.swing.*; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -80,10 +83,19 @@ private JPanel createActionButtonsPanel() { actionPanel.setBackground(Color.WHITE); actionPanel.setAlignmentX(Component.CENTER_ALIGNMENT); - JButton buyButton = new JButton("BUY"); + final JButton buyButton = new JButton("BUY"); buyButton.setFont(new Font("SansSerif", Font.BOLD, 18)); buyButton.setFocusPainted(false); - buyButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "Buy action triggered!")); + // buyButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "Buy action triggered!")); + // respond to buy button + buyButton.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + viewManagerModel.setState("buy view"); + viewManagerModel.firePropertyChanged(); + } + } + ); JButton favoriteButton = new JButton("★"); favoriteButton.setFont(new Font("SansSerif", Font.BOLD, 18)); From ac6f5849f2b646deb1444489465d066bb7694fc5 Mon Sep 17 00:00:00 2001 From: Ryan Jin Date: Thu, 21 Nov 2024 21:17:17 -0500 Subject: [PATCH 051/113] help --- .../data_access/FileUserDataAccessObject.java | 16 ++++++++++++++-- src/main/java/entity/SimulatedHolding.java | 12 ++++++++++++ .../portfolio/PortfolioDataAccessInterface.java | 17 +++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 59e2e7643..9bb5375b0 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -11,6 +11,7 @@ import entity.*; import use_case.change_password.ChangePasswordUserDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; +import use_case.portfolio.PortfolioDataAccessInterface; import use_case.signup.SignupUserDataAccessInterface; import use_case.watchlist.WatchListDataAccessInterface; import use_case.watchlist.WatchListModifyDataAccessInterface; @@ -18,7 +19,7 @@ /** * DAO for user data implemented using a File to persist the data. */ -public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface { +public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface, PortfolioDataAccessInterface { private final ArrayList watchList = new ArrayList<>(); private final ArrayList portfolioList = new ArrayList<>(); @@ -123,7 +124,18 @@ public void savePortfolioList() { } public void addToPortfolioList(SimulatedHolding simulatedHolding) { - portfolioList.add(simulatedHolding); + boolean duplicate = false; + for (SimulatedHolding data : portfolioList) { + if (data.getSymbol().equals(simulatedHolding.getSymbol())) { + data.setPurchaseAmount(simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount()); + data.setPurchasePrice(simulatedHolding.getPurchasePrice() + data.getPurchasePrice()); + duplicate = true; + break; + } + } + if (!duplicate) { + portfolioList.add(simulatedHolding); + } savePortfolioList(); } diff --git a/src/main/java/entity/SimulatedHolding.java b/src/main/java/entity/SimulatedHolding.java index 18f1b659f..c339e08ad 100644 --- a/src/main/java/entity/SimulatedHolding.java +++ b/src/main/java/entity/SimulatedHolding.java @@ -19,4 +19,16 @@ public interface SimulatedHolding { */ int getPurchaseAmount(); + /** + * Sets the new purchase price of the stock. + * @param purchasePrice the new purchase price of the stock. + */ + void setPurchasePrice(double purchasePrice); + + /** + * Sets the new purchase amount of the stock. + * @param purchaseAmount the new purchase amount of the stock. + */ + void setPurchaseAmount(int purchaseAmount); + } diff --git a/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java b/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java new file mode 100644 index 000000000..5d5e0b226 --- /dev/null +++ b/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java @@ -0,0 +1,17 @@ +package use_case.portfolio; + +import entity.SimulatedHolding; + +import java.util.ArrayList; + +public interface PortfolioDataAccessInterface { + + public ArrayList getPortfolioList(); + + public void savePortfolioList(); + + public void addToPortfolioList(SimulatedHolding simulatedHolding); + + public void removeFromPortfolioList(SimulatedHolding simulatedHolding); + +} From 1a2545c17e6a890080b5059ad1b87965039aa290 Mon Sep 17 00:00:00 2001 From: misaka Date: Fri, 22 Nov 2024 17:34:20 -0500 Subject: [PATCH 052/113] Added functionality to allow clicking on individual stocks in the watchlist to navigate to their detailed pages. The back functionality is not fully implemented yet. --- src/main/java/app/AppBuilder.java | 26 ++++++++-- src/main/java/app/Main.java | 1 + .../data_access/DBUserDataAccessObject.java | 14 +++++- .../watchlist_view/WatchListViewModel.java | 1 + .../watchlist_view/WatchListViewState.java | 7 +++ .../watchlist_view/WatchlistController.java | 33 +++++++++++++ .../watchlist_view/WatchlistPresenter.java | 48 +++++++++++++++++++ .../watchlist/WatchlistInputBoundary.java | 21 ++++++++ .../watchlist/WatchlistInteractor.java | 48 +++++++++++++++++++ .../watchlist/WatchlistOutputBoundary.java | 19 ++++++++ .../watchlist/WatchlistOutputData.java | 25 ++++++++++ src/main/java/view/WatchListView.java | 46 +++++++++++++++--- 12 files changed, 278 insertions(+), 11 deletions(-) create mode 100644 src/main/java/interface_adapter/watchlist_view/WatchlistController.java create mode 100644 src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java create mode 100644 src/main/java/use_case/watchlist/WatchlistInputBoundary.java create mode 100644 src/main/java/use_case/watchlist/WatchlistInteractor.java create mode 100644 src/main/java/use_case/watchlist/WatchlistOutputBoundary.java create mode 100644 src/main/java/use_case/watchlist/WatchlistOutputData.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index f13b11bb6..4afba30f9 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -18,9 +18,9 @@ import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; import interface_adapter.change_password.LoggedInViewModel; -import interface_adapter.home_view.HomeController; -import interface_adapter.home_view.HomePresenter; -import interface_adapter.home_view.HomeViewModel; +import interface_adapter.home_view.*; +import interface_adapter.home_view.WatchlistController; +import interface_adapter.home_view.WatchlistPresenter; import interface_adapter.login.LoginController; import interface_adapter.login.LoginPresenter; import interface_adapter.login.LoginViewModel; @@ -40,6 +40,10 @@ import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; import use_case.home_view.*; +import use_case.home_view.WatchlistInputBoundary; +import use_case.home_view.WatchlistInteractor; +import use_case.home_view.WatchlistOutputBoundary; +import use_case.home_view.WatchlistOutputData; import use_case.login.LoginInputBoundary; import use_case.login.LoginInteractor; import use_case.login.LoginOutputBoundary; @@ -207,6 +211,22 @@ public AppBuilder addHomeUseCase() { return this; } + /** + * Adds the Home Use Case to the application. + * @return this builder + */ + public AppBuilder addWatchlistUseCase() { + final WatchlistOutputBoundary watchlistOutputBoundary = new WatchlistPresenter( + viewManagerModel, + stockViewModel, + watchListViewModel); + final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary); + + final WatchlistController controller = new WatchlistController(watchlistInteractor); + watchListView.setwatchlistController(controller); + return this; + } + /** * Adds the Signup Use Case to the application. * @return this builder diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index a748c79e6..51b2c51d5 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -20,6 +20,7 @@ public static void main(String[] args) { .addLoggedInView() .addStockView() .addHomeUseCase() + .addWatchlistUseCase() .addSignupUseCase() .addLoginUseCase() .addLogoutUseCase() diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index a1d83372e..1def6227b 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -18,6 +18,7 @@ import use_case.login.LoginUserDataAccessInterface; import use_case.logout.LogoutUserDataAccessInterface; import use_case.signup.SignupUserDataAccessInterface; +import use_case.watchlist.WatchListDataAccessInterface; /** * The DAO for user data. @@ -26,7 +27,8 @@ public class DBUserDataAccessObject implements SignupUserDataAccessInterface, LoginUserDataAccessInterface, ChangePasswordUserDataAccessInterface, LogoutUserDataAccessInterface, - HomeDataAccessInterface { + HomeDataAccessInterface, + WatchListDataAccessInterface { private static final int SUCCESS_CODE = 200; private static final String CONTENT_TYPE_LABEL = "Content-Type"; @@ -199,4 +201,14 @@ public Stock getStock(String symbol) { public String getCurrentUsername() { return null; } + + @Override + public ArrayList getWatchList() { + return null; + } + + @Override + public void saveWatchList() { + + } } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java index 95c06c9f8..f2aa3d0a5 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java @@ -11,6 +11,7 @@ * The View Model for the WatchList View. */ public class WatchListViewModel extends ViewModel { + public WatchListViewModel() { super("watch list view"); setState(new WatchListViewState()); diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java index f87d3e6c9..5c35bd2e8 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java @@ -8,8 +8,12 @@ * The state for the Stock View Model. */ public class WatchListViewState { + private String symbol; private Stock stock; private ArrayList watchlist; + public String getSymbol() { + return symbol; + } public Stock getStock() { return stock; @@ -26,4 +30,7 @@ public void setStock(Stock newStock) { public void setWatchlist(ArrayList newWatchlist) { this.watchlist = newWatchlist; } + public void setSymbol(String symbol) { + this.symbol = symbol; + } } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchlistController.java b/src/main/java/interface_adapter/watchlist_view/WatchlistController.java new file mode 100644 index 000000000..3184d72ce --- /dev/null +++ b/src/main/java/interface_adapter/watchlist_view/WatchlistController.java @@ -0,0 +1,33 @@ +package interface_adapter.home_view; + +import entity.Stock; +import use_case.home_view.HomeInputBoundary; +import use_case.home_view.SearchInputData; +import use_case.home_view.WatchlistInputBoundary; + +import java.util.ArrayList; + +/** + * The controller for the Login Use Case. + */ +public class WatchlistController { + + private final WatchlistInputBoundary watchlistInteractor; + + public WatchlistController(WatchlistInputBoundary watchlistInteractor) { + this.watchlistInteractor = watchlistInteractor; + } + + /** + * Executes the Search Use Case. + * @param symbol the username of the user logging in + */ + public void search(String symbol) { + final SearchInputData searchInputData = new SearchInputData(symbol); + watchlistInteractor.search(searchInputData); + } + + public void getWatchList() { + watchlistInteractor.getWatchListData(); + } +} \ No newline at end of file diff --git a/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java b/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java new file mode 100644 index 000000000..d5594b19d --- /dev/null +++ b/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java @@ -0,0 +1,48 @@ +package interface_adapter.home_view; + +import entity.Stock; +import interface_adapter.ViewManagerModel; +import interface_adapter.login.LoginViewModel; +import interface_adapter.portfolio.PortfolioViewModel; +import interface_adapter.signup.SignupViewModel; +import interface_adapter.stock_view.StockViewModel; +import interface_adapter.stock_view.StockViewState; +import interface_adapter.stock_view.WatchListViewState; +import interface_adapter.watchlist_view.WatchListViewModel; +import use_case.home_view.HomeOutputBoundary; +import use_case.home_view.SearchOutputData; +import use_case.home_view.WatchlistOutputBoundary; +import view.WatchListView; + +import java.util.ArrayList; + +/** + * The Presenter for the Search Use Case. + */ +public class WatchlistPresenter implements WatchlistOutputBoundary { + + private final ViewManagerModel viewManagerModel; + private final StockViewModel stockViewModel; + private final WatchListViewModel watchListViewModel; + + public WatchlistPresenter( + ViewManagerModel viewManagerModel, + StockViewModel stockViewModel, + WatchListViewModel watchListViewModel) { + + this.viewManagerModel = viewManagerModel; + this.stockViewModel = stockViewModel; + this.watchListViewModel = watchListViewModel; + } + + public void prepareSuccessView(SearchOutputData searchOutputData) { + // Show stock view + final StockViewState stockViewState = new StockViewState(); + stockViewState.setStock(searchOutputData.getStock()); + this.stockViewModel.setState(stockViewState); + this.stockViewModel.firePropertyChanged(); + + viewManagerModel.setState("StockView"); + viewManagerModel.firePropertyChanged(); + } +} \ No newline at end of file diff --git a/src/main/java/use_case/watchlist/WatchlistInputBoundary.java b/src/main/java/use_case/watchlist/WatchlistInputBoundary.java new file mode 100644 index 000000000..9373b1e3b --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchlistInputBoundary.java @@ -0,0 +1,21 @@ +package use_case.home_view; + +import entity.Stock; + +import java.util.ArrayList; + +public interface WatchlistInputBoundary { + + /** + * Executes the search use case. + * @param searchInputData the input data + */ + void search(SearchInputData searchInputData); + + /** + * Executes the search use case. + * @param searchInputData the input data + */ + + void getWatchListData(); +} diff --git a/src/main/java/use_case/watchlist/WatchlistInteractor.java b/src/main/java/use_case/watchlist/WatchlistInteractor.java new file mode 100644 index 000000000..8ecd5bc11 --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchlistInteractor.java @@ -0,0 +1,48 @@ +package use_case.home_view; + +import entity.CommonStockFactory; +import entity.Stock; +import entity.StockFactory; +import interface_adapter.home_view.WatchlistPresenter; +import use_case.home_view.WatchlistOutputBoundary; +import use_case.home_view.WatchlistInputBoundary; +import use_case.watchlist.WatchListDataAccessInterface; + +/** + * The Home View Interactor. + */ +public class WatchlistInteractor implements WatchlistInputBoundary { + + private final WatchListDataAccessInterface watchlistDataAccessInterface; + private final WatchlistOutputBoundary watchlistPresenter; + + public WatchlistInteractor(WatchListDataAccessInterface watchlistDataAccessInterface, + WatchlistOutputBoundary watchlistPresenter) { + this.watchlistDataAccessInterface = watchlistDataAccessInterface; + this.watchlistPresenter = watchlistPresenter; + } + + public void search(SearchInputData searchInputData) { +// final String stockSymbol = searchInputData.getStockSymbol(); +// try { +// final Stock stock = homeDataAccessInterface.getStock(stockSymbol); +// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); +// homePresenter.prepareSuccessView(searchOutputData); +// } +// catch (Exception err) { +// err.printStackTrace(); +// homePresenter.prepareFailView("Failed to fetch stock data"); +// } + final StockFactory stockFactory = new CommonStockFactory(); + final Stock stock = stockFactory.create(searchInputData.getStockSymbol(), 128.2, 322.1, 100002322, 500.1, 100.23); + + final SearchOutputData searchOutputData = new SearchOutputData(stock, false); + watchlistPresenter.prepareSuccessView(searchOutputData); + } + + + @Override + public void getWatchListData() { + + } +} \ No newline at end of file diff --git a/src/main/java/use_case/watchlist/WatchlistOutputBoundary.java b/src/main/java/use_case/watchlist/WatchlistOutputBoundary.java new file mode 100644 index 000000000..059d9f515 --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchlistOutputBoundary.java @@ -0,0 +1,19 @@ +package use_case.home_view; + +import entity.Stock; + +import java.util.ArrayList; + +/** + * The output boundary for the Home Use Case. + */ +public interface WatchlistOutputBoundary { + + /** + * Prepares the success view for the Search Use Case. + * + * @param searchOutputData the output data + */ + void prepareSuccessView(SearchOutputData searchOutputData); + +} \ No newline at end of file diff --git a/src/main/java/use_case/watchlist/WatchlistOutputData.java b/src/main/java/use_case/watchlist/WatchlistOutputData.java new file mode 100644 index 000000000..efd1cfe50 --- /dev/null +++ b/src/main/java/use_case/watchlist/WatchlistOutputData.java @@ -0,0 +1,25 @@ +package use_case.home_view; + +import entity.Stock; + +import java.util.ArrayList; + +/** + * The output boundary for the Home Use Case. + */ +public interface WatchlistOutputData { + + /** + * Prepares the success view for the Search Use Case. + * + * @param searchOutputData the output data + */ + void prepareSuccessView(SearchOutputData searchOutputData); + + /** + * Prepares the failure view for the Search Use Case. + * + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); +} \ No newline at end of file diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 625e9a7d8..e68a62418 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -1,10 +1,8 @@ package view; import data_access.DBUserDataAccessObject; -import entity.Stock; import interface_adapter.ViewManagerModel; -import interface_adapter.change_password.LoggedInState; -import interface_adapter.login.LoginState; +import interface_adapter.home_view.WatchlistController; import interface_adapter.stock_view.WatchListViewState; import interface_adapter.watchlist_view.WatchListViewModel; @@ -23,6 +21,7 @@ public class WatchListView extends JPanel implements PropertyChangeListener { private final WatchListViewModel watchListViewModel; private final DBUserDataAccessObject dbUserDataAccessObject; private final JPanel contentPanel = new JPanel(); + private WatchlistController watchlistController; public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel, DBUserDataAccessObject dbUserDataAccessObject) { this.viewManagerModel = viewManagerModel; @@ -32,6 +31,7 @@ public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerM this.watchListViewModel = viewModel; this.watchListViewModel.addPropertyChangeListener(this); + // return button final JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); topPanel.setBackground(Color.WHITE); @@ -59,9 +59,9 @@ public void actionPerformed(ActionEvent e) { contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); - addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09"); - addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00"); - addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25"); +// addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09"); +// addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00"); +// addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25"); add(contentPanel, BorderLayout.CENTER); @@ -72,8 +72,26 @@ private void addStockItem(JPanel contentPanel, String code, String price, String stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); stockPanel.setBackground(Color.WHITE); + stockPanel.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mouseClicked(java.awt.event.MouseEvent evt) { + goToStockInformation(code); + } + + @Override + public void mouseEntered(java.awt.event.MouseEvent evt) { + stockPanel.setBackground(new Color(240, 240, 240)); + } + + @Override + public void mouseExited(java.awt.event.MouseEvent evt) { + stockPanel.setBackground(Color.WHITE); + } + }); + // Left part: Stock code and price final JPanel leftPanel = new JPanel(); + leftPanel.setOpaque(false); leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); leftPanel.setBackground(Color.WHITE); @@ -87,6 +105,7 @@ private void addStockItem(JPanel contentPanel, String code, String price, String // Right part: up and down information final JPanel rightPanel = new JPanel(); + rightPanel.setOpaque(false); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); rightPanel.setBackground(Color.WHITE); rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); @@ -122,6 +141,15 @@ private void updateWatchList(ArrayList updatedWatchList) { contentPanel.repaint(); } + private void goToStockInformation(String code) { + if (watchlistController == null) { + throw new IllegalStateException("WatchlistController is not initialized."); + } + final WatchListViewState currentState = watchListViewModel.getState(); + currentState.setSymbol(code); + watchlistController.search(currentState.getSymbol()); + } + private void createWatchListview(ArrayList WatchList) { for (String stockcode: WatchList) { // final Stock stock = dbUserDataAccessObject.getStock(stockcode); @@ -131,7 +159,7 @@ private void createWatchListview(ArrayList WatchList) { // final String volume = Double.toString(stock.getVolume()); // addStockItem(contentPanel, stock.getSymbol(), "$" + price, dailyChange, dailyPercentage, volume); - addStockItem(contentPanel, stockcode, "$" + "123", "234", "0.45%", "1232323.23"); + addStockItem(contentPanel, stockcode, "$" + "123", "+$" + "234", "0.45%", "100000.23"); } } @@ -139,5 +167,9 @@ public String getViewName() { return "WatchListView"; } + public void setwatchlistController(WatchlistController controller) { + this.watchlistController = controller; + controller.getWatchList(); + } } From 94708667a00709c27fe8fcc2730c551fb1581e20 Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 22 Nov 2024 19:38:25 -0500 Subject: [PATCH 053/113] stock use case partly done --- src/main/java/app/AppBuilder.java | 19 +++++++++ src/main/java/app/Main.java | 1 + .../java/use_case/stock/StockController.java | 21 ++++++++++ .../use_case/stock/StockInputBoundary.java | 17 ++++++++ .../java/use_case/stock/StockInteractor.java | 34 ++++++++++++++++ .../use_case/stock/StockOutputBoundary.java | 23 +++++++++++ .../java/use_case/stock/StockPresenter.java | 33 +++++++++++++++ src/main/java/view/StockView.java | 40 ++++++++++++------- 8 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 src/main/java/use_case/stock/StockController.java create mode 100644 src/main/java/use_case/stock/StockInputBoundary.java create mode 100644 src/main/java/use_case/stock/StockInteractor.java create mode 100644 src/main/java/use_case/stock/StockOutputBoundary.java create mode 100644 src/main/java/use_case/stock/StockPresenter.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index f13b11bb6..ec8f4584e 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -30,6 +30,8 @@ import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupPresenter; import interface_adapter.signup.SignupViewModel; +import interface_adapter.stock_view.StockController; +import interface_adapter.stock_view.StockPresenter; import interface_adapter.stock_view.StockViewModel; import interface_adapter.watchlist_view.WatchListViewModel; import interface_adapter.portfolio.PortfolioViewModel; @@ -49,6 +51,10 @@ import use_case.signup.SignupInputBoundary; import use_case.signup.SignupInteractor; import use_case.signup.SignupOutputBoundary; +import use_case.stock.StockInputBoundary; +import use_case.stock.StockInteractor; +import use_case.stock.StockOutputBoundary; +import use_case.watchlist.WatchListModifyDataAccessInterface; import view.*; /** @@ -269,6 +275,19 @@ public AppBuilder addLogoutUseCase() { loggedInView.setLogoutController(logoutController); return this; } + /** + * Adds the Stock Use Case to the application. + * @return this builder + */ + public AppBuilder addStockUseCase() { + final StockOutputBoundary stockPresenter = new StockPresenter(stockViewModel, viewManagerModel); + final WatchListModifyDataAccessInterface watchlistDataAccess = dbUserDataAccessObject; // 确保 dbUserDataAccessObject 实现了 WatchListModifyDataAccessInterface + final StockInputBoundary stockInteractor = new StockInteractor(stockPresenter, watchlistDataAccess); + StockController stockController = new StockController(stockInteractor); + stockView.setStockController(stockController); + + return this; + } /** * Adds the Buy Use Case to the application. diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index a748c79e6..f1140e917 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -24,6 +24,7 @@ public static void main(String[] args) { .addLoginUseCase() .addLogoutUseCase() .addChangePasswordUseCase() + .addStockUseCase() .addPortfolioView() .build(); diff --git a/src/main/java/use_case/stock/StockController.java b/src/main/java/use_case/stock/StockController.java new file mode 100644 index 000000000..a2c5d56b7 --- /dev/null +++ b/src/main/java/use_case/stock/StockController.java @@ -0,0 +1,21 @@ +package interface_adapter.stock_view; + +import entity.Stock; +import use_case.stock.StockInputBoundary; + +public class StockController { + + private final StockInputBoundary stockInteractor; + + public StockController(StockInputBoundary stockInteractor) { + this.stockInteractor = stockInteractor; + } + + public void buyStock(Stock stock) { + stockInteractor.buyStock(stock); + } + + public void toggleWatchlist(Stock stock) { + stockInteractor.toggleWatchlist(stock); + } +} diff --git a/src/main/java/use_case/stock/StockInputBoundary.java b/src/main/java/use_case/stock/StockInputBoundary.java new file mode 100644 index 000000000..37e15873b --- /dev/null +++ b/src/main/java/use_case/stock/StockInputBoundary.java @@ -0,0 +1,17 @@ +package use_case.stock; + +import entity.Stock; + +public interface StockInputBoundary { + /** + * Handles the logic when a user wants to buy a stock. + * @param stock The stock to buy. + */ + void buyStock(Stock stock); + + /** + * Handles adding or removing a stock from the watchlist. + * @param stock The stock to add/remove from the watchlist. + */ + void toggleWatchlist(Stock stock); +} diff --git a/src/main/java/use_case/stock/StockInteractor.java b/src/main/java/use_case/stock/StockInteractor.java new file mode 100644 index 000000000..e1ff903ff --- /dev/null +++ b/src/main/java/use_case/stock/StockInteractor.java @@ -0,0 +1,34 @@ +package use_case.stock; + +import entity.Stock; +import interface_adapter.stock_view.StockOutputBoundary; +import use_case.watchlist.WatchListModifyDataAccessInterface; + +public class StockInteractor implements StockInputBoundary { + + private final StockOutputBoundary stockPresenter; + private final WatchListModifyDataAccessInterface watchlistDataAccess; + + public StockInteractor(StockOutputBoundary stockPresenter, WatchListModifyDataAccessInterface watchlistDataAccess) { + this.stockPresenter = stockPresenter; + this.watchlistDataAccess = watchlistDataAccess; + } + + @Override + public void buyStock(Stock stock) { + // 通知 Presenter 执行页面跳转 + stockPresenter.presentBuyView(stock); + } + + @Override + public void toggleWatchlist(Stock stock) { + if (watchlistDataAccess.isInWatchList(stock.getSymbol())) { + watchlistDataAccess.removeFromWatchList(stock.getSymbol()); + stockPresenter.presentRemoveFromWatchlist(stock); + } + else { + watchlistDataAccess.addToWatchList(stock.getSymbol()); + stockPresenter.presentAddToWatchlist(stock); + } + } +} diff --git a/src/main/java/use_case/stock/StockOutputBoundary.java b/src/main/java/use_case/stock/StockOutputBoundary.java new file mode 100644 index 000000000..1c4f3c0ea --- /dev/null +++ b/src/main/java/use_case/stock/StockOutputBoundary.java @@ -0,0 +1,23 @@ +package use_case.stock; + +import entity.Stock; + +public interface StockOutputBoundary { + /** + * Presents the Buy View when a user wants to buy a stock. + * @param stock The stock to buy. + */ + void presentBuyView(Stock stock); + + /** + * Presents the updated watchlist after adding a stock. + * @param stock The stock that was added. + */ + void presentAddToWatchlist(Stock stock); + + /** + * Presents the updated watchlist after removing a stock. + * @param stock The stock that was removed. + */ + void presentRemoveFromWatchlist(Stock stock); +} diff --git a/src/main/java/use_case/stock/StockPresenter.java b/src/main/java/use_case/stock/StockPresenter.java new file mode 100644 index 000000000..6ee21554b --- /dev/null +++ b/src/main/java/use_case/stock/StockPresenter.java @@ -0,0 +1,33 @@ +package interface_adapter.stock_view; + +import entity.Stock; +import interface_adapter.ViewManagerModel; +import use_case.stock.StockOutputBoundary; + +public class StockPresenter implements StockOutputBoundary { + + private final StockViewModel stockViewModel; + private final ViewManagerModel viewManagerModel; + + public StockPresenter(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { + this.stockViewModel = stockViewModel; + this.viewManagerModel = viewManagerModel; + } + + @Override + public void presentBuyView(Stock stock) { + stockViewModel.setStock(stock); + viewManagerModel.setState("buy view"); + viewManagerModel.firePropertyChanged(); + } + + @Override + public void presentAddToWatchlist(Stock stock) { + System.out.println("Stock " + stock.getSymbol() + " added to watchlist."); + } + + @Override + public void presentRemoveFromWatchlist(Stock stock) { + System.out.println("Stock " + stock.getSymbol() + " removed from watchlist."); + } +} diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 37493f7aa..ae0b48666 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -3,12 +3,12 @@ import entity.Stock; import interface_adapter.ViewManagerModel; import interface_adapter.stock_view.StockViewModel; +import interface_adapter.stock_view.StockController; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -19,6 +19,8 @@ public class StockView extends AbstractViewWithBackButton implements PropertyCha private final ViewManagerModel viewManagerModel; private final StockViewModel stockViewModel; + private StockController stockController; + private JLabel stockSymbolLabel; private JLabel stockPriceLabel; private JLabel openPriceLabel; @@ -49,6 +51,10 @@ public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerMode add(Box.createVerticalGlue()); } + public void setStockController(StockController stockController) { + this.stockController = stockController; + } + private void initializeComponents(JPanel contentPanel) { stockSymbolLabel = createLabel("", 24, Font.BOLD); stockPriceLabel = createLabel("", 18, Font.PLAIN); @@ -86,21 +92,26 @@ private JPanel createActionButtonsPanel() { final JButton buyButton = new JButton("BUY"); buyButton.setFont(new Font("SansSerif", Font.BOLD, 18)); buyButton.setFocusPainted(false); - // buyButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "Buy action triggered!")); - // respond to buy button - buyButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent evt) { - viewManagerModel.setState("buy view"); - viewManagerModel.firePropertyChanged(); - } - } - ); + + // Buy Button Action: Invoke the controller to buy stock + buyButton.addActionListener(e -> { + Stock currentStock = stockViewModel.getState().getStock(); + if (currentStock != null && stockController != null) { + stockController.buyStock(currentStock); + } + }); JButton favoriteButton = new JButton("★"); favoriteButton.setFont(new Font("SansSerif", Font.BOLD, 18)); favoriteButton.setFocusPainted(false); - favoriteButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "Added to favorites!")); + + // Favorite Button Action: Add or remove stock from watchlist + favoriteButton.addActionListener(e -> { + Stock currentStock = stockViewModel.getState().getStock(); + if (currentStock != null && stockController != null) { + stockController.toggleWatchlist(currentStock); + } + }); actionPanel.add(Box.createHorizontalGlue()); actionPanel.add(buyButton); @@ -121,8 +132,7 @@ public void updateStockData(Stock stock) { double volume = stock.getVolume(); String volumeText = (volume >= 1_000_000) ? (volume / 1_000_000) + " M" : - (volume >= 1_000) ? (volume / 1_000) + " K" : - String.valueOf(volume); + (volume >= 1_000) ? (volume / 1_000) + " K" : String.valueOf(volume); volumeLabel.setText("Volume: " + volumeText); } @@ -150,4 +160,4 @@ public void propertyChange(PropertyChangeEvent evt) { updateStockData(updatedStock); } } -} \ No newline at end of file +} From 51a6b51db696726cf5956144961fb76480c3cb74 Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 22 Nov 2024 19:42:40 -0500 Subject: [PATCH 054/113] stock use case partly done --- src/main/java/app/AppBuilder.java | 9 +-------- .../stock_view}/StockOutputBoundary.java | 2 +- src/main/java/use_case/stock/StockInteractor.java | 1 - src/main/java/use_case/stock/StockPresenter.java | 1 - 4 files changed, 2 insertions(+), 11 deletions(-) rename src/main/java/{use_case/stock => interface_adapter/stock_view}/StockOutputBoundary.java (93%) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index ec8f4584e..4581de49b 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -9,11 +9,8 @@ import data_access.DBUserDataAccessObject; import data_access.FileUserDataAccessObject; -import data_access.InMemoryUserDataAccessObject; import entity.*; import interface_adapter.ViewManagerModel; -import interface_adapter.buy_view.BuyController; -import interface_adapter.buy_view.BuyPresenter; import interface_adapter.buy_view.BuyViewModel; import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; @@ -34,10 +31,6 @@ import interface_adapter.stock_view.StockPresenter; import interface_adapter.stock_view.StockViewModel; import interface_adapter.watchlist_view.WatchListViewModel; -import interface_adapter.portfolio.PortfolioViewModel; -import use_case.buy.BuyInputBoundary; -import use_case.buy.BuyInteractor; -import use_case.buy.BuyOutputBoundary; import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; @@ -53,7 +46,7 @@ import use_case.signup.SignupOutputBoundary; import use_case.stock.StockInputBoundary; import use_case.stock.StockInteractor; -import use_case.stock.StockOutputBoundary; +import interface_adapter.stock_view.StockOutputBoundary; import use_case.watchlist.WatchListModifyDataAccessInterface; import view.*; diff --git a/src/main/java/use_case/stock/StockOutputBoundary.java b/src/main/java/interface_adapter/stock_view/StockOutputBoundary.java similarity index 93% rename from src/main/java/use_case/stock/StockOutputBoundary.java rename to src/main/java/interface_adapter/stock_view/StockOutputBoundary.java index 1c4f3c0ea..1da6babe0 100644 --- a/src/main/java/use_case/stock/StockOutputBoundary.java +++ b/src/main/java/interface_adapter/stock_view/StockOutputBoundary.java @@ -1,4 +1,4 @@ -package use_case.stock; +package interface_adapter.stock_view; import entity.Stock; diff --git a/src/main/java/use_case/stock/StockInteractor.java b/src/main/java/use_case/stock/StockInteractor.java index e1ff903ff..ce967de56 100644 --- a/src/main/java/use_case/stock/StockInteractor.java +++ b/src/main/java/use_case/stock/StockInteractor.java @@ -16,7 +16,6 @@ public StockInteractor(StockOutputBoundary stockPresenter, WatchListModifyDataAc @Override public void buyStock(Stock stock) { - // 通知 Presenter 执行页面跳转 stockPresenter.presentBuyView(stock); } diff --git a/src/main/java/use_case/stock/StockPresenter.java b/src/main/java/use_case/stock/StockPresenter.java index 6ee21554b..b48e723b4 100644 --- a/src/main/java/use_case/stock/StockPresenter.java +++ b/src/main/java/use_case/stock/StockPresenter.java @@ -2,7 +2,6 @@ import entity.Stock; import interface_adapter.ViewManagerModel; -import use_case.stock.StockOutputBoundary; public class StockPresenter implements StockOutputBoundary { From 7f28618c8581253aacb331972a34f0e01143137f Mon Sep 17 00:00:00 2001 From: misaka Date: Fri, 22 Nov 2024 21:13:40 -0500 Subject: [PATCH 055/113] fix bug in CommonSimulatedHolding.java --- src/main/java/entity/CommonSimulatedHolding.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/entity/CommonSimulatedHolding.java b/src/main/java/entity/CommonSimulatedHolding.java index 364108d1c..c5459fd3a 100644 --- a/src/main/java/entity/CommonSimulatedHolding.java +++ b/src/main/java/entity/CommonSimulatedHolding.java @@ -28,4 +28,14 @@ public double getPurchasePrice() { public int getPurchaseAmount() { return amount; } + + @Override + public void setPurchasePrice(double purchasePrice) { + + } + + @Override + public void setPurchaseAmount(int purchaseAmount) { + + } } From 3420f81bce890b7caa3d755144c1d75dcc1c78f7 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Nov 2024 12:53:36 -0500 Subject: [PATCH 056/113] Finish add stock to watchlist feature --- src/main/java/app/AppBuilder.java | 16 ++-- src/main/java/data_access/watchlist.txt | 2 +- .../interface_adapter/buy_view/BuyState.java | 5 ++ .../home_view/HomePresenter.java | 2 +- .../home_view/HomeState.java | 17 ++++ .../stock_view}/StockController.java | 4 +- .../stock_view/StockPresenter.java | 84 +++++++++++++++++++ .../stock_view/StockViewState.java | 5 ++ .../watchlist_view/WatchListViewState.java | 14 ++++ .../watchlist_view/WatchlistPresenter.java | 2 +- .../use_case/home_view/HomeInteractor.java | 12 +-- .../use_case/home_view/SearchInputData.java | 2 +- .../use_case/stock/StockInputBoundary.java | 3 +- .../java/use_case/stock/StockInteractor.java | 41 ++++++--- .../stock}/StockOutputBoundary.java | 8 +- .../java/use_case/stock/StockPresenter.java | 32 ------- src/main/java/view/StockView.java | 19 +++-- 17 files changed, 199 insertions(+), 69 deletions(-) rename src/main/java/{use_case/stock => interface_adapter/stock_view}/StockController.java (74%) create mode 100644 src/main/java/interface_adapter/stock_view/StockPresenter.java rename src/main/java/{interface_adapter/stock_view => use_case/stock}/StockOutputBoundary.java (75%) delete mode 100644 src/main/java/use_case/stock/StockPresenter.java diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index d5f77ee5a..7d131dd40 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -38,7 +38,6 @@ import use_case.home_view.WatchlistInputBoundary; import use_case.home_view.WatchlistInteractor; import use_case.home_view.WatchlistOutputBoundary; -import use_case.home_view.WatchlistOutputData; import use_case.login.LoginInputBoundary; import use_case.login.LoginInteractor; import use_case.login.LoginOutputBoundary; @@ -50,8 +49,7 @@ import use_case.signup.SignupOutputBoundary; import use_case.stock.StockInputBoundary; import use_case.stock.StockInteractor; -import interface_adapter.stock_view.StockOutputBoundary; -import use_case.watchlist.WatchListModifyDataAccessInterface; +import use_case.stock.StockOutputBoundary; import view.*; /** @@ -78,7 +76,6 @@ public class AppBuilder { // thought question: is the hard dependency below a problem? private final DBUserDataAccessObject dbUserDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(simulatedHoldingFactory); - //private final Buy private HomeView homeView; private HomeViewModel homeViewModel; @@ -293,10 +290,13 @@ public AppBuilder addLogoutUseCase() { * @return this builder */ public AppBuilder addStockUseCase() { - final StockOutputBoundary stockPresenter = new StockPresenter(stockViewModel, viewManagerModel); - final WatchListModifyDataAccessInterface watchlistDataAccess = dbUserDataAccessObject; // 确保 dbUserDataAccessObject 实现了 WatchListModifyDataAccessInterface - final StockInputBoundary stockInteractor = new StockInteractor(stockPresenter, watchlistDataAccess); - StockController stockController = new StockController(stockInteractor); + final StockOutputBoundary stockPresenter = new StockPresenter(stockViewModel, + buyViewModel, + homeViewModel, + watchListViewModel, + viewManagerModel); + final StockInputBoundary stockInteractor = new StockInteractor(stockPresenter, fileUserDataAccessObject, fileUserDataAccessObject); + final StockController stockController = new StockController(stockInteractor); stockView.setStockController(stockController); return this; diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index 73d1a68fd..a7493d35e 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPl,NVDA,AMD \ No newline at end of file +AAPl,AMD,NVDA \ No newline at end of file diff --git a/src/main/java/interface_adapter/buy_view/BuyState.java b/src/main/java/interface_adapter/buy_view/BuyState.java index 920ef269a..1fc64cb96 100644 --- a/src/main/java/interface_adapter/buy_view/BuyState.java +++ b/src/main/java/interface_adapter/buy_view/BuyState.java @@ -6,6 +6,7 @@ public class BuyState { private String price = ""; private String quantity = ""; + private String symbol = ""; public String getPrice() { return price; @@ -15,9 +16,13 @@ public String getQunatity() { return quantity; } + public String getSymbol() { return symbol; } + public void setPrice(String price) { this.price = price; } public void setQuantity(String qunatity) { this.quantity = qunatity; } + + public void setSymbol(String symbol) { this.symbol = symbol; } } diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index eeb65b148..67de60407 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -51,7 +51,7 @@ public void prepareSuccessView(SearchOutputData searchOutputData) { final StockViewState stockViewState = new StockViewState(); stockViewState.setStock(searchOutputData.getStock()); this.stockViewModel.setState(stockViewState); - this.stockViewModel.firePropertyChanged(); + this.stockViewModel.firePropertyChanged("switchToStockView"); // Clean home view error message this.sendErrorMessage(""); diff --git a/src/main/java/interface_adapter/home_view/HomeState.java b/src/main/java/interface_adapter/home_view/HomeState.java index cb3471185..7607e61eb 100644 --- a/src/main/java/interface_adapter/home_view/HomeState.java +++ b/src/main/java/interface_adapter/home_view/HomeState.java @@ -35,4 +35,21 @@ public void setWatchList(ArrayList watchList) { public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + + public void add(Stock stock) { + this.watchList.add(stock); + System.out.println(watchList.size()); + } + + public void remove(Stock stock) { + int i = 0; + for (Stock s : watchList) { + if (s.getSymbol().equals(stock.getSymbol())) { + break; + } + i++; + } + this.watchList.remove(i); + System.out.println(watchList.size()); + } } diff --git a/src/main/java/use_case/stock/StockController.java b/src/main/java/interface_adapter/stock_view/StockController.java similarity index 74% rename from src/main/java/use_case/stock/StockController.java rename to src/main/java/interface_adapter/stock_view/StockController.java index a2c5d56b7..eb6f6a641 100644 --- a/src/main/java/use_case/stock/StockController.java +++ b/src/main/java/interface_adapter/stock_view/StockController.java @@ -15,7 +15,7 @@ public void buyStock(Stock stock) { stockInteractor.buyStock(stock); } - public void toggleWatchlist(Stock stock) { - stockInteractor.toggleWatchlist(stock); + public void toggleWatchlist(Stock stock, Boolean shouldModifyData) { + stockInteractor.toggleWatchlist(stock, shouldModifyData); } } diff --git a/src/main/java/interface_adapter/stock_view/StockPresenter.java b/src/main/java/interface_adapter/stock_view/StockPresenter.java new file mode 100644 index 000000000..57c666695 --- /dev/null +++ b/src/main/java/interface_adapter/stock_view/StockPresenter.java @@ -0,0 +1,84 @@ +package interface_adapter.stock_view; + +import entity.Stock; +import interface_adapter.ViewManagerModel; +import interface_adapter.buy_view.BuyState; +import interface_adapter.buy_view.BuyViewModel; +import interface_adapter.home_view.HomeState; +import interface_adapter.home_view.HomeViewModel; +import interface_adapter.watchlist_view.WatchListViewModel; +import interface_adapter.stock_view.WatchListViewState; +import use_case.stock.StockOutputBoundary; +import view.StockView; + +import java.util.ArrayList; + +public class StockPresenter implements StockOutputBoundary { + + private final StockViewModel stockViewModel; + private final BuyViewModel buyViewModel; + private final HomeViewModel homeViewModel; + private final WatchListViewModel watchListViewModel; + private final ViewManagerModel viewManagerModel; + + public StockPresenter(StockViewModel stockViewModel, + BuyViewModel buyViewModel, + HomeViewModel homeViewModel, + WatchListViewModel watchListViewModel, + ViewManagerModel viewManagerModel) { + this.stockViewModel = stockViewModel; + this.buyViewModel = buyViewModel; + this.homeViewModel = homeViewModel; + this.watchListViewModel = watchListViewModel; + this.viewManagerModel = viewManagerModel; + } + + @Override + public void presentBuyView(Stock stock) { + // Pass stock data to buy view + final BuyState buyState = new BuyState(); + buyState.setSymbol(stock.getSymbol()); + buyState.setPrice(Double.toString(stock.getClosePrice())); + buyViewModel.setState(buyState); + buyViewModel.firePropertyChanged(); + + // Switch to buy view + viewManagerModel.setState("buy view"); + viewManagerModel.firePropertyChanged(); + } + + @Override + public void presentAddToWatchlist(Stock stock) { + final HomeState homeState = homeViewModel.getState(); + final WatchListViewState watchListViewState = watchListViewModel.getState(); + + // Pass new watchlist data to homeView + homeState.add(stock); + homeViewModel.firePropertyChanged("getWatchList"); + + // Pass new watchlist data to watchListView + watchListViewState.add(stock.getSymbol()); + watchListViewModel.firePropertyChanged("getWatchList"); + } + + @Override + public void presentRemoveFromWatchlist(Stock stock) { + final HomeState homeState = homeViewModel.getState(); + final WatchListViewState watchListViewState = watchListViewModel.getState(); + + // Pass new watchlist data to homeView + homeState.remove(stock); + homeViewModel.firePropertyChanged("getWatchList"); + + // Pass new watchlist data to watchListView + watchListViewState.remove(stock.getSymbol()); + watchListViewModel.firePropertyChanged("getWatchList"); + } + + @Override + public void updateFavouriteButton(Boolean isFavourite) { + final StockViewState stockViewState = stockViewModel.getState(); + stockViewState.setIsFavorite(isFavourite); + stockViewModel.firePropertyChanged("updateFavouriteButton"); + } +} diff --git a/src/main/java/interface_adapter/stock_view/StockViewState.java b/src/main/java/interface_adapter/stock_view/StockViewState.java index 116ee75d2..130b6618d 100644 --- a/src/main/java/interface_adapter/stock_view/StockViewState.java +++ b/src/main/java/interface_adapter/stock_view/StockViewState.java @@ -7,12 +7,17 @@ */ public class StockViewState { private Stock stock; + private Boolean isFavorite; public Stock getStock() { return stock; } + public Boolean getIsFavorite() { return isFavorite; } + public void setStock(Stock newStock) { this.stock = newStock; } + + public void setIsFavorite(Boolean isFavorite) { this.isFavorite = isFavorite; } } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java index 5c35bd2e8..dcd9666f8 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java @@ -11,6 +11,7 @@ public class WatchListViewState { private String symbol; private Stock stock; private ArrayList watchlist; + public String getSymbol() { return symbol; } @@ -30,7 +31,20 @@ public void setStock(Stock newStock) { public void setWatchlist(ArrayList newWatchlist) { this.watchlist = newWatchlist; } + public void setSymbol(String symbol) { this.symbol = symbol; } + + public void add(String ticker) { + if (this.watchlist != null) { + this.watchlist.add(ticker); + } + } + + public void remove(String ticker) { + if (this.watchlist != null) { + this.watchlist.remove(ticker); + } + } } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java b/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java index d5594b19d..4703b6372 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java @@ -40,7 +40,7 @@ public void prepareSuccessView(SearchOutputData searchOutputData) { final StockViewState stockViewState = new StockViewState(); stockViewState.setStock(searchOutputData.getStock()); this.stockViewModel.setState(stockViewState); - this.stockViewModel.firePropertyChanged(); + this.stockViewModel.firePropertyChanged("switchToStockView"); viewManagerModel.setState("StockView"); viewManagerModel.firePropertyChanged(); diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index aadf7ffe4..9cbb64162 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -71,13 +71,13 @@ public void getWatchListData() { final ArrayList watchList = new ArrayList<>(); final StockFactory stockFactory = new CommonStockFactory(); - final Stock aapl = stockFactory.create("AAPL", 552.2, 130.3, 100002322, 500.1, 100.23); - final Stock nvda = stockFactory.create("NVDA", 128.2, 148.2, 100002322, 500.1, 100.23); - final Stock meta = stockFactory.create("META", 11.2, 559.2, 100002322, 500.1, 100.23); - watchList.add(aapl); - watchList.add(nvda); - watchList.add(meta); + int i = 0; + for (String symbol : watchListData) { + final Stock stock = stockFactory.create(symbol, i, 130.3, 100002322, 500.1, 100.23); + watchList.add(stock); + i += 1; + } // for (String symbol : watchListData) { // final Stock stockData = homeDataAccessInterface.getStock(symbol); diff --git a/src/main/java/use_case/home_view/SearchInputData.java b/src/main/java/use_case/home_view/SearchInputData.java index 072feb612..10a10b0cc 100644 --- a/src/main/java/use_case/home_view/SearchInputData.java +++ b/src/main/java/use_case/home_view/SearchInputData.java @@ -8,7 +8,7 @@ public class SearchInputData { private final String stockSymbol; public SearchInputData(String stockSymbol) { - this.stockSymbol = stockSymbol; + this.stockSymbol = stockSymbol.toUpperCase(); } public String getStockSymbol() { diff --git a/src/main/java/use_case/stock/StockInputBoundary.java b/src/main/java/use_case/stock/StockInputBoundary.java index 37e15873b..940f78478 100644 --- a/src/main/java/use_case/stock/StockInputBoundary.java +++ b/src/main/java/use_case/stock/StockInputBoundary.java @@ -12,6 +12,7 @@ public interface StockInputBoundary { /** * Handles adding or removing a stock from the watchlist. * @param stock The stock to add/remove from the watchlist. + * @param shouldModifyData To decide if we should modify data or not. */ - void toggleWatchlist(Stock stock); + void toggleWatchlist(Stock stock, Boolean shouldModifyData); } diff --git a/src/main/java/use_case/stock/StockInteractor.java b/src/main/java/use_case/stock/StockInteractor.java index ce967de56..e7889c46d 100644 --- a/src/main/java/use_case/stock/StockInteractor.java +++ b/src/main/java/use_case/stock/StockInteractor.java @@ -1,17 +1,23 @@ package use_case.stock; import entity.Stock; -import interface_adapter.stock_view.StockOutputBoundary; +import use_case.watchlist.WatchListDataAccessInterface; import use_case.watchlist.WatchListModifyDataAccessInterface; +import java.util.ArrayList; + public class StockInteractor implements StockInputBoundary { private final StockOutputBoundary stockPresenter; - private final WatchListModifyDataAccessInterface watchlistDataAccess; + private final WatchListDataAccessInterface watchListDataAccessInterface; + private final WatchListModifyDataAccessInterface watchListModifyDataAccessInterface; - public StockInteractor(StockOutputBoundary stockPresenter, WatchListModifyDataAccessInterface watchlistDataAccess) { + public StockInteractor(StockOutputBoundary stockPresenter, + WatchListDataAccessInterface watchListDataAccessInterface, + WatchListModifyDataAccessInterface watchListModifyDataAccessInterface) { this.stockPresenter = stockPresenter; - this.watchlistDataAccess = watchlistDataAccess; + this.watchListDataAccessInterface = watchListDataAccessInterface; + this.watchListModifyDataAccessInterface = watchListModifyDataAccessInterface; } @Override @@ -20,14 +26,29 @@ public void buyStock(Stock stock) { } @Override - public void toggleWatchlist(Stock stock) { - if (watchlistDataAccess.isInWatchList(stock.getSymbol())) { - watchlistDataAccess.removeFromWatchList(stock.getSymbol()); - stockPresenter.presentRemoveFromWatchlist(stock); + public void toggleWatchlist(Stock stock, Boolean shouldModifyData) { + // When user comes from HomeView or WatchlistView, we should only update favourite button UI based on watchlist Data. + // We update watchlist data iff when user clicked favourite button. + final ArrayList watchList = watchListDataAccessInterface.getWatchList(); + if (watchList.contains(stock.getSymbol())) { + if (shouldModifyData) { + watchListModifyDataAccessInterface.removeFromWatchList(stock.getSymbol()); + stockPresenter.presentRemoveFromWatchlist(stock); + stockPresenter.updateFavouriteButton(false); + } + else { + stockPresenter.updateFavouriteButton(true); + } } else { - watchlistDataAccess.addToWatchList(stock.getSymbol()); - stockPresenter.presentAddToWatchlist(stock); + if (shouldModifyData) { + watchListModifyDataAccessInterface.addToWatchList(stock.getSymbol()); + stockPresenter.presentAddToWatchlist(stock); + stockPresenter.updateFavouriteButton(true); + } + else { + stockPresenter.updateFavouriteButton(false); + } } } } diff --git a/src/main/java/interface_adapter/stock_view/StockOutputBoundary.java b/src/main/java/use_case/stock/StockOutputBoundary.java similarity index 75% rename from src/main/java/interface_adapter/stock_view/StockOutputBoundary.java rename to src/main/java/use_case/stock/StockOutputBoundary.java index 1da6babe0..85f82d92d 100644 --- a/src/main/java/interface_adapter/stock_view/StockOutputBoundary.java +++ b/src/main/java/use_case/stock/StockOutputBoundary.java @@ -1,4 +1,4 @@ -package interface_adapter.stock_view; +package use_case.stock; import entity.Stock; @@ -20,4 +20,10 @@ public interface StockOutputBoundary { * @param stock The stock that was removed. */ void presentRemoveFromWatchlist(Stock stock); + + /** + * Updates favourite button UI. + * @param isFavourite is stock in watchlist. + */ + void updateFavouriteButton(Boolean isFavourite); } diff --git a/src/main/java/use_case/stock/StockPresenter.java b/src/main/java/use_case/stock/StockPresenter.java deleted file mode 100644 index b48e723b4..000000000 --- a/src/main/java/use_case/stock/StockPresenter.java +++ /dev/null @@ -1,32 +0,0 @@ -package interface_adapter.stock_view; - -import entity.Stock; -import interface_adapter.ViewManagerModel; - -public class StockPresenter implements StockOutputBoundary { - - private final StockViewModel stockViewModel; - private final ViewManagerModel viewManagerModel; - - public StockPresenter(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { - this.stockViewModel = stockViewModel; - this.viewManagerModel = viewManagerModel; - } - - @Override - public void presentBuyView(Stock stock) { - stockViewModel.setStock(stock); - viewManagerModel.setState("buy view"); - viewManagerModel.firePropertyChanged(); - } - - @Override - public void presentAddToWatchlist(Stock stock) { - System.out.println("Stock " + stock.getSymbol() + " added to watchlist."); - } - - @Override - public void presentRemoveFromWatchlist(Stock stock) { - System.out.println("Stock " + stock.getSymbol() + " removed from watchlist."); - } -} diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index ae0b48666..a9678a515 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -4,6 +4,7 @@ import interface_adapter.ViewManagerModel; import interface_adapter.stock_view.StockViewModel; import interface_adapter.stock_view.StockController; +import interface_adapter.stock_view.StockViewState; import javax.swing.*; import java.awt.*; @@ -107,9 +108,9 @@ private JPanel createActionButtonsPanel() { // Favorite Button Action: Add or remove stock from watchlist favoriteButton.addActionListener(e -> { - Stock currentStock = stockViewModel.getState().getStock(); + final Stock currentStock = stockViewModel.getState().getStock(); if (currentStock != null && stockController != null) { - stockController.toggleWatchlist(currentStock); + stockController.toggleWatchlist(currentStock, true); } }); @@ -155,9 +156,17 @@ void backButtonAction() { @Override public void propertyChange(PropertyChangeEvent evt) { - Stock updatedStock = stockViewModel.getState().getStock(); - if (updatedStock != null) { - updateStockData(updatedStock); + final StockViewState stockViewState = stockViewModel.getState(); + if (evt.getPropertyName().equals("switchToStockView")) { + final Stock updatedStock = stockViewState.getStock(); + if (updatedStock != null) { + updateStockData(updatedStock); + stockController.toggleWatchlist(updatedStock, false); + } + } + else if (evt.getPropertyName().equals("updateFavouriteButton")) { + // Change favourite button UI + System.out.println("updateFavouriteButton" + " " + stockViewState.getIsFavorite()); } } } From 5c276264f12c7a3c258b93e371b546c01094912c Mon Sep 17 00:00:00 2001 From: misaka Date: Sat, 23 Nov 2024 14:01:43 -0500 Subject: [PATCH 057/113] Set correct back button when visit stock information from watchlist --- src/main/java/view/StockView.java | 15 ++++++++++++++- src/main/java/view/WatchListView.java | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index a9678a515..63d5a65e7 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -30,6 +30,9 @@ public class StockView extends AbstractViewWithBackButton implements PropertyCha private JLabel highPriceLabel; private JLabel volumeLabel; + private String previousView; + + public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; this.stockViewModel = stockViewModel; @@ -150,7 +153,12 @@ public String getViewName() { @Override void backButtonAction() { - viewManagerModel.setState("home view"); + if ("StockView".equals(viewManagerModel.getState())) { + viewManagerModel.setState("WatchListView"); + } + else { + viewManagerModel.setState("home view"); + } viewManagerModel.firePropertyChanged(); } @@ -169,4 +177,9 @@ else if (evt.getPropertyName().equals("updateFavouriteButton")) { System.out.println("updateFavouriteButton" + " " + stockViewState.getIsFavorite()); } } + + public void setPreviousView(String viewName) { + this.previousView = viewName; + } + } diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index e68a62418..8772449e3 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -148,6 +148,9 @@ private void goToStockInformation(String code) { final WatchListViewState currentState = watchListViewModel.getState(); currentState.setSymbol(code); watchlistController.search(currentState.getSymbol()); + + viewManagerModel.setState("StockView"); + viewManagerModel.firePropertyChanged(); } private void createWatchListview(ArrayList WatchList) { From b16ac92cbab6a57d761560abbb605895ea56c04d Mon Sep 17 00:00:00 2001 From: misaka Date: Sat, 23 Nov 2024 14:05:34 -0500 Subject: [PATCH 058/113] =?UTF-8?q?Use=20=E2=86=90=20as=20back=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/AbstractViewWithBackButton.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/view/AbstractViewWithBackButton.java b/src/main/java/view/AbstractViewWithBackButton.java index 555144067..bb353d0db 100644 --- a/src/main/java/view/AbstractViewWithBackButton.java +++ b/src/main/java/view/AbstractViewWithBackButton.java @@ -10,7 +10,7 @@ */ abstract class AbstractViewWithBackButton extends JPanel { - private final JButton backButton = new JButton("Back"); + private final JButton backButton = new JButton("←"); AbstractViewWithBackButton() { // Configure back button @@ -19,7 +19,7 @@ abstract class AbstractViewWithBackButton extends JPanel { backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); backButton.setFocusPainted(false); backButton.setContentAreaFilled(false); - topPanel.add(backButton, BorderLayout.WEST); + topPanel.add(backButton, BorderLayout.WEST);; backButton.addActionListener(new ActionListener() { @Override From 0ae4970d18fcf7199fe0f128cff52a1f4e5f5fc1 Mon Sep 17 00:00:00 2001 From: misaka Date: Sat, 23 Nov 2024 14:43:05 -0500 Subject: [PATCH 059/113] Add scroll functionality with for View --- src/main/java/data_access/watchlist.txt | 2 +- .../HomeViewComponents/ScrollablePanel.java | 20 ++++++++++++++ src/main/java/view/WatchListView.java | 27 +++++++++++++++---- 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 src/main/java/view/HomeViewComponents/ScrollablePanel.java diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index a7493d35e..aa2a27101 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPl,AMD,NVDA \ No newline at end of file +AAPl,NVDA,AMD,QQQ,COST,TQQ,MSTR \ No newline at end of file diff --git a/src/main/java/view/HomeViewComponents/ScrollablePanel.java b/src/main/java/view/HomeViewComponents/ScrollablePanel.java new file mode 100644 index 000000000..fab093e6e --- /dev/null +++ b/src/main/java/view/HomeViewComponents/ScrollablePanel.java @@ -0,0 +1,20 @@ +package view.HomeViewComponents; + +import javax.swing.*; +import java.awt.*; + +public class ScrollablePanel extends JPanel { + public ScrollablePanel() { + super(); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setBackground(Color.WHITE); + } + + public JScrollPane wrapInScrollPane() { + JScrollPane scrollPane = new JScrollPane(this); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.getVerticalScrollBar().setUnitIncrement(16); + return scrollPane; + } +} diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 8772449e3..673a34ee8 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -1,5 +1,7 @@ package view; +import view.HomeViewComponents.ScrollablePanel; + import data_access.DBUserDataAccessObject; import interface_adapter.ViewManagerModel; import interface_adapter.home_view.WatchlistController; @@ -20,7 +22,7 @@ public class WatchListView extends JPanel implements PropertyChangeListener { private final ViewManagerModel viewManagerModel; private final WatchListViewModel watchListViewModel; private final DBUserDataAccessObject dbUserDataAccessObject; - private final JPanel contentPanel = new JPanel(); + private final ScrollablePanel contentPanel = new ScrollablePanel(); private WatchlistController watchlistController; public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel, DBUserDataAccessObject dbUserDataAccessObject) { @@ -31,7 +33,6 @@ public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerM this.watchListViewModel = viewModel; this.watchListViewModel.addPropertyChangeListener(this); - // return button final JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); topPanel.setBackground(Color.WHITE); @@ -63,15 +64,27 @@ public void actionPerformed(ActionEvent e) { // addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00"); // addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25"); - add(contentPanel, BorderLayout.CENTER); + JScrollPane scrollPane = contentPanel.wrapInScrollPane(); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + add(scrollPane, BorderLayout.CENTER); } private void addStockItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage, String volume) { final JPanel stockPanel = new JPanel(new BorderLayout()); - stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); stockPanel.setBackground(Color.WHITE); + final boolean isFirstStock = contentPanel.getComponentCount() == 0; + if (isFirstStock) { + stockPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.LIGHT_GRAY)); + } + else { + stockPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); + } + + stockPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 80)); + stockPanel.setMinimumSize(new Dimension(0, 80)); + stockPanel.addMouseListener(new java.awt.event.MouseAdapter() { @Override public void mouseClicked(java.awt.event.MouseEvent evt) { @@ -97,26 +110,30 @@ public void mouseExited(java.awt.event.MouseEvent evt) { final JLabel stockCodeLabel = new JLabel(code); stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + final JLabel stockPriceLabel = new JLabel(price); stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + leftPanel.add(Box.createVerticalGlue()); leftPanel.add(stockCodeLabel); leftPanel.add(stockPriceLabel); + leftPanel.add(Box.createVerticalGlue()); // Right part: up and down information final JPanel rightPanel = new JPanel(); rightPanel.setOpaque(false); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); rightPanel.setBackground(Color.WHITE); - rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); final JLabel volumeLabel = new JLabel("Volume: " + volume); volumeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + rightPanel.add(Box.createVerticalGlue()); rightPanel.add(dailyChangeLabel); rightPanel.add(volumeLabel); + rightPanel.add(Box.createVerticalGlue()); // Add all to stockPanel stockPanel.add(leftPanel, BorderLayout.WEST); From b8fc830f90ca8be4356f2fcf930799901b0af6e0 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sat, 23 Nov 2024 16:36:32 -0500 Subject: [PATCH 060/113] Buy: quantity and price. --- src/main/java/app/AppBuilder.java | 23 ++++++++++------- src/main/java/app/Main.java | 2 ++ .../buy_view/BuyController.java | 4 +-- .../buy_view/BuyPresenter.java | 18 +++++++------ src/main/java/use_case/buy/BuyInputData.java | 16 +++++++----- src/main/java/use_case/buy/BuyInteractor.java | 25 ++++++++++++------- src/main/java/use_case/buy/BuyOutputData.java | 13 +++++++--- src/main/java/view/BuyView.java | 9 +++++-- 8 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 7d131dd40..0f47dfbc4 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -11,6 +11,8 @@ import data_access.FileUserDataAccessObject; import entity.*; import interface_adapter.ViewManagerModel; +import interface_adapter.buy_view.BuyController; +import interface_adapter.buy_view.BuyPresenter; import interface_adapter.buy_view.BuyViewModel; import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; @@ -31,6 +33,9 @@ import interface_adapter.stock_view.StockPresenter; import interface_adapter.stock_view.StockViewModel; import interface_adapter.watchlist_view.WatchListViewModel; +import use_case.buy.BuyInputBoundary; +import use_case.buy.BuyInteractor; +import use_case.buy.BuyOutputBoundary; import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; @@ -306,15 +311,15 @@ public AppBuilder addStockUseCase() { * Adds the Buy Use Case to the application. * @return this builder */ - //public AppBuilder addBuyUseCase() { - // final BuyOutputBoundary buyOutputBoundary = new BuyPresenter(buyViewModel, viewManagerModel, homeViewModel); - // final BuyInputBoundary buyInteractor = new BuyInteractor( - // buyUserDataAccessObject, buyOutputBoundary); - - // final BuyController buyController = new BuyController(buyInteractor); - // buyView.setBuyController(buyController); - // return this; - //} + public AppBuilder addBuyUseCase() { + final BuyOutputBoundary buyOutputBoundary = new BuyPresenter(buyViewModel, portfolioViewModel, viewManagerModel, homeViewModel); + final BuyInputBoundary buyInteractor = new BuyInteractor( + buyOutputBoundary, fileUserDataAccessObject); + + final BuyController buyController = new BuyController(buyInteractor); + buyView.setBuyController(buyController); + return this; + } /** * Creates the JFrame for the application and initially sets the SignupView to be displayed. diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index e2ff8c45c..9c94688b4 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -27,6 +27,8 @@ public static void main(String[] args) { .addChangePasswordUseCase() .addStockUseCase() .addPortfolioView() + .addBuyView() + .addBuyUseCase() .build(); application.pack(); diff --git a/src/main/java/interface_adapter/buy_view/BuyController.java b/src/main/java/interface_adapter/buy_view/BuyController.java index 93b4c8c20..608cbf93c 100644 --- a/src/main/java/interface_adapter/buy_view/BuyController.java +++ b/src/main/java/interface_adapter/buy_view/BuyController.java @@ -22,9 +22,9 @@ public BuyController(BuyInputBoundary buyUseCaseInteractor) { * @param quantity the quantity of the stock */ - public void execute(String price, String quantity) { + public void execute(String symbol, double price, int quantity) { final BuyInputData buyInputData = new BuyInputData( - price, quantity); + symbol, price, quantity); buyUseCaseInteractor.execute(buyInputData); } diff --git a/src/main/java/interface_adapter/buy_view/BuyPresenter.java b/src/main/java/interface_adapter/buy_view/BuyPresenter.java index f9aa4e667..5cbd7f90c 100644 --- a/src/main/java/interface_adapter/buy_view/BuyPresenter.java +++ b/src/main/java/interface_adapter/buy_view/BuyPresenter.java @@ -1,9 +1,13 @@ package interface_adapter.buy_view; +import entity.CommonSimulatedHoldingFactory; +import entity.SimulatedHolding; import interface_adapter.ViewManagerModel; import interface_adapter.change_password.IsLoggedIn; import interface_adapter.change_password.LoggedInState; import interface_adapter.home_view.HomeViewModel; +import interface_adapter.portfolio.PortfolioState; +import interface_adapter.portfolio.PortfolioViewModel; import use_case.buy.BuyOutputBoundary; import use_case.buy.BuyOutputData; import use_case.login.LoginOutputBoundary; @@ -12,23 +16,21 @@ public class BuyPresenter implements BuyOutputBoundary { - private BuyViewModel buyViewModel; + private final BuyViewModel buyViewModel; + private final PortfolioViewModel portfolioViewModel; private final ViewManagerModel viewManagerModel; private final HomeViewModel homeViewModel; - public BuyPresenter(BuyViewModel buyViewModel, ViewManagerModel viewManagerModel, HomeViewModel homeViewModel) { + public BuyPresenter(BuyViewModel buyViewModel, PortfolioViewModel portfolioViewModel, ViewManagerModel viewManagerModel, HomeViewModel homeViewModel) { this.buyViewModel = buyViewModel; + this.portfolioViewModel = portfolioViewModel; this.viewManagerModel = viewManagerModel; this.homeViewModel = homeViewModel; } @Override public void prepareSuccessView(BuyOutputData response) { - // On success, switch to the home view. - // Store the stock information later. - - this.viewManagerModel.setState(homeViewModel.getViewName()); - this.viewManagerModel.firePropertyChanged(); + portfolioViewModel.firePropertyChanged("getPortfolioList"); } @Override @@ -36,4 +38,4 @@ public void switchToHomeView() { viewManagerModel.setState(homeViewModel.getViewName()); viewManagerModel.firePropertyChanged(); } -} +} \ No newline at end of file diff --git a/src/main/java/use_case/buy/BuyInputData.java b/src/main/java/use_case/buy/BuyInputData.java index db5801925..109d73a7d 100644 --- a/src/main/java/use_case/buy/BuyInputData.java +++ b/src/main/java/use_case/buy/BuyInputData.java @@ -5,20 +5,24 @@ */ public class BuyInputData { - private final String price; - private final String quantity; + private final String symbol; + private final double price; + private final int quantity; - public BuyInputData(String price, String quantity) { + public BuyInputData(String symbol, double price, int quantity) { + this.symbol = symbol; this.price = price; this.quantity = quantity; } - String getPrice() { + public String getSymbol() { return symbol; } + + public double getPrice() { return price; } - String getQuantity() { + public int getQuantity() { return quantity; } -} +} \ No newline at end of file diff --git a/src/main/java/use_case/buy/BuyInteractor.java b/src/main/java/use_case/buy/BuyInteractor.java index 2cfcee3cc..07d485981 100644 --- a/src/main/java/use_case/buy/BuyInteractor.java +++ b/src/main/java/use_case/buy/BuyInteractor.java @@ -1,30 +1,37 @@ package use_case.buy; +import entity.CommonSimulatedHoldingFactory; +import entity.SimulatedHolding; import entity.Stock; +import use_case.portfolio.PortfolioDataAccessInterface; /** * The Login Interactor. */ public class BuyInteractor implements BuyInputBoundary { - private final BuyUserDataAccessInterface buyUserDataAccessObject; private final BuyOutputBoundary buyPresenter; + private final PortfolioDataAccessInterface portfolioDataAccessObject; - public BuyInteractor(BuyUserDataAccessInterface userDataAccessInterface, BuyOutputBoundary buyOutputBoundary) { - this.buyUserDataAccessObject = userDataAccessInterface; + public BuyInteractor(BuyOutputBoundary buyOutputBoundary, PortfolioDataAccessInterface portfolioDataAccessObject) { this.buyPresenter = buyOutputBoundary; + this.portfolioDataAccessObject = portfolioDataAccessObject; } @Override public void execute(BuyInputData buyInputData) { - final String price = buyInputData.getPrice(); - final String quantity = buyInputData.getQuantity(); + final String symbol = buyInputData.getSymbol(); + final double price = buyInputData.getPrice(); + final int quantity = buyInputData.getQuantity(); - // More code is needed to save the purchased stock information - // final Stock stock = buyUserDataAccessObject.get(buyInputData.getPrice()); - // buyUserDataAccessObject.setCurrentPrice(stock.getPrice()); + final CommonSimulatedHoldingFactory commonSimulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final SimulatedHolding simulatedHolding = commonSimulatedHoldingFactory.create(symbol, price, quantity); - final BuyOutputData buyOutputData = new BuyOutputData(); + // Add simulated holding into database + portfolioDataAccessObject.addToPortfolioList(simulatedHolding); + + // Pass data to Portfolio View + final BuyOutputData buyOutputData = new BuyOutputData(simulatedHolding); buyPresenter.prepareSuccessView(buyOutputData); } diff --git a/src/main/java/use_case/buy/BuyOutputData.java b/src/main/java/use_case/buy/BuyOutputData.java index ac4a10506..8369962ce 100644 --- a/src/main/java/use_case/buy/BuyOutputData.java +++ b/src/main/java/use_case/buy/BuyOutputData.java @@ -1,11 +1,18 @@ package use_case.buy; +import entity.SimulatedHolding; + /** * Output Data for the Buy Use Case. */ public class BuyOutputData { - // private final String quantity; - // private final String stockName; + private final SimulatedHolding simulatedHolding; + + public BuyOutputData(SimulatedHolding simulatedHolding) { + this.simulatedHolding = simulatedHolding; + } - public BuyOutputData() {} + public SimulatedHolding getSimulatedHolding() { + return simulatedHolding; + } } diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index aa725bfef..b71c7b11f 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -62,7 +62,8 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { buy.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt) { - buyController.switchToHomeView(); + final BuyState buyState = buyViewModel.getState(); + buyController.execute(buyState.getSymbol(), Double.parseDouble(buyState.getPrice()), Integer.parseInt(quantityInputField.getText())); } } ); @@ -81,6 +82,8 @@ private void documentListenerHelper() { final BuyState currentState = buyViewModel.getState(); currentState.setQuantity(quantityInputField.getText()); buyViewModel.setState(currentState); + + System.out.println("Quantity:" + currentState.getQunatity()); } @Override @@ -107,6 +110,8 @@ private void documentListenerHelper() { final BuyState currentState = buyViewModel.getState(); currentState.setPrice(new String(priceInputField.getText())); buyViewModel.setState(currentState); + + System.out.println("Price:" + currentState.getPrice()); } @Override @@ -157,4 +162,4 @@ public String getViewName() { public void setBuyController(BuyController buyController) { this.buyController = buyController; } -} +} \ No newline at end of file From d0b2056c8e5ab7fe058fb4d132113d408d017ee9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Nov 2024 16:58:44 -0500 Subject: [PATCH 061/113] fix home view bugs --- .../java/data_access/FileUserDataAccessObject.java | 8 ++++---- src/main/java/data_access/portfolio.txt | 2 ++ src/main/java/data_access/watchlist.txt | 2 +- src/main/java/use_case/home_view/HomeInteractor.java | 11 +++++++++-- src/main/java/use_case/home_view/SearchInputData.java | 2 +- src/main/java/view/HomeView.java | 5 ++--- 6 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 src/main/java/data_access/portfolio.txt diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index f66a19807..89021637f 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -58,9 +58,9 @@ public ArrayList getWatchList() { } else { // If the watchlist file doesn't exist, we create a new one - this.watchList.add("AAPl"); + this.watchList.add("AAPL"); + this.watchList.add("COST"); this.watchList.add("NVDA"); - this.watchList.add("AMD"); this.saveWatchList(); // Get watchlist data again @@ -99,7 +99,7 @@ public void removeFromWatchList(String symbol) { // Portfolio list related APIs public ArrayList getPortfolioList() { // Check if watchlist.txt file exists - final Boolean isFileExisted = new File(mainFilePath + watchListFilePath).isFile(); + final Boolean isFileExisted = new File(mainFilePath + portfolioFilePath).isFile(); if (isFileExisted) { // Using FileReader and BufferedReader to read the file @@ -137,7 +137,7 @@ public void savePortfolioList() { final BufferedWriter writer; try { // Override original file - writer = new BufferedWriter(new FileWriter(mainFilePath + watchListFilePath, false)); + writer = new BufferedWriter(new FileWriter(mainFilePath + portfolioFilePath, false)); for (SimulatedHolding simulatedHolding : portfolioList) { writer.write(simulatedHolding.getSymbol() + ","); writer.write(simulatedHolding.getPurchasePrice() + ","); diff --git a/src/main/java/data_access/portfolio.txt b/src/main/java/data_access/portfolio.txt new file mode 100644 index 000000000..85f337c63 --- /dev/null +++ b/src/main/java/data_access/portfolio.txt @@ -0,0 +1,2 @@ +NVDA,123.0,123, +AMD,322.1,1, diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index a7493d35e..f0f3aae7a 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPl,AMD,NVDA \ No newline at end of file +AAPL,BBB,NVDA \ No newline at end of file diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 9cbb64162..90855391e 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -37,8 +37,15 @@ public void search(SearchInputData searchInputData) { // err.printStackTrace(); // homePresenter.prepareFailView("Failed to fetch stock data"); // } + String stockSymbol = searchInputData.getStockSymbol(); + if (stockSymbol == null || stockSymbol.isEmpty()) { + stockSymbol = "NVDA"; + } + else { + stockSymbol = stockSymbol.toUpperCase(); + } final StockFactory stockFactory = new CommonStockFactory(); - final Stock stock = stockFactory.create(searchInputData.getStockSymbol(), 128.2, 322.1, 100002322, 500.1, 100.23); + final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); final SearchOutputData searchOutputData = new SearchOutputData(stock, false); homePresenter.prepareSuccessView(searchOutputData); @@ -74,7 +81,7 @@ public void getWatchListData() { int i = 0; for (String symbol : watchListData) { - final Stock stock = stockFactory.create(symbol, i, 130.3, 100002322, 500.1, 100.23); + final Stock stock = stockFactory.create(symbol, i, i, 100002322, 500.1, 100.23); watchList.add(stock); i += 1; } diff --git a/src/main/java/use_case/home_view/SearchInputData.java b/src/main/java/use_case/home_view/SearchInputData.java index 10a10b0cc..072feb612 100644 --- a/src/main/java/use_case/home_view/SearchInputData.java +++ b/src/main/java/use_case/home_view/SearchInputData.java @@ -8,7 +8,7 @@ public class SearchInputData { private final String stockSymbol; public SearchInputData(String stockSymbol) { - this.stockSymbol = stockSymbol.toUpperCase(); + this.stockSymbol = stockSymbol; } public String getStockSymbol() { diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index b5f494099..8e7001736 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -85,8 +85,6 @@ public HomeView(HomeViewModel homeViewModel) { public void actionPerformed(ActionEvent evt) { if (evt.getSource().equals(searchButton)) { final HomeState currentState = homeViewModel.getState(); - currentState.setSymbol("NVDA"); - homeController.search(currentState.getSymbol()); } } @@ -192,7 +190,8 @@ public void actionPerformed(ActionEvent evt) { public void propertyChange(PropertyChangeEvent evt) { final HomeState state = (HomeState) evt.getNewValue(); if (evt.getPropertyName().equals("getWatchList")) { - final ArrayList watchList = state.getWatchList(); + // We only display first three stocks + final ArrayList watchList = new ArrayList(state.getWatchList().subList(0, 3)); watchListContentPanel.removeAll(); updateWatchListComponents(watchList); watchListContentPanel.revalidate(); From 7a3d9b8f0b18883b870258204e39505048ba7f20 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 23 Nov 2024 18:30:56 -0500 Subject: [PATCH 062/113] stock star done --- src/main/java/data_access/watchlist.txt | 2 +- src/main/java/view/StockView.java | 35 ++++++++++++++++--------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index 22bdc4640..a1f214c1f 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPl,AMD,BBB,COST,MSTR,NVDA,QQQ,TQQ \ No newline at end of file +AAPL,AAPl,BBB,COST,MSTR,QQQ,TQQ \ No newline at end of file diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 63d5a65e7..f3fae7ee7 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -29,10 +29,9 @@ public class StockView extends AbstractViewWithBackButton implements PropertyCha private JLabel lowPriceLabel; private JLabel highPriceLabel; private JLabel volumeLabel; - + private JButton favoriteButton; private String previousView; - public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; this.stockViewModel = stockViewModel; @@ -105,15 +104,20 @@ private JPanel createActionButtonsPanel() { } }); - JButton favoriteButton = new JButton("★"); + // Star button for favorite + favoriteButton = new JButton("☆"); favoriteButton.setFont(new Font("SansSerif", Font.BOLD, 18)); favoriteButton.setFocusPainted(false); // Favorite Button Action: Add or remove stock from watchlist favoriteButton.addActionListener(e -> { - final Stock currentStock = stockViewModel.getState().getStock(); + Stock currentStock = stockViewModel.getState().getStock(); if (currentStock != null && stockController != null) { - stockController.toggleWatchlist(currentStock, true); + boolean isFavorite = "★".equals(favoriteButton.getText()); + stockController.toggleWatchlist(currentStock, !isFavorite); + + // Toggle button text between filled and empty star + favoriteButton.setText(isFavorite ? "☆" : "★"); } }); @@ -138,6 +142,19 @@ public void updateStockData(Stock stock) { String volumeText = (volume >= 1_000_000) ? (volume / 1_000_000) + " M" : (volume >= 1_000) ? (volume / 1_000) + " K" : String.valueOf(volume); volumeLabel.setText("Volume: " + volumeText); + + // 星星按钮的可见性 + updateFavoriteButtonVisibility(stock.getSymbol()); + } + + private void updateFavoriteButtonVisibility(String stockSymbol) { + // 隐藏 AAPL, COST, NVDA 的星星按钮 + if ("AAPL".equals(stockSymbol) || "COST".equals(stockSymbol) || "NVDA".equals(stockSymbol)) { + favoriteButton.setVisible(false); + } + else { + favoriteButton.setVisible(true); + } } private JLabel createLabel(String text, int fontSize, int fontStyle) { @@ -165,21 +182,15 @@ void backButtonAction() { @Override public void propertyChange(PropertyChangeEvent evt) { final StockViewState stockViewState = stockViewModel.getState(); - if (evt.getPropertyName().equals("switchToStockView")) { + if ("switchToStockView".equals(evt.getPropertyName())) { final Stock updatedStock = stockViewState.getStock(); if (updatedStock != null) { updateStockData(updatedStock); - stockController.toggleWatchlist(updatedStock, false); } } - else if (evt.getPropertyName().equals("updateFavouriteButton")) { - // Change favourite button UI - System.out.println("updateFavouriteButton" + " " + stockViewState.getIsFavorite()); - } } public void setPreviousView(String viewName) { this.previousView = viewName; } - } From cb7decc19577bafc6ad03c9eebf97f68afff1efe Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sat, 23 Nov 2024 19:11:25 -0500 Subject: [PATCH 063/113] Save log in information. --- .../data_access/FileUserDataAccessObject.java | 51 ++++++++++++++++++- .../login/LoginPresenter.java | 8 +++ .../java/use_case/login/LoginInteractor.java | 8 +++ src/main/java/view/HomeView.java | 46 ++++++++++------- 4 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index f66a19807..c33661761 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -9,6 +9,7 @@ import java.util.*; import entity.*; +import use_case.buy.BuyUserDataAccessInterface; import use_case.change_password.ChangePasswordUserDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; import use_case.portfolio.PortfolioDataAccessInterface; @@ -19,7 +20,7 @@ /** * DAO for user data implemented using a File to persist the data. */ -public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface, PortfolioDataAccessInterface { +public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface, PortfolioDataAccessInterface, BuyUserDataAccessInterface { private final ArrayList watchList = new ArrayList<>(); private final ArrayList portfolioList = new ArrayList<>(); @@ -27,8 +28,14 @@ public class FileUserDataAccessObject implements WatchListDataAccessInterface, W private final String mainFilePath = "src/main/java/data_access/"; private final String watchListFilePath = "watchlist.txt"; private final String portfolioFilePath = "portfolio.txt"; + private final String userIsLoggedInFilePath = "userIsLoggedIn.txt"; - private final SimulatedHoldingFactory simulatedHoldingFactory; + private SimulatedHoldingFactory simulatedHoldingFactory; + + private boolean userIsLoggedIn; + + public FileUserDataAccessObject() { + } public FileUserDataAccessObject(SimulatedHoldingFactory simulatedHoldingFactory) { this.simulatedHoldingFactory = simulatedHoldingFactory; @@ -173,4 +180,44 @@ public void removeFromPortfolioList(SimulatedHolding simulatedHolding) { portfolioList.remove(simulatedHolding); savePortfolioList(); } + + // Method to get the login status of the user + public boolean isUserLoggedIn() { + // Check whether the file exists + final File file = new File(mainFilePath + userIsLoggedInFilePath); + if (file.isFile()) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + final String line = reader.readLine(); + if (line != null) { + // Converts the read string to a Boolean value + userIsLoggedIn = Boolean.parseBoolean(line); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } else { + // If the file does not exist, the default setting is not logged in + userIsLoggedIn = false; + saveUserLoginStatus(); + } + return userIsLoggedIn; + } + + // Method to save the login status of the user + public void saveUserLoginStatus() { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(mainFilePath + userIsLoggedInFilePath, false))) { + // Converts a Boolean value to a string and writes it to a file + writer.write(Boolean.toString(userIsLoggedIn)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + // Change the login status of the user + public void setUserLoggedIn(boolean loggedIn) { + this.userIsLoggedIn = loggedIn; + saveUserLoginStatus(); + } } + + diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java index 2f67ce57d..c11b1320e 100644 --- a/src/main/java/interface_adapter/login/LoginPresenter.java +++ b/src/main/java/interface_adapter/login/LoginPresenter.java @@ -1,5 +1,6 @@ package interface_adapter.login; +import data_access.FileUserDataAccessObject; import interface_adapter.ViewManagerModel; import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; @@ -8,6 +9,7 @@ import interface_adapter.signup.SignupViewModel; import use_case.login.LoginOutputBoundary; import use_case.login.LoginOutputData; +import use_case.login.LoginUserDataAccessInterface; /** * The Presenter for the Login Use Case. @@ -19,6 +21,7 @@ public class LoginPresenter implements LoginOutputBoundary { private final ViewManagerModel viewManagerModel; private final SignupViewModel signUpViewModel; private final HomeViewModel homeViewModel; + private final FileUserDataAccessObject dataAccessObject = new FileUserDataAccessObject(); public LoginPresenter(ViewManagerModel viewManagerModel, LoggedInViewModel loggedInViewModel, @@ -36,6 +39,11 @@ public LoginPresenter(ViewManagerModel viewManagerModel, public void prepareSuccessView(LoginOutputData response) { // On success, switch to the logged in view. + dataAccessObject.setUserLoggedIn(true); + dataAccessObject.saveUserLoginStatus(); + + homeViewModel.firePropertyChanged(); + final LoggedInState loggedInState = loggedInViewModel.getState(); loggedInState.setUsername(response.getUsername()); diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java index 977740587..8e6575742 100644 --- a/src/main/java/use_case/login/LoginInteractor.java +++ b/src/main/java/use_case/login/LoginInteractor.java @@ -1,12 +1,17 @@ package use_case.login; +import data_access.FileUserDataAccessObject; import entity.User; import interface_adapter.change_password.IsLoggedIn; +import view.HomeView; /** * The Login Interactor. */ public class LoginInteractor implements LoginInputBoundary { + // Add DAO + private final FileUserDataAccessObject dataAccessObject; + private final LoginUserDataAccessInterface userDataAccessObject; private final LoginOutputBoundary loginPresenter; @@ -14,6 +19,7 @@ public LoginInteractor(LoginUserDataAccessInterface userDataAccessInterface, LoginOutputBoundary loginOutputBoundary) { this.userDataAccessObject = userDataAccessInterface; this.loginPresenter = loginOutputBoundary; + this.dataAccessObject = new FileUserDataAccessObject(); } @Override @@ -29,6 +35,8 @@ public void execute(LoginInputData loginInputData) { loginPresenter.prepareFailView("Incorrect password for \"" + username + "\"."); } else { + dataAccessObject.setUserLoggedIn(true); + dataAccessObject.saveUserLoginStatus(); final User user = userDataAccessObject.get(loginInputData.getUsername()); diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index b5f494099..84ee68c8a 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -1,5 +1,7 @@ package view; +import data_access.FileUserDataAccessObject; +import entity.SimulatedHoldingFactory; import entity.Stock; import interface_adapter.change_password.IsLoggedIn; import interface_adapter.home_view.HomeController; @@ -52,7 +54,13 @@ public class HomeView extends JPanel implements PropertyChangeListener { // Login/Logout component private JButton loginButton = new JButton(); + // Add DAO + private final FileUserDataAccessObject dataAccessObject; + public HomeView(HomeViewModel homeViewModel) { + + this.dataAccessObject = new FileUserDataAccessObject(); + // Set up homeViewModel and listen any upcoming events this.homeViewModel = homeViewModel; this.homeViewModel.addPropertyChangeListener(this); @@ -163,16 +171,17 @@ public void actionPerformed(ActionEvent evt) { final JPanel loginPanel = new JPanel(); loginButton.setPreferredSize(new Dimension(150, 32)); loginPanel.add(loginButton); - loginButton.setText("Log Out"); + changeLoginButtonText(); loginButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { -// if (IsLoggedIn.isLoggedIn()) { -// showLogoutDialog(); -// } -// else { -// homeController.switchToLoginView(); -// } - homeController.switchToLoginView(); + if (dataAccessObject.isUserLoggedIn()) { + // If you are logged in, the logout dialog box is displayed + showLogoutDialog(); + } else { + // If you are not logged in, switch to the login view + homeController.switchToLoginView(); + } + } }); @@ -271,21 +280,22 @@ public void setHomeController(HomeController homeController) { homeController.getWatchList(); } - public void changeLoginButtonText() { - if (IsLoggedIn.isLoggedIn()) { - loginButton.setText("Log Out"); - } - else { - loginButton.setText("Log In"); - } - } - public void showLogoutDialog() { final int response = JOptionPane.showConfirmDialog(this, "Do you want to logout?", "Confirm", JOptionPane.YES_NO_OPTION); if (response == JOptionPane.YES_OPTION) { - IsLoggedIn.setLoggedIn(false); + dataAccessObject.setUserLoggedIn(false); + dataAccessObject.saveUserLoginStatus(); changeLoginButtonText(); } } + + public void changeLoginButtonText() { + if (dataAccessObject.isUserLoggedIn()) { + loginButton.setText("Log Out"); + } else { + loginButton.setText("Log In"); + } + homeViewModel.firePropertyChanged(); + } } From 46ae146829ed99a3ae84515028c207307697e14f Mon Sep 17 00:00:00 2001 From: Ryan Jin Date: Sun, 24 Nov 2024 11:43:43 -0800 Subject: [PATCH 064/113] check in so far --- .../PortfolioController.java | 16 ++++++ .../portfolio/PortfolioPresenter.java | 25 +++++++++ .../portfolio/PortfolioState.java | 14 +++++ .../portfolio/PortfolioInputBoundary.java | 8 +++ .../portfolio/PortfolioInteractor.java | 51 +++++++++++++++++++ .../portfolio/PortfolioOutputBoundary.java | 15 ++++++ src/main/java/view/PortfolioView.java | 35 +++++++++++-- 7 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 src/main/java/interface_adapter/PortfolioController.java create mode 100644 src/main/java/interface_adapter/portfolio/PortfolioPresenter.java create mode 100644 src/main/java/use_case/portfolio/PortfolioInputBoundary.java create mode 100644 src/main/java/use_case/portfolio/PortfolioInteractor.java create mode 100644 src/main/java/use_case/portfolio/PortfolioOutputBoundary.java diff --git a/src/main/java/interface_adapter/PortfolioController.java b/src/main/java/interface_adapter/PortfolioController.java new file mode 100644 index 000000000..07028e2b4 --- /dev/null +++ b/src/main/java/interface_adapter/PortfolioController.java @@ -0,0 +1,16 @@ +package interface_adapter.portfolio; + +import use_case.portfolio.PortfolioInputBoundary; + +public class PortfolioController { + + private final PortfolioInputBoundary portfolioInteractor; + + public PortfolioController(PortfolioInputBoundary portfolioInteractor) { + this.portfolioInteractor = portfolioInteractor; + } + + public void getPortfolioList() { + portfolioInteractor.getPortfolioListData(); + } +} diff --git a/src/main/java/interface_adapter/portfolio/PortfolioPresenter.java b/src/main/java/interface_adapter/portfolio/PortfolioPresenter.java new file mode 100644 index 000000000..4c2267aad --- /dev/null +++ b/src/main/java/interface_adapter/portfolio/PortfolioPresenter.java @@ -0,0 +1,25 @@ +package interface_adapter.portfolio; + +import entity.SimulatedHolding; +import entity.Stock; +import use_case.portfolio.PortfolioOutputBoundary; + +import java.util.ArrayList; + +public class PortfolioPresenter implements PortfolioOutputBoundary { + + private final PortfolioViewModel portfolioViewModel; + + public PortfolioPresenter(final PortfolioViewModel portfolioViewModel) { + this.portfolioViewModel = portfolioViewModel; + } + + @Override + public void presentPortfolioListData(ArrayList portfolioList, ArrayList stockList) { + final PortfolioState portfolioState = portfolioViewModel.getState(); + portfolioState.setSimulatedHoldings(portfolioList); + portfolioState.setStocks(stockList); + portfolioViewModel.setState(portfolioState); + portfolioViewModel.firePropertyChanged("getPortfolioList"); + } +} diff --git a/src/main/java/interface_adapter/portfolio/PortfolioState.java b/src/main/java/interface_adapter/portfolio/PortfolioState.java index db5cc23a5..1d764522f 100644 --- a/src/main/java/interface_adapter/portfolio/PortfolioState.java +++ b/src/main/java/interface_adapter/portfolio/PortfolioState.java @@ -1,6 +1,7 @@ package interface_adapter.portfolio; import entity.SimulatedHolding; +import entity.Stock; import java.util.ArrayList; @@ -8,13 +9,26 @@ * The state for the Signup View Model. */ public class PortfolioState { + private ArrayList stocks; private ArrayList simulatedHoldings; + public ArrayList getStocks() { + return stocks; + } + public ArrayList getSimulatedHoldings() { return simulatedHoldings; } + public void setStocks(ArrayList stocks) { + this.stocks = stocks; + } + public void setSimulatedHoldings(ArrayList simulatedHoldings) { this.simulatedHoldings = simulatedHoldings; } + + public void addToPortfolioList(SimulatedHolding simulatedHolding) { + simulatedHoldings.add(simulatedHolding); + } } diff --git a/src/main/java/use_case/portfolio/PortfolioInputBoundary.java b/src/main/java/use_case/portfolio/PortfolioInputBoundary.java new file mode 100644 index 000000000..9a8f8ea3e --- /dev/null +++ b/src/main/java/use_case/portfolio/PortfolioInputBoundary.java @@ -0,0 +1,8 @@ +package use_case.portfolio; + +public interface PortfolioInputBoundary { + /** + * Get portfolio list data. + */ + void getPortfolioListData(); +} diff --git a/src/main/java/use_case/portfolio/PortfolioInteractor.java b/src/main/java/use_case/portfolio/PortfolioInteractor.java new file mode 100644 index 000000000..3ae93281b --- /dev/null +++ b/src/main/java/use_case/portfolio/PortfolioInteractor.java @@ -0,0 +1,51 @@ +package use_case.portfolio; + +import entity.CommonStockFactory; +import entity.SimulatedHolding; +import entity.Stock; +import use_case.home_view.HomeDataAccessInterface; + +import java.util.ArrayList; + +public class PortfolioInteractor implements PortfolioInputBoundary{ + private final PortfolioDataAccessInterface portfolioDataAccessObject; + private final HomeDataAccessInterface homeDataAccessObject; + private final PortfolioOutputBoundary portfolioPresenter; + + public PortfolioInteractor(PortfolioDataAccessInterface portfolioDataAccessObject, + HomeDataAccessInterface homeDataAccessObject, + PortfolioOutputBoundary portfolioPresenter) { + this.portfolioDataAccessObject = portfolioDataAccessObject; + this.homeDataAccessObject = homeDataAccessObject; + this.portfolioPresenter = portfolioPresenter; + } + + @Override + public void getPortfolioListData() { + final ArrayList simulatedHoldings = portfolioDataAccessObject.getPortfolioList(); + final ArrayList stockList = new ArrayList<>(); + +// // Get data from API +// for (SimulatedHolding simulatedHolding : simulatedHoldings) { +// final Stock stock = homeDataAccessObject.getStock(simulatedHolding.getSymbol()); +// stockList.add(stock); +// } + + // Using fake data + int i = 100; + final CommonStockFactory stockFactory = new CommonStockFactory(); + for (SimulatedHolding simulatedHolding : simulatedHoldings) { + final Stock stock = stockFactory.create(simulatedHolding.getSymbol(), + 0, + i + 50, + 10000000.2, + 0, + 0); + stockList.add(stock); + + i += 100; + } + + portfolioPresenter.presentPortfolioListData(simulatedHoldings, stockList); + } +} diff --git a/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java b/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java new file mode 100644 index 000000000..879182309 --- /dev/null +++ b/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java @@ -0,0 +1,15 @@ +package use_case.portfolio; + +import java.util.ArrayList; + +import entity.SimulatedHolding; +import entity.Stock; + +public interface PortfolioOutputBoundary { + /** + * Present portfolio list data for Portfolio View. + * @param portfolioList portfolio list data + * @param stockList portfolio list data + */ + void presentPortfolioListData(ArrayList portfolioList, ArrayList stockList); +} diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index aa71da696..cc2649bc7 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -1,22 +1,33 @@ package view; +import entity.SimulatedHolding; +import entity.Stock; import interface_adapter.ViewManagerModel; +import interface_adapter.portfolio.PortfolioController; +import interface_adapter.portfolio.PortfolioState; import interface_adapter.portfolio.PortfolioViewModel; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; import javax.swing.border.Border; import javax.swing.BorderFactory; -public class PortfolioView extends JPanel { +public class PortfolioView extends JPanel implements PropertyChangeListener { + private final PortfolioViewModel portfolioViewModel; private final ViewManagerModel viewManagerModel; + private PortfolioController portfolioController; - public PortfolioView(PortfolioViewModel viewModel, ViewManagerModel viewManagerModel) { + public PortfolioView(PortfolioViewModel portfolioViewModel, ViewManagerModel viewManagerModel) { + this.portfolioViewModel = portfolioViewModel; + this.portfolioViewModel.addPropertyChangeListener(this); this.viewManagerModel = viewManagerModel; setLayout(new BorderLayout()); setBackground(Color.WHITE); @@ -199,5 +210,23 @@ private void addStockItem(JPanel contentPanel, String code, String price, String public String getViewName() { return "PortfolioView"; } -} + public void setController(PortfolioController controller) { + this.portfolioController = controller; + + // Load portfolio list data + controller.getPortfolioList(); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + // Listen getPortfolioList event + if (evt.getPropertyName().equals("getPortfolioList")) { + final PortfolioState portfolioState = portfolioViewModel.getState(); + final ArrayList portfolioList = portfolioState.getSimulatedHoldings(); + final ArrayList stockList = portfolioState.getStocks(); + System.out.println("Portfolio list size: " + portfolioList.size()); + // Update UI components based on data + } + } +} \ No newline at end of file From 795d4e531e6178be28b7ea1198abef024509c266 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 24 Nov 2024 16:13:03 -0500 Subject: [PATCH 065/113] You cannot return to the home page without continuing to log in --- src/main/java/view/HomeView.java | 2 ++ src/main/java/view/LoginView.java | 19 +------------------ 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 84ee68c8a..3458afc76 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -180,6 +180,8 @@ public void actionPerformed(ActionEvent evt) { } else { // If you are not logged in, switch to the login view homeController.switchToLoginView(); + dataAccessObject.setUserLoggedIn(true); + changeLoginButtonText(); } } diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index 216aaab4a..de6e25d02 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -6,12 +6,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JTextField; +import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; @@ -37,7 +32,6 @@ public class LoginView extends JPanel implements ActionListener, PropertyChangeL private final JButton signUp; private final JButton logIn; - private final JButton cancel; private final ViewManagerModel viewManagerModel; private LoginController loginController; @@ -61,9 +55,6 @@ public LoginView(LoginViewModel loginViewModel, ViewManagerModel viewManagerMode logIn = new JButton("Log In"); buttons.add(logIn); - cancel = new JButton("Cancel"); - buttons.add(cancel); - signUp.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt) { @@ -87,14 +78,6 @@ public void actionPerformed(ActionEvent evt) { } ); - cancel.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - viewManagerModel.setState("home view"); - viewManagerModel.firePropertyChanged(); - } - }); - usernameInputField.getDocument().addDocumentListener(new DocumentListener() { private void documentListenerHelper() { From 692f47447c7033c11586d673459e9d09b46d9fa7 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 24 Nov 2024 16:15:17 -0500 Subject: [PATCH 066/113] You cannot return to the home page without continuing to log in --- src/main/java/view/LoginView.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index de6e25d02..ea98143b7 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -6,6 +6,12 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; From 755535bc075200cde612f4e6e6e7ba7963ae393b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Nov 2024 16:21:38 -0500 Subject: [PATCH 067/113] fix favourite button UI bug --- src/main/java/data_access/userIsLoggedIn.txt | 1 + src/main/java/data_access/watchlist.txt | 2 +- .../interface_adapter/stock_view/StockPresenter.java | 2 +- src/main/java/use_case/home_view/HomeInteractor.java | 1 + src/main/java/view/StockView.java | 12 ++++++++---- 5 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 src/main/java/data_access/userIsLoggedIn.txt diff --git a/src/main/java/data_access/userIsLoggedIn.txt b/src/main/java/data_access/userIsLoggedIn.txt new file mode 100644 index 000000000..02e4a84d6 --- /dev/null +++ b/src/main/java/data_access/userIsLoggedIn.txt @@ -0,0 +1 @@ +false \ No newline at end of file diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index a1f214c1f..2e2aea805 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPL,AAPl,BBB,COST,MSTR,QQQ,TQQ \ No newline at end of file +AAPL,COST,MSTR,QQQ,TQQ \ No newline at end of file diff --git a/src/main/java/interface_adapter/stock_view/StockPresenter.java b/src/main/java/interface_adapter/stock_view/StockPresenter.java index 57c666695..9c86e70f4 100644 --- a/src/main/java/interface_adapter/stock_view/StockPresenter.java +++ b/src/main/java/interface_adapter/stock_view/StockPresenter.java @@ -72,7 +72,7 @@ public void presentRemoveFromWatchlist(Stock stock) { // Pass new watchlist data to watchListView watchListViewState.remove(stock.getSymbol()); - watchListViewModel.firePropertyChanged("getWatchList"); + watchListViewModel.firePropertyChanged("watchList"); } @Override diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 90855391e..6a7ea712a 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -37,6 +37,7 @@ public void search(SearchInputData searchInputData) { // err.printStackTrace(); // homePresenter.prepareFailView("Failed to fetch stock data"); // } + // Default search symbol is NVDA String stockSymbol = searchInputData.getStockSymbol(); if (stockSymbol == null || stockSymbol.isEmpty()) { stockSymbol = "NVDA"; diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index f3fae7ee7..899313922 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -114,10 +114,7 @@ private JPanel createActionButtonsPanel() { Stock currentStock = stockViewModel.getState().getStock(); if (currentStock != null && stockController != null) { boolean isFavorite = "★".equals(favoriteButton.getText()); - stockController.toggleWatchlist(currentStock, !isFavorite); - - // Toggle button text between filled and empty star - favoriteButton.setText(isFavorite ? "☆" : "★"); + stockController.toggleWatchlist(currentStock, true); } }); @@ -187,6 +184,13 @@ public void propertyChange(PropertyChangeEvent evt) { if (updatedStock != null) { updateStockData(updatedStock); } + + // Update favourite button UI only + stockController.toggleWatchlist(updatedStock, false); + } + else if ("updateFavouriteButton".equals(evt.getPropertyName())) { + // Toggle button text between filled and empty star + favoriteButton.setText(stockViewState.getIsFavorite() ? "★" : "☆"); } } From 7ddc1945cd0142d7c7628a7353627a5c1c6dd00b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Nov 2024 16:39:04 -0500 Subject: [PATCH 068/113] hide stock button in home page --- src/main/java/data_access/userIsLoggedIn.txt | 2 +- src/main/java/view/HomeView.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/data_access/userIsLoggedIn.txt b/src/main/java/data_access/userIsLoggedIn.txt index 02e4a84d6..f32a5804e 100644 --- a/src/main/java/data_access/userIsLoggedIn.txt +++ b/src/main/java/data_access/userIsLoggedIn.txt @@ -1 +1 @@ -false \ No newline at end of file +true \ No newline at end of file diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index dcfcf9889..ef6bbfcbc 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -179,7 +179,6 @@ public void actionPerformed(ActionEvent evt) { // If you are not logged in, switch to the login view homeController.switchToLoginView(); } - } }); @@ -209,7 +208,9 @@ public void propertyChange(PropertyChangeEvent evt) { else if (evt.getPropertyName().equals("error")) { searchErrorMessageLabel.setText(state.getErrorMessage()); } - + else if (evt.getPropertyName().equals("error")) { + changeLoginButtonText(); + } } public void updateLabelStyle(JLabel label, int fontSize) { @@ -258,7 +259,7 @@ public void actionPerformed(ActionEvent evt) { } }); - rightPanel.add(viewStockButton); +// rightPanel.add(viewStockButton); // Add all to stockPanel stockPanel.add(leftPanel, BorderLayout.WEST); From 59d77ca3c195b0428ad44803429db1b8e75797c8 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 24 Nov 2024 16:55:34 -0500 Subject: [PATCH 069/113] Without logging in, pressing any button will prompt a message requiring you to log in first. --- src/main/java/view/HomeView.java | 45 +++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index a38d14bc0..a28c2a062 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -92,8 +92,14 @@ public HomeView(HomeViewModel homeViewModel) { searchButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if (evt.getSource().equals(searchButton)) { - final HomeState currentState = homeViewModel.getState(); - homeController.search(currentState.getSymbol()); + if (dataAccessObject.isUserLoggedIn()) { + final HomeState currentState = homeViewModel.getState(); + homeController.search(currentState.getSymbol()); + } + else { + showLoginRequiredDialog(); + } + } } }); @@ -137,7 +143,12 @@ public void changedUpdate(DocumentEvent e) { portfolioPanel.add(portfolioRightPanel); portfolioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - homeController.switchToPortfolio(); + if (dataAccessObject.isUserLoggedIn()) { + homeController.switchToPortfolio(); + } + else { + showLoginRequiredDialog(); + } } }); @@ -157,7 +168,12 @@ public void actionPerformed(ActionEvent evt) { watchListPanel.setBorder(BorderFactory.createEmptyBorder(16, 0, 0, 0)); watchListButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - homeController.switchToWatchList(); + if (dataAccessObject.isUserLoggedIn()) { + homeController.switchToWatchList(); + } + else { + showLoginRequiredDialog(); + } } }); // Watch list stock information @@ -299,4 +315,25 @@ public void changeLoginButtonText() { } homeViewModel.firePropertyChanged(); } + + public void showLoginRequiredDialog() { + final Object[] options = {"Go to Login"}; + final int response = JOptionPane.showOptionDialog( + this, + "You need to login first", + "Login Required", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + null, + options, + options[0] + ); + + if (response == JOptionPane.YES_OPTION) { + dataAccessObject.setUserLoggedIn(true); + dataAccessObject.saveUserLoginStatus(); + changeLoginButtonText(); + homeController.switchToLoginView(); + } + } } From 1e05a0cc4dade67574b06c03f25de2b70f0d2acb Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 24 Nov 2024 17:58:17 -0500 Subject: [PATCH 070/113] Merge this branch to allow viewing the stock page without logging in. --- src/main/java/view/HomeView.java | 6 ------ src/main/java/view/StockView.java | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index a28c2a062..db9f67e92 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -92,14 +92,8 @@ public HomeView(HomeViewModel homeViewModel) { searchButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if (evt.getSource().equals(searchButton)) { - if (dataAccessObject.isUserLoggedIn()) { final HomeState currentState = homeViewModel.getState(); homeController.search(currentState.getSymbol()); - } - else { - showLoginRequiredDialog(); - } - } } }); diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index f3fae7ee7..c9f706ec3 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -1,5 +1,6 @@ package view; +import data_access.FileUserDataAccessObject; import entity.Stock; import interface_adapter.ViewManagerModel; import interface_adapter.stock_view.StockViewModel; @@ -30,9 +31,15 @@ public class StockView extends AbstractViewWithBackButton implements PropertyCha private JLabel highPriceLabel; private JLabel volumeLabel; private JButton favoriteButton; + private JButton buyButton; private String previousView; + private FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(); + public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { + fileUserDataAccessObject.isUserLoggedIn(); + fileUserDataAccessObject.saveUserLoginStatus(); + this.viewManagerModel = viewManagerModel; this.stockViewModel = stockViewModel; @@ -92,7 +99,7 @@ private JPanel createActionButtonsPanel() { actionPanel.setBackground(Color.WHITE); actionPanel.setAlignmentX(Component.CENTER_ALIGNMENT); - final JButton buyButton = new JButton("BUY"); + buyButton = new JButton("BUY"); buyButton.setFont(new Font("SansSerif", Font.BOLD, 18)); buyButton.setFocusPainted(false); @@ -157,6 +164,11 @@ private void updateFavoriteButtonVisibility(String stockSymbol) { } } + private void setButtonVisible(boolean visible) { + favoriteButton.setVisible(visible); + buyButton.setVisible(visible); + } + private JLabel createLabel(String text, int fontSize, int fontStyle) { final JLabel label = new JLabel(text); label.setFont(new Font("SansSerif", fontStyle, fontSize)); @@ -188,6 +200,7 @@ public void propertyChange(PropertyChangeEvent evt) { updateStockData(updatedStock); } } + setButtonVisible(fileUserDataAccessObject.isUserLoggedIn()); } public void setPreviousView(String viewName) { From 75dc48f3f82552e0409bd6debf774e2a1bab98e7 Mon Sep 17 00:00:00 2001 From: misaka Date: Sun, 24 Nov 2024 19:27:21 -0500 Subject: [PATCH 071/113] fix the back button bug, and add more information about stock from watchlist on the home page --- .../interface_adapter/ViewManagerModel.java | 40 ++++++++++++++++++- .../home_view/HomePresenter.java | 8 ++-- .../watchlist_view/WatchlistPresenter.java | 3 +- src/main/java/view/HomeView.java | 24 +++++++---- src/main/java/view/StockView.java | 7 +--- src/main/java/view/WatchListView.java | 6 +-- 6 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/main/java/interface_adapter/ViewManagerModel.java b/src/main/java/interface_adapter/ViewManagerModel.java index 99dc0ffe5..c01f1b643 100644 --- a/src/main/java/interface_adapter/ViewManagerModel.java +++ b/src/main/java/interface_adapter/ViewManagerModel.java @@ -1,14 +1,50 @@ package interface_adapter; +import java.util.Stack; + /** * Model for the View Manager. Its state is the name of the View which * is currently active. An initial state of "" is used. */ public class ViewManagerModel extends ViewModel { + private final Stack viewStack = new Stack<>(); + /** + * Constructs a ViewManagerModel with the initial state set to "home view". + */ public ViewManagerModel() { super("view manager"); - this.setState(""); + this.setState("home view"); + } + + /** + * Pushes the current view to the stack and navigates to the specified view. + * + * @param viewName The name of the new view to navigate to. + */ + public void pushView(String viewName) { + + if (!this.getState().equals(viewName)) { + viewStack.push(this.getState()); + } + this.setState(viewName); + this.firePropertyChanged(); } -} + /** + * Pops the last view from the stack and navigates back to it. + * If the stack is empty, it stays on the current view. + */ + public void popView() { + + if (!viewStack.isEmpty()) { + final String previousView = viewStack.pop(); + this.setState(previousView); + this.firePropertyChanged(); + } + else { + this.setState("home view"); + this.firePropertyChanged(); + } + } +} \ No newline at end of file diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index 67de60407..64f560bec 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -56,8 +56,7 @@ public void prepareSuccessView(SearchOutputData searchOutputData) { // Clean home view error message this.sendErrorMessage(""); - viewManagerModel.setState("StockView"); - viewManagerModel.firePropertyChanged(); + viewManagerModel.pushView("StockView"); } @Override @@ -69,6 +68,8 @@ public void prepareFailView(String errorMessage) { public void switchToPortfolio() { viewManagerModel.setState("PortfolioView"); viewManagerModel.firePropertyChanged(); + + viewManagerModel.pushView("PortfolioView"); } @Override @@ -79,8 +80,7 @@ public void switchToWatchList(ArrayList watchListSymbols) { watchListViewModel.setState(watchListViewState); watchListViewModel.firePropertyChanged("watchList"); - viewManagerModel.setState("WatchListView"); - viewManagerModel.firePropertyChanged(); + viewManagerModel.pushView("WatchListView"); } @Override diff --git a/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java b/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java index 4703b6372..0fc0b867c 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchlistPresenter.java @@ -42,7 +42,6 @@ public void prepareSuccessView(SearchOutputData searchOutputData) { this.stockViewModel.setState(stockViewState); this.stockViewModel.firePropertyChanged("switchToStockView"); - viewManagerModel.setState("StockView"); - viewManagerModel.firePropertyChanged(); + viewManagerModel.pushView("StockView"); } } \ No newline at end of file diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index b5f494099..f3cd3d081 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -210,11 +210,15 @@ public void updateLabelStyle(JLabel label, int fontSize) { public void updateWatchListComponents(ArrayList watchList) { for (Stock stock : watchList) { - this.addWatchListItem(watchListContentPanel, stock.getSymbol(), String.valueOf(stock.getClosePrice())); + final String symbol = stock.getSymbol(); + final String price = String.valueOf(stock.getClosePrice()); + final String dailyChange = String.valueOf(stock.getDailyChange()); + final String dailyPercentage = (((stock.getDailyChange() / stock.getOpenPrice()) * 100)) + "%"; + this.addWatchListItem(watchListContentPanel, symbol, price, dailyChange, dailyPercentage); } } - private void addWatchListItem(JPanel contentPanel, String code, String price) { + private void addWatchListItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage) { final JPanel stockPanel = new JPanel(new BorderLayout()); stockPanel.setBackground(Color.WHITE); @@ -224,19 +228,23 @@ private void addWatchListItem(JPanel contentPanel, String code, String price) { leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); leftPanel.setBackground(Color.WHITE); + // Right part: up and down information + final JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.setBackground(Color.WHITE); + rightPanel.setBorder(BorderFactory.createEmptyBorder(24, 0, 0, 5)); + final JLabel stockCodeLabel = new JLabel("- " + code); stockCodeLabel.setFont(new Font(FONTFAMILY, Font.BOLD, 16)); final JLabel stockPriceLabel = new JLabel(price); stockPriceLabel.setFont(new Font(FONTFAMILY, Font.PLAIN, 14)); + final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); + dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + leftPanel.add(stockCodeLabel); leftPanel.add(stockPriceLabel); - - // Right part: up and down information - final JPanel rightPanel = new JPanel(); - rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); - rightPanel.setBackground(Color.WHITE); - rightPanel.setBorder(BorderFactory.createEmptyBorder(24, 0, 0, 5)); + rightPanel.add(dailyChangeLabel); final JButton viewStockButton = new JButton(RIGHTARROW); viewStockButton.addActionListener(new ActionListener() { diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 63d5a65e7..8abbe31e3 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -153,12 +153,7 @@ public String getViewName() { @Override void backButtonAction() { - if ("StockView".equals(viewManagerModel.getState())) { - viewManagerModel.setState("WatchListView"); - } - else { - viewManagerModel.setState("home view"); - } + viewManagerModel.popView(); viewManagerModel.firePropertyChanged(); } diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 673a34ee8..20e41207c 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -49,8 +49,7 @@ public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerM backButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - viewManagerModel.setState("home view"); - viewManagerModel.firePropertyChanged(); + viewManagerModel.popView(); } }); @@ -166,8 +165,7 @@ private void goToStockInformation(String code) { currentState.setSymbol(code); watchlistController.search(currentState.getSymbol()); - viewManagerModel.setState("StockView"); - viewManagerModel.firePropertyChanged(); + viewManagerModel.pushView("StockView"); } private void createWatchListview(ArrayList WatchList) { From d2aeb32aba025376ad10a470cdd94a7a9b9cf3fb Mon Sep 17 00:00:00 2001 From: misaka Date: Sun, 24 Nov 2024 19:45:29 -0500 Subject: [PATCH 072/113] fix the bug about calculate double --- src/main/java/entity/CommonStock.java | 25 +++++++++++++++++++++++-- src/main/java/view/HomeView.java | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/entity/CommonStock.java b/src/main/java/entity/CommonStock.java index 9f9d9c868..c86d6a32c 100644 --- a/src/main/java/entity/CommonStock.java +++ b/src/main/java/entity/CommonStock.java @@ -1,5 +1,8 @@ package entity; +import java.math.BigDecimal; +import java.math.RoundingMode; + /** * A simple implementation of the Stock interface. */ @@ -53,11 +56,29 @@ public double getLow() { @Override public double getDailyChange() { - return closePrice - openPrice; + return roundToOneDecimalPlace(closePrice - openPrice); } @Override public double getDailyPercentage() { - return (closePrice - openPrice) / openPrice * 100; + double result; + if (openPrice == 0) { + result = 0; + } else { + final double percentage = (closePrice - openPrice) / openPrice * 100; + result = roundToOneDecimalPlace(percentage); + } + return result; + } + + /** + * Helper method to round a double to one decimal place. + * @param value The double value to be rounded. + * @return The rounded value. + */ + private double roundToOneDecimalPlace(double value) { + return BigDecimal.valueOf(value) + .setScale(1, RoundingMode.HALF_UP) + .doubleValue(); } } diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index f3cd3d081..a8680f340 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -213,7 +213,7 @@ public void updateWatchListComponents(ArrayList watchList) { final String symbol = stock.getSymbol(); final String price = String.valueOf(stock.getClosePrice()); final String dailyChange = String.valueOf(stock.getDailyChange()); - final String dailyPercentage = (((stock.getDailyChange() / stock.getOpenPrice()) * 100)) + "%"; + final String dailyPercentage = stock.getDailyPercentage() + "%"; this.addWatchListItem(watchListContentPanel, symbol, price, dailyChange, dailyPercentage); } } From c39ee79f29f7940d2b36d6b006d3481723f79f42 Mon Sep 17 00:00:00 2001 From: Ryan Jin Date: Sun, 24 Nov 2024 22:44:28 -0800 Subject: [PATCH 073/113] added scroller --- src/main/java/view/PortfolioView.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index cc2649bc7..4038a776d 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -118,7 +118,14 @@ public void actionPerformed(ActionEvent e) { addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); - add(contentPanel, BorderLayout.CENTER); + // Wrap the content panel in a JScrollPane + final JScrollPane scrollPane = new JScrollPane(contentPanel); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + // Set the preferred size of the scroll pane + scrollPane.setPreferredSize(new Dimension(400, 300)); + add(scrollPane, BorderLayout.CENTER); } private void percentChangeLayout(JPanel statisticsPanel, String title, String totalChange, String totalPercentage) { From b708501b03d21b2b695db421d7f82e2b4f4ee951 Mon Sep 17 00:00:00 2001 From: Kairan Zhai Date: Mon, 25 Nov 2024 10:41:16 -0500 Subject: [PATCH 074/113] fix bugs and add missing code --- src/main/java/app/AppBuilder.java | 19 ++++++++++++++++- src/main/java/app/Main.java | 5 +++-- .../data_access/DBUserDataAccessObject.java | 5 +++++ .../data_access/FileUserDataAccessObject.java | 21 +++++++++++++++---- src/main/java/data_access/portfolio.txt | 2 -- src/main/java/data_access/userIsLoggedIn.txt | 2 +- src/main/java/data_access/watchlist.txt | 2 +- .../buy_view/BuyPresenter.java | 2 ++ .../home_view/HomeController.java | 7 +++++++ .../home_view/HomePresenter.java | 15 ++++++++++++- .../home_view/HomeState.java | 11 ++++++++++ .../{ => portfolio}/PortfolioController.java | 0 .../watchlist_view/WatchListViewState.java | 14 +++++++++++++ src/main/java/use_case/buy/BuyInteractor.java | 3 +++ .../use_case/home_view/HomeInputBoundary.java | 5 +++++ .../use_case/home_view/HomeInteractor.java | 12 +++++++++++ .../home_view/HomeOutputBoundary.java | 4 ++++ .../PortfolioDataAccessInterface.java | 2 ++ .../WatchListDataAccessInterface.java | 5 +++++ src/main/java/view/BuyView.java | 4 ---- src/main/java/view/HomeView.java | 7 ++++++- src/main/java/view/WatchListView.java | 4 +++- 22 files changed, 133 insertions(+), 18 deletions(-) rename src/main/java/interface_adapter/{ => portfolio}/PortfolioController.java (100%) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 0f47dfbc4..46a07d2ef 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -25,6 +25,8 @@ import interface_adapter.login.LoginViewModel; import interface_adapter.logout.LogoutController; import interface_adapter.logout.LogoutPresenter; +import interface_adapter.portfolio.PortfolioController; +import interface_adapter.portfolio.PortfolioPresenter; import interface_adapter.portfolio.PortfolioViewModel; import interface_adapter.signup.SignupController; import interface_adapter.signup.SignupPresenter; @@ -49,6 +51,9 @@ import use_case.logout.LogoutInputBoundary; import use_case.logout.LogoutInteractor; import use_case.logout.LogoutOutputBoundary; +import use_case.portfolio.PortfolioInputBoundary; +import use_case.portfolio.PortfolioInteractor; +import use_case.portfolio.PortfolioOutputBoundary; import use_case.signup.SignupInputBoundary; import use_case.signup.SignupInteractor; import use_case.signup.SignupOutputBoundary; @@ -205,7 +210,10 @@ public AppBuilder addHomeUseCase() { portfolioViewModel, stockViewModel, watchListViewModel); - final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, fileUserDataAccessObject, homeOutputBoundary); + final HomeInputBoundary homeInteractor = new HomeInteractor(dbUserDataAccessObject, + fileUserDataAccessObject, + fileUserDataAccessObject, + homeOutputBoundary); final HomeController controller = new HomeController(homeInteractor); homeView.setHomeController(controller); @@ -321,6 +329,15 @@ public AppBuilder addBuyUseCase() { return this; } + public AppBuilder addPortfolioUseCase() { + final PortfolioOutputBoundary portfolioPresenter = new PortfolioPresenter(portfolioViewModel); + final PortfolioInputBoundary portfolioInteractor = new PortfolioInteractor(fileUserDataAccessObject, dbUserDataAccessObject ,portfolioPresenter); + final PortfolioController portfolioController = new PortfolioController(portfolioInteractor); + portfolioView.setController(portfolioController); + + return this; + } + /** * Creates the JFrame for the application and initially sets the SignupView to be displayed. * @return the application diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 9c94688b4..ce4abb325 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -19,6 +19,8 @@ public static void main(String[] args) { .addSignupView() .addLoggedInView() .addStockView() + .addPortfolioView() + .addBuyView() .addHomeUseCase() .addWatchlistUseCase() .addSignupUseCase() @@ -26,9 +28,8 @@ public static void main(String[] args) { .addLogoutUseCase() .addChangePasswordUseCase() .addStockUseCase() - .addPortfolioView() - .addBuyView() .addBuyUseCase() + .addPortfolioUseCase() .build(); application.pack(); diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java index 1def6227b..9eef90dc9 100644 --- a/src/main/java/data_access/DBUserDataAccessObject.java +++ b/src/main/java/data_access/DBUserDataAccessObject.java @@ -211,4 +211,9 @@ public ArrayList getWatchList() { public void saveWatchList() { } + + @Override + public void createWatchList() { + + } } diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index d2a93ef89..2211f34f5 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -65,10 +65,7 @@ public ArrayList getWatchList() { } else { // If the watchlist file doesn't exist, we create a new one - this.watchList.add("AAPL"); - this.watchList.add("COST"); - this.watchList.add("NVDA"); - this.saveWatchList(); + createWatchList(); // Get watchlist data again return this.getWatchList(); @@ -90,6 +87,15 @@ public void saveWatchList() { } } + @Override + public void createWatchList() { + this.watchList.clear(); + this.watchList.add("AAPL"); + this.watchList.add("COST"); + this.watchList.add("NVDA"); + this.saveWatchList(); + } + @Override public void addToWatchList(String symbol) { watchList.add(symbol); @@ -160,6 +166,13 @@ public void savePortfolioList() { } } + @Override + public void createPortfolioList() { + // Create an empty portfolio list file + this.portfolioList.clear(); + savePortfolioList(); + } + public void addToPortfolioList(SimulatedHolding simulatedHolding) { boolean duplicate = false; for (SimulatedHolding data : portfolioList) { diff --git a/src/main/java/data_access/portfolio.txt b/src/main/java/data_access/portfolio.txt index 85f337c63..e69de29bb 100644 --- a/src/main/java/data_access/portfolio.txt +++ b/src/main/java/data_access/portfolio.txt @@ -1,2 +0,0 @@ -NVDA,123.0,123, -AMD,322.1,1, diff --git a/src/main/java/data_access/userIsLoggedIn.txt b/src/main/java/data_access/userIsLoggedIn.txt index f32a5804e..02e4a84d6 100644 --- a/src/main/java/data_access/userIsLoggedIn.txt +++ b/src/main/java/data_access/userIsLoggedIn.txt @@ -1 +1 @@ -true \ No newline at end of file +false \ No newline at end of file diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index 2e2aea805..12395cbeb 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPL,COST,MSTR,QQQ,TQQ \ No newline at end of file +AAPL,COST,NVDA \ No newline at end of file diff --git a/src/main/java/interface_adapter/buy_view/BuyPresenter.java b/src/main/java/interface_adapter/buy_view/BuyPresenter.java index 5cbd7f90c..0354b1b10 100644 --- a/src/main/java/interface_adapter/buy_view/BuyPresenter.java +++ b/src/main/java/interface_adapter/buy_view/BuyPresenter.java @@ -31,6 +31,8 @@ public BuyPresenter(BuyViewModel buyViewModel, PortfolioViewModel portfolioViewM @Override public void prepareSuccessView(BuyOutputData response) { portfolioViewModel.firePropertyChanged("getPortfolioList"); + + switchToHomeView(); } @Override diff --git a/src/main/java/interface_adapter/home_view/HomeController.java b/src/main/java/interface_adapter/home_view/HomeController.java index 875baf233..599895a8f 100644 --- a/src/main/java/interface_adapter/home_view/HomeController.java +++ b/src/main/java/interface_adapter/home_view/HomeController.java @@ -60,4 +60,11 @@ public void switchToSignupView() { public void getWatchList() { homeUseCaseInteractor.getWatchListData(); } + + /** + * Delete watchlist and portfolio data, and updates relative views + */ + public void deleteLocalData() { + homeUseCaseInteractor.deleteLocalData(); + } } diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index 64f560bec..6fba92b33 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -97,12 +97,25 @@ public void switchToSignupView() { @Override public void getWatchListData(ArrayList watchList) { - final HomeState homeState = new HomeState(); + final HomeState homeState = this.homeViewModel.getState(); homeState.setWatchList(watchList); this.homeViewModel.setState(homeState); this.homeViewModel.firePropertyChanged("getWatchList"); } + @Override + public void deleteLocalData() { + final HomeState homeState = this.homeViewModel.getState(); + homeState.resetWatchList(); + homeViewModel.setState(homeState); + homeViewModel.firePropertyChanged("getWatchList"); + + final WatchListViewState watchListViewState = this.watchListViewModel.getState(); + watchListViewState.resetWatchlist(); + watchListViewModel.setState(watchListViewState); + watchListViewModel.firePropertyChanged("watchList"); + } + // Helper function private void sendErrorMessage(String errorMessage) { final HomeState homeState = this.homeViewModel.getState(); diff --git a/src/main/java/interface_adapter/home_view/HomeState.java b/src/main/java/interface_adapter/home_view/HomeState.java index 209087d7a..8e4af3d02 100644 --- a/src/main/java/interface_adapter/home_view/HomeState.java +++ b/src/main/java/interface_adapter/home_view/HomeState.java @@ -59,4 +59,15 @@ public void remove(Stock stock) { this.watchList.remove(i); System.out.println(watchList.size()); } + + public void resetWatchList() { + final ArrayList temp = new ArrayList<>(); + for (Stock s : watchList) { + if (s.getSymbol().equals("AAPL") || s.getSymbol().equals("COST") || s.getSymbol().equals("NVDA")) { + temp.add(s); + } + } + this.watchList.clear(); + this.watchList.addAll(temp); + } } diff --git a/src/main/java/interface_adapter/PortfolioController.java b/src/main/java/interface_adapter/portfolio/PortfolioController.java similarity index 100% rename from src/main/java/interface_adapter/PortfolioController.java rename to src/main/java/interface_adapter/portfolio/PortfolioController.java diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java index dcd9666f8..f12809125 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java @@ -47,4 +47,18 @@ public void remove(String ticker) { this.watchlist.remove(ticker); } } + + public void resetWatchlist() { + if (this.watchlist != null) { + final ArrayList temp = new ArrayList<>(); + for (String ticker : this.watchlist) { + if (ticker.equals("AAPL") || ticker.equals("COST") || ticker.equals("NVDA")) { + temp.add(ticker); + } + } + + this.watchlist.clear(); + this.watchlist.addAll(temp); + } + } } diff --git a/src/main/java/use_case/buy/BuyInteractor.java b/src/main/java/use_case/buy/BuyInteractor.java index 07d485981..34e078e50 100644 --- a/src/main/java/use_case/buy/BuyInteractor.java +++ b/src/main/java/use_case/buy/BuyInteractor.java @@ -33,6 +33,9 @@ public void execute(BuyInputData buyInputData) { // Pass data to Portfolio View final BuyOutputData buyOutputData = new BuyOutputData(simulatedHolding); buyPresenter.prepareSuccessView(buyOutputData); + + // Tempory output data + System.out.println("User purchases: " + quantity + " " + symbol + " stock(s) at $" + price); } @Override diff --git a/src/main/java/use_case/home_view/HomeInputBoundary.java b/src/main/java/use_case/home_view/HomeInputBoundary.java index 5fe2c54df..b59da3ef1 100644 --- a/src/main/java/use_case/home_view/HomeInputBoundary.java +++ b/src/main/java/use_case/home_view/HomeInputBoundary.java @@ -36,4 +36,9 @@ public interface HomeInputBoundary { * Executes the switch to Signup view use case. */ void getWatchListData(); + + /** + * Executes the switch to Signup view use case. + */ + void deleteLocalData(); } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 6a7ea712a..22d05e709 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -3,6 +3,7 @@ import entity.CommonStockFactory; import entity.Stock; import entity.StockFactory; +import use_case.portfolio.PortfolioDataAccessInterface; import use_case.watchlist.WatchListDataAccessInterface; import java.util.ArrayList; @@ -15,13 +16,16 @@ public class HomeInteractor implements HomeInputBoundary { private final HomeDataAccessInterface homeDataAccessInterface; private final WatchListDataAccessInterface watchListDataAccessInterface; + private final PortfolioDataAccessInterface portfolioDataAccessInterface; private final HomeOutputBoundary homePresenter; public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, WatchListDataAccessInterface watchListDataAccessInterface, + PortfolioDataAccessInterface portfolioDataAccessInterface, HomeOutputBoundary homeInteractor) { this.homeDataAccessInterface = homeDataAccessInterface; this.watchListDataAccessInterface = watchListDataAccessInterface; + this.portfolioDataAccessInterface = portfolioDataAccessInterface; this.homePresenter = homeInteractor; } @@ -94,4 +98,12 @@ public void getWatchListData() { homePresenter.getWatchListData(watchList); } + + @Override + public void deleteLocalData() { + watchListDataAccessInterface.createWatchList(); + portfolioDataAccessInterface.createPortfolioList(); + + homePresenter.deleteLocalData(); + } } diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index 3f2623e5e..afef013e6 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -48,4 +48,8 @@ public interface HomeOutputBoundary { */ void getWatchListData(ArrayList watchList); + /** + * Delete watchlist and portfolio data, and updates relative views + */ + void deleteLocalData(); } diff --git a/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java b/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java index 5d5e0b226..df3fee059 100644 --- a/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java +++ b/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java @@ -14,4 +14,6 @@ public interface PortfolioDataAccessInterface { public void removeFromPortfolioList(SimulatedHolding simulatedHolding); + public void createPortfolioList(); + } diff --git a/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java b/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java index 63bc1ce31..262dff90b 100644 --- a/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java +++ b/src/main/java/use_case/watchlist/WatchListDataAccessInterface.java @@ -14,4 +14,9 @@ public interface WatchListDataAccessInterface { * Saves the watchlist. */ void saveWatchList(); + + /** + * Creates the watchlist. + */ + void createWatchList(); } diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index b71c7b11f..96651c9ff 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -82,8 +82,6 @@ private void documentListenerHelper() { final BuyState currentState = buyViewModel.getState(); currentState.setQuantity(quantityInputField.getText()); buyViewModel.setState(currentState); - - System.out.println("Quantity:" + currentState.getQunatity()); } @Override @@ -110,8 +108,6 @@ private void documentListenerHelper() { final BuyState currentState = buyViewModel.getState(); currentState.setPrice(new String(priceInputField.getText())); buyViewModel.setState(currentState); - - System.out.println("Price:" + currentState.getPrice()); } @Override diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index d49d273d8..72632a6d3 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -211,7 +211,11 @@ public void propertyChange(PropertyChangeEvent evt) { final HomeState state = (HomeState) evt.getNewValue(); if (evt.getPropertyName().equals("getWatchList")) { // We only display first three stocks - final ArrayList watchList = new ArrayList(state.getWatchList().subList(0, 3)); + ArrayList watchList = state.getWatchList(); + if (watchList.size() != 3) { + watchList = new ArrayList(state.getWatchList().subList(0, 3)); + } + watchListContentPanel.removeAll(); updateWatchListComponents(watchList); watchListContentPanel.revalidate(); @@ -307,6 +311,7 @@ public void showLogoutDialog() { dataAccessObject.setUserLoggedIn(false); dataAccessObject.saveUserLoginStatus(); changeLoginButtonText(); + homeController.deleteLocalData(); } } diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 20e41207c..01e4199ea 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -146,7 +146,9 @@ public void mouseExited(java.awt.event.MouseEvent evt) { public void propertyChange(PropertyChangeEvent evt) { if ("watchList".equals(evt.getPropertyName())) { final WatchListViewState watchListViewState = (WatchListViewState) evt.getNewValue(); - updateWatchList(watchListViewState.getWatchlist()); + if (watchListViewState.getWatchlist() != null) { + updateWatchList(watchListViewState.getWatchlist()); + } } } From d94b5d4d33811997478e4596252fa3a074e91def Mon Sep 17 00:00:00 2001 From: Ta0p1 Date: Mon, 25 Nov 2024 12:14:48 -0500 Subject: [PATCH 075/113] Displays the actual price and disables user input --- src/main/java/view/BuyView.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index 96651c9ff..eb1473087 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -40,6 +40,7 @@ public class BuyView extends JPanel implements ActionListener, PropertyChangeLis public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; this.buyViewModel = viewModel; + this.buyViewModel.addPropertyChangeListener(this); setLayout(new BorderLayout()); setBackground(Color.WHITE); @@ -52,6 +53,9 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { final LabelTextPanel quantityInfo = new LabelTextPanel( new JLabel("Quantity"), quantityInputField); + priceInputField.setEditable(false); + priceInputField.setText(buyViewModel.getState().getPrice()); + final JPanel buttons = new JPanel(); buy = new JButton("Buy"); buttons.add(buy); @@ -63,7 +67,11 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { new ActionListener() { public void actionPerformed(ActionEvent evt) { final BuyState buyState = buyViewModel.getState(); - buyController.execute(buyState.getSymbol(), Double.parseDouble(buyState.getPrice()), Integer.parseInt(quantityInputField.getText())); + // 从状态中获取价格,不需要从输入字段获取 + buyController.execute( + buyState.getSymbol(), + Double.parseDouble(buyState.getPrice()), + Integer.parseInt(quantityInputField.getText())); } } ); @@ -144,6 +152,7 @@ public void actionPerformed(ActionEvent evt) { public void propertyChange(PropertyChangeEvent evt) { final BuyState state = (BuyState) evt.getNewValue(); setFields(state); + setPrice(); } private void setFields(BuyState state) { @@ -158,4 +167,8 @@ public String getViewName() { public void setBuyController(BuyController buyController) { this.buyController = buyController; } + + public void setPrice() { + this.priceInputField.setText(buyViewModel.getState().getPrice()); + } } \ No newline at end of file From 2ebd846fa975af8c1fa523247162e7d05d51e9a4 Mon Sep 17 00:00:00 2001 From: Ta0p1 Date: Mon, 25 Nov 2024 12:16:27 -0500 Subject: [PATCH 076/113] Displays the actual price and disables user input --- src/main/java/view/BuyView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index eb1473087..32c47671c 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -67,7 +67,7 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { new ActionListener() { public void actionPerformed(ActionEvent evt) { final BuyState buyState = buyViewModel.getState(); - // 从状态中获取价格,不需要从输入字段获取 + // Get the price from the status, not from the input field buyController.execute( buyState.getSymbol(), Double.parseDouble(buyState.getPrice()), From 9d1607be906e686ae5ad4282f68c081ed627735d Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 25 Nov 2024 13:07:56 -0500 Subject: [PATCH 077/113] fix the bug in buying stock already exist --- .../data_access/FileUserDataAccessObject.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 2211f34f5..8f3672468 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -6,6 +6,8 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.*; import entity.*; @@ -175,15 +177,26 @@ public void createPortfolioList() { public void addToPortfolioList(SimulatedHolding simulatedHolding) { boolean duplicate = false; - for (SimulatedHolding data : portfolioList) { + SimulatedHolding updatedHolding = null; + + for (SimulatedHolding data : new ArrayList<>(portfolioList)) { if (data.getSymbol().equals(simulatedHolding.getSymbol())) { - data.setPurchaseAmount(simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount()); - data.setPurchasePrice(simulatedHolding.getPurchasePrice() + data.getPurchasePrice()); + int newAmount = simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount(); + double newPrice = roundToOneDecimalPlace( + (simulatedHolding.getPurchasePrice() * simulatedHolding.getPurchaseAmount() + + data.getPurchasePrice() * data.getPurchaseAmount()) / newAmount); + + updatedHolding = simulatedHoldingFactory.create(data.getSymbol(), newPrice, newAmount); + + portfolioList.remove(data); duplicate = true; break; } } - if (!duplicate) { + if (duplicate) { + portfolioList.add(updatedHolding); + } + else { portfolioList.add(simulatedHolding); } savePortfolioList(); @@ -231,6 +244,19 @@ public void setUserLoggedIn(boolean loggedIn) { this.userIsLoggedIn = loggedIn; saveUserLoginStatus(); } + + /** + * Helper method to round a double to one decimal place. + * @param value The double value to be rounded. + * @return The rounded value. + */ + private double roundToOneDecimalPlace(double value) { + return BigDecimal.valueOf(value) + .setScale(1, RoundingMode.HALF_UP) + .doubleValue(); + } } + + From 3e4d10f1bd68b66d5993f1e6d95d9f0566db88de Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 25 Nov 2024 23:00:05 -0500 Subject: [PATCH 078/113] port partly --- .../java/entity/CommonSimulatedHolding.java | 22 ++- src/main/java/entity/SimulatedHolding.java | 7 + src/main/java/view/PortfolioView.java | 152 ++++++++---------- 3 files changed, 80 insertions(+), 101 deletions(-) diff --git a/src/main/java/entity/CommonSimulatedHolding.java b/src/main/java/entity/CommonSimulatedHolding.java index c5459fd3a..1cb6ccb96 100644 --- a/src/main/java/entity/CommonSimulatedHolding.java +++ b/src/main/java/entity/CommonSimulatedHolding.java @@ -1,17 +1,14 @@ package entity; -/** - * A simple implementation of the Simulated Holding interface. - */ public class CommonSimulatedHolding implements SimulatedHolding { private String symbol; - private double price; - private int amount; + private double purchasePrice; + private int purchaseAmount; - public CommonSimulatedHolding(String symbol, double price, int amount) { + public CommonSimulatedHolding(String symbol, double purchasePrice, int purchaseAmount) { this.symbol = symbol; - this.price = price; - this.amount = amount; + this.purchasePrice = purchasePrice; + this.purchaseAmount = purchaseAmount; } @Override @@ -21,21 +18,22 @@ public String getSymbol() { @Override public double getPurchasePrice() { - return price; + return purchasePrice; } @Override public int getPurchaseAmount() { - return amount; + return purchaseAmount; } @Override public void setPurchasePrice(double purchasePrice) { - + this.purchasePrice = purchasePrice; } @Override public void setPurchaseAmount(int purchaseAmount) { - + this.purchaseAmount = purchaseAmount; } } + diff --git a/src/main/java/entity/SimulatedHolding.java b/src/main/java/entity/SimulatedHolding.java index c339e08ad..334829576 100644 --- a/src/main/java/entity/SimulatedHolding.java +++ b/src/main/java/entity/SimulatedHolding.java @@ -31,4 +31,11 @@ public interface SimulatedHolding { */ void setPurchaseAmount(int purchaseAmount); + /** + * Returns the amount of the stock that the user purchased. + * @return the amount of the stock that the user purchased. + */ + default int getAmount() { + return getPurchaseAmount(); + } } diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index 4038a776d..6a9660906 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -9,8 +9,6 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.ActionListener; -import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; @@ -18,12 +16,15 @@ import javax.swing.border.Border; import javax.swing.BorderFactory; - public class PortfolioView extends JPanel implements PropertyChangeListener { private final PortfolioViewModel portfolioViewModel; private final ViewManagerModel viewManagerModel; private PortfolioController portfolioController; + private JPanel contentPanel; + private JLabel currentAmount; + private JLabel todayChangeLabel; + private JLabel allTimeChangeLabel; public PortfolioView(PortfolioViewModel portfolioViewModel, ViewManagerModel viewManagerModel) { this.portfolioViewModel = portfolioViewModel; @@ -44,24 +45,19 @@ public PortfolioView(PortfolioViewModel portfolioViewModel, ViewManagerModel vie topRowPanel.setBackground(Color.WHITE); // Return button - final JButton backButton = new JButton("←"); + final JButton backButton = new JButton("\u2190"); backButton.setFont(new Font("SansSerif", Font.PLAIN, 20)); backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); backButton.setFocusPainted(false); backButton.setContentAreaFilled(false); - backButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - - // Sample action, replace with your own logic - viewManagerModel.setState("home view"); - viewManagerModel.firePropertyChanged(); - } + backButton.addActionListener(e -> { + viewManagerModel.setState("home view"); + viewManagerModel.firePropertyChanged(); }); // Current amount - final JLabel currentAmount = new JLabel("$12345"); + currentAmount = new JLabel("$0"); currentAmount.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); currentAmount.setFont(new Font("SansSerif", Font.PLAIN, 20)); @@ -85,39 +81,24 @@ public void actionPerformed(ActionEvent e) { // Add topRowPanel to statisticsPanel statisticsPanel.add(mainPanel); - // portfolio amount changes - percentChangeLayout(statisticsPanel, "Today", "+$44.09", "+24.10%"); - percentChangeLayout(statisticsPanel, "All Time", "+$84.09", "+40.10%"); + // Portfolio amount changes + todayChangeLabel = new JLabel("Today: $0 (0%)"); + todayChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + todayChangeLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); + statisticsPanel.add(todayChangeLabel); + + allTimeChangeLabel = new JLabel("All Time: $0 (0%)"); + allTimeChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + allTimeChangeLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); + statisticsPanel.add(allTimeChangeLabel); + add(statisticsPanel, BorderLayout.NORTH); // Create the content panel - final JPanel contentPanel = new JPanel(); + contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); - // Create a sub-panel with BorderLayout - final JPanel titlePanel = new JPanel(new BorderLayout()); - titlePanel.setBackground(Color.WHITE); - - // Portfolio title on the left - final JLabel portfolioTitle = new JLabel("Portfolio"); - portfolioTitle.setFont(new Font("SansSerif", Font.BOLD, 24)); - portfolioTitle.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); - titlePanel.add(portfolioTitle, BorderLayout.WEST); - - // Dummy text on the right - final JLabel dummyLabel = new JLabel(""); - dummyLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - dummyLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - titlePanel.add(dummyLabel, BorderLayout.EAST); - - // Add the sub-panel to the main content panel - contentPanel.add(titlePanel); - - addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09", "+24.10%"); - addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00", "+68.03%"); - addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25", "+37.87%"); - // Wrap the content panel in a JScrollPane final JScrollPane scrollPane = new JScrollPane(contentPanel); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); @@ -128,53 +109,24 @@ public void actionPerformed(ActionEvent e) { add(scrollPane, BorderLayout.CENTER); } - private void percentChangeLayout(JPanel statisticsPanel, String title, String totalChange, String totalPercentage) { - final JPanel displayPanel = new JPanel(new BorderLayout()); - displayPanel.setBackground(Color.WHITE); - displayPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); - - // Left part: Title - final JPanel leftPanel = new JPanel(); - leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); - leftPanel.setBackground(Color.WHITE); - - final JLabel titleLabel = new JLabel(title); - titleLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); - - leftPanel.add(titleLabel); - - // Right part: up and down information - final JPanel rightPanel = new JPanel(); - rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); - rightPanel.setBackground(Color.WHITE); - rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); - - final JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); - totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - - rightPanel.add(totalChangeLabel); - - // Add all to displayPanel - displayPanel.add(leftPanel, BorderLayout.WEST); - displayPanel.add(rightPanel, BorderLayout.EAST); - - // Add all information - statisticsPanel.add(displayPanel); - } - - private void addStockItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage, String totalChange, String totalPercentage) { + private void addStockItem(String code, double closePrice, double purchasePrice, int amount, double openPrice) { final JPanel stockPanel = new JPanel(new BorderLayout()); stockPanel.setBackground(Color.WHITE); // Create a matte border and an empty border final Border matteBorder = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY); - final Border emptyBorder = BorderFactory.createEmptyBorder(0, 30, 0, 0); + final Border emptyBorder = BorderFactory.createEmptyBorder(10, 10, 10, 10); // Combine them using CompoundBorder final Border combinedBorder = BorderFactory.createCompoundBorder(emptyBorder, matteBorder); - - // Set the combined border stockPanel.setBorder(combinedBorder); + // Calculate values + double currentValue = closePrice * amount; + double dailyChange = (closePrice - openPrice) * amount; + double allTimeChange = (closePrice - purchasePrice) * amount; + double dailyPercentage = ((closePrice - openPrice) / openPrice) * 100; + double allTimePercentage = ((closePrice - purchasePrice) / purchasePrice) * 100; + // Left part: Stock code and price final JPanel leftPanel = new JPanel(); leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); @@ -183,23 +135,29 @@ private void addStockItem(JPanel contentPanel, String code, String price, String final JLabel stockCodeLabel = new JLabel(code); stockCodeLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); stockCodeLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); - final JLabel stockPriceLabel = new JLabel(price); + + final JLabel stockPriceLabel = new JLabel("$" + String.format("%.2f", currentValue)); stockPriceLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + stockPriceLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + + final JLabel stockAmountLabel = new JLabel("Amount: " + amount); + stockAmountLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); leftPanel.add(stockCodeLabel); leftPanel.add(stockPriceLabel); + leftPanel.add(stockAmountLabel); - // Right part: up and down information + // Right part: daily change and total change final JPanel rightPanel = new JPanel(); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); rightPanel.setBackground(Color.WHITE); rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10)); - final JLabel dailyChangeLabel = new JLabel(dailyChange + "(" + dailyPercentage + ")"); + final JLabel dailyChangeLabel = new JLabel(String.format("%+.2f (%.2f%%)", dailyChange, dailyPercentage)); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); dailyChangeLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); - final JLabel totalChangeLabel = new JLabel(totalChange + "(" + totalPercentage + ")"); + final JLabel totalChangeLabel = new JLabel(String.format("%+.2f (%.2f%%)", allTimeChange, allTimePercentage)); totalChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); totalChangeLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); @@ -210,7 +168,7 @@ private void addStockItem(JPanel contentPanel, String code, String price, String stockPanel.add(leftPanel, BorderLayout.WEST); stockPanel.add(rightPanel, BorderLayout.EAST); - // Add all information + // Add all information to the content panel contentPanel.add(stockPanel); } @@ -220,20 +178,36 @@ public String getViewName() { public void setController(PortfolioController controller) { this.portfolioController = controller; - - // Load portfolio list data controller.getPortfolioList(); } @Override public void propertyChange(PropertyChangeEvent evt) { - // Listen getPortfolioList event if (evt.getPropertyName().equals("getPortfolioList")) { final PortfolioState portfolioState = portfolioViewModel.getState(); final ArrayList portfolioList = portfolioState.getSimulatedHoldings(); final ArrayList stockList = portfolioState.getStocks(); - System.out.println("Portfolio list size: " + portfolioList.size()); - // Update UI components based on data + + double totalValue = 0; + double totalDailyChange = 0; + double totalAllTimeChange = 0; + + int minSize = Math.min(portfolioList.size(), stockList.size()); + for (int i = 0; i < minSize; i++) { + SimulatedHolding holding = portfolioList.get(i); + Stock stock = stockList.get(i); + totalValue += stock.getClosePrice() * holding.getAmount(); + totalDailyChange += (stock.getClosePrice() - stock.getOpenPrice()) * holding.getAmount(); + totalAllTimeChange += (stock.getClosePrice() - holding.getPurchasePrice()) * holding.getAmount(); + addStockItem(holding.getSymbol(), stock.getClosePrice(), holding.getPurchasePrice(), holding.getAmount(), stock.getOpenPrice()); + } + + currentAmount.setText("$" + String.format("%.2f", totalValue)); + todayChangeLabel.setText(String.format("Today: %+.2f (%.2f%%)", totalDailyChange, (totalDailyChange / (totalValue - totalDailyChange)) * 100)); + allTimeChangeLabel.setText(String.format("All Time: %+.2f (%.2f%%)", totalAllTimeChange, (totalAllTimeChange / (totalValue - totalAllTimeChange)) * 100)); + + contentPanel.revalidate(); + contentPanel.repaint(); } } -} \ No newline at end of file +} From 5fdbc9a54ea81f070a407d007dc1e6c7679c61a5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 26 Nov 2024 14:50:31 -0500 Subject: [PATCH 079/113] add test cases for search usecase --- .../use_case/home_view/HomeInteractor.java | 42 +++--- .../home_view/HomeViewInteratorTest.java | 124 +++++++++++++++++- 2 files changed, 142 insertions(+), 24 deletions(-) diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 22d05e709..4def53f89 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -31,29 +31,29 @@ public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, @Override public void search(SearchInputData searchInputData) { -// final String stockSymbol = searchInputData.getStockSymbol(); -// try { -// final Stock stock = homeDataAccessInterface.getStock(stockSymbol); -// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); -// homePresenter.prepareSuccessView(searchOutputData); -// } -// catch (Exception err) { -// err.printStackTrace(); -// homePresenter.prepareFailView("Failed to fetch stock data"); -// } - // Default search symbol is NVDA - String stockSymbol = searchInputData.getStockSymbol(); - if (stockSymbol == null || stockSymbol.isEmpty()) { - stockSymbol = "NVDA"; + final String stockSymbol = searchInputData.getStockSymbol(); + try { + final Stock stock = homeDataAccessInterface.getStock(stockSymbol); + final SearchOutputData searchOutputData = new SearchOutputData(stock, false); + homePresenter.prepareSuccessView(searchOutputData); } - else { - stockSymbol = stockSymbol.toUpperCase(); + catch (Exception err) { + err.printStackTrace(); + homePresenter.prepareFailView("Failed to fetch stock data"); } - final StockFactory stockFactory = new CommonStockFactory(); - final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); - - final SearchOutputData searchOutputData = new SearchOutputData(stock, false); - homePresenter.prepareSuccessView(searchOutputData); +// // Default search symbol is NVDA +// String stockSymbol = searchInputData.getStockSymbol(); +// if (stockSymbol == null || stockSymbol.isEmpty()) { +// stockSymbol = "NVDA"; +// } +// else { +// stockSymbol = stockSymbol.toUpperCase(); +// } +// final StockFactory stockFactory = new CommonStockFactory(); +// final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); +// +// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); +// homePresenter.prepareSuccessView(searchOutputData); } @Override diff --git a/src/test/java/use_case/home_view/HomeViewInteratorTest.java b/src/test/java/use_case/home_view/HomeViewInteratorTest.java index 685de67c1..fca1d8a1d 100644 --- a/src/test/java/use_case/home_view/HomeViewInteratorTest.java +++ b/src/test/java/use_case/home_view/HomeViewInteratorTest.java @@ -13,8 +13,64 @@ public class HomeViewInteratorTest { @Test - public void successTest() { - final SearchInputData inputData = new SearchInputData("NVDA"); + public void successSearchWithGivenTextTest() { + final SearchInputData inputData = new SearchInputData("COST"); + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + assertEquals("COST", searchOutputData.getStock().getSymbol()); + } + + @Override + public void prepareFailView(String errorMessage) { + // Do nothing right now + } + + @Override + public void switchToPortfolio() { + // Do nothing right now + } + + @Override + public void switchToWatchList(ArrayList watchList) { + // Do nothing right now + } + + @Override + public void switchToLoginView() { + // Do nothing right now + } + + @Override + public void switchToSignupView() { + // Do nothing right now + } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.search(inputData); + } + + @Test + public void successSearchWithDefaultTextTest() { + final SearchInputData inputData = new SearchInputData(""); final UserFactory userFactory = new CommonUserFactory(); final StockFactory stockFactory = new CommonStockFactory(); final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); @@ -57,9 +113,71 @@ public void switchToSignupView() { public void getWatchListData(ArrayList watchList) { // Do nothing } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.search(inputData); + } + + @Test + public void failSearchWithWrongTextTest() { + final SearchInputData inputData = new SearchInputData("ABC"); + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + assertEquals("Failed to fetch stock data", errorMessage); + } + + @Override + public void switchToPortfolio() { + // Do nothing right now + } + + @Override + public void switchToWatchList(ArrayList watchList) { + // Do nothing right now + } + + @Override + public void switchToLoginView() { + // Do nothing right now + } + + @Override + public void switchToSignupView() { + // Do nothing right now + } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } + + @Override + public void deleteLocalData() { + // Do nothing + } }; - final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, successPresenter); + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); interactor.search(inputData); } } From 09048e69261707175b39a999ba1deaa50b6931e6 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 26 Nov 2024 16:04:23 -0500 Subject: [PATCH 080/113] port fix nan --- src/main/java/view/PortfolioView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index 6a9660906..7138d48db 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -124,8 +124,8 @@ private void addStockItem(String code, double closePrice, double purchasePrice, double currentValue = closePrice * amount; double dailyChange = (closePrice - openPrice) * amount; double allTimeChange = (closePrice - purchasePrice) * amount; - double dailyPercentage = ((closePrice - openPrice) / openPrice) * 100; - double allTimePercentage = ((closePrice - purchasePrice) / purchasePrice) * 100; + double dailyPercentage = openPrice != 0 ? ((closePrice - openPrice) / openPrice) * 100 : 0; + double allTimePercentage = purchasePrice != 0 ? ((closePrice - purchasePrice) / purchasePrice) * 100 : 0; // Left part: Stock code and price final JPanel leftPanel = new JPanel(); @@ -203,8 +203,8 @@ public void propertyChange(PropertyChangeEvent evt) { } currentAmount.setText("$" + String.format("%.2f", totalValue)); - todayChangeLabel.setText(String.format("Today: %+.2f (%.2f%%)", totalDailyChange, (totalDailyChange / (totalValue - totalDailyChange)) * 100)); - allTimeChangeLabel.setText(String.format("All Time: %+.2f (%.2f%%)", totalAllTimeChange, (totalAllTimeChange / (totalValue - totalAllTimeChange)) * 100)); + todayChangeLabel.setText(String.format("Today: %+.2f (%.2f%%)", totalDailyChange, totalValue != 0 ? (totalDailyChange / totalValue) * 100 : 0)); + allTimeChangeLabel.setText(String.format("All Time: %+.2f (%.2f%%)", totalAllTimeChange, totalValue != 0 ? (totalAllTimeChange / totalValue) * 100 : 0)); contentPanel.revalidate(); contentPanel.repaint(); From 3236dd893365a40171700a222771a346bb82e66f Mon Sep 17 00:00:00 2001 From: Ta0p1 Date: Tue, 26 Nov 2024 17:48:09 -0500 Subject: [PATCH 081/113] The price can be changed by the user (for demonstration purposes). Check whether the quantity of stock purchased is allowed, set the quantity data to double. --- .../data_access/FileUserDataAccessObject.java | 4 ++-- .../java/entity/CommonSimulatedHolding.java | 8 +++---- .../entity/CommonSimulatedHoldingFactory.java | 2 +- src/main/java/entity/SimulatedHolding.java | 6 ++--- .../java/entity/SimulatedHoldingFactory.java | 2 +- .../buy_view/BuyController.java | 2 +- src/main/java/use_case/buy/BuyInputData.java | 6 ++--- src/main/java/use_case/buy/BuyInteractor.java | 2 +- src/main/java/view/BuyView.java | 24 +++++++++++++------ src/main/java/view/PortfolioView.java | 2 +- 10 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 8f3672468..326679c4a 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -127,7 +127,7 @@ public ArrayList getPortfolioList() { final String[] data = line.split(","); final String symbol = data[0]; final double price = Double.parseDouble(data[1]); - final int amount = Integer.parseInt(data[2]); + final double amount = Double.parseDouble(data[2]); final SimulatedHolding simulatedHolding = simulatedHoldingFactory.create(symbol, price, amount); portfolioList.add(simulatedHolding); @@ -181,7 +181,7 @@ public void addToPortfolioList(SimulatedHolding simulatedHolding) { for (SimulatedHolding data : new ArrayList<>(portfolioList)) { if (data.getSymbol().equals(simulatedHolding.getSymbol())) { - int newAmount = simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount(); + double newAmount = simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount(); double newPrice = roundToOneDecimalPlace( (simulatedHolding.getPurchasePrice() * simulatedHolding.getPurchaseAmount() + data.getPurchasePrice() * data.getPurchaseAmount()) / newAmount); diff --git a/src/main/java/entity/CommonSimulatedHolding.java b/src/main/java/entity/CommonSimulatedHolding.java index 1cb6ccb96..5300b6e6c 100644 --- a/src/main/java/entity/CommonSimulatedHolding.java +++ b/src/main/java/entity/CommonSimulatedHolding.java @@ -3,9 +3,9 @@ public class CommonSimulatedHolding implements SimulatedHolding { private String symbol; private double purchasePrice; - private int purchaseAmount; + private double purchaseAmount; - public CommonSimulatedHolding(String symbol, double purchasePrice, int purchaseAmount) { + public CommonSimulatedHolding(String symbol, double purchasePrice, double purchaseAmount) { this.symbol = symbol; this.purchasePrice = purchasePrice; this.purchaseAmount = purchaseAmount; @@ -22,7 +22,7 @@ public double getPurchasePrice() { } @Override - public int getPurchaseAmount() { + public double getPurchaseAmount() { return purchaseAmount; } @@ -32,7 +32,7 @@ public void setPurchasePrice(double purchasePrice) { } @Override - public void setPurchaseAmount(int purchaseAmount) { + public void setPurchaseAmount(double purchaseAmount) { this.purchaseAmount = purchaseAmount; } } diff --git a/src/main/java/entity/CommonSimulatedHoldingFactory.java b/src/main/java/entity/CommonSimulatedHoldingFactory.java index cd1a45c60..b760c933e 100644 --- a/src/main/java/entity/CommonSimulatedHoldingFactory.java +++ b/src/main/java/entity/CommonSimulatedHoldingFactory.java @@ -5,7 +5,7 @@ */ public class CommonSimulatedHoldingFactory implements SimulatedHoldingFactory { @Override - public SimulatedHolding create(String symbol, double price, int amount) { + public SimulatedHolding create(String symbol, double price, double amount) { return new CommonSimulatedHolding(symbol, price, amount); } } diff --git a/src/main/java/entity/SimulatedHolding.java b/src/main/java/entity/SimulatedHolding.java index 334829576..0e8dfc786 100644 --- a/src/main/java/entity/SimulatedHolding.java +++ b/src/main/java/entity/SimulatedHolding.java @@ -17,7 +17,7 @@ public interface SimulatedHolding { * Returns the amount the stock that user purchased. * @return the amount the stock that user purchased. */ - int getPurchaseAmount(); + double getPurchaseAmount(); /** * Sets the new purchase price of the stock. @@ -29,13 +29,13 @@ public interface SimulatedHolding { * Sets the new purchase amount of the stock. * @param purchaseAmount the new purchase amount of the stock. */ - void setPurchaseAmount(int purchaseAmount); + void setPurchaseAmount(double purchaseAmount); /** * Returns the amount of the stock that the user purchased. * @return the amount of the stock that the user purchased. */ - default int getAmount() { + default double getAmount() { return getPurchaseAmount(); } } diff --git a/src/main/java/entity/SimulatedHoldingFactory.java b/src/main/java/entity/SimulatedHoldingFactory.java index af05d343d..62b66f538 100644 --- a/src/main/java/entity/SimulatedHoldingFactory.java +++ b/src/main/java/entity/SimulatedHoldingFactory.java @@ -10,5 +10,5 @@ public interface SimulatedHoldingFactory { * @param amount the amount that user purchased * @return the new simulated holding */ - SimulatedHolding create(String symbol, double price, int amount); + SimulatedHolding create(String symbol, double price, double amount); } diff --git a/src/main/java/interface_adapter/buy_view/BuyController.java b/src/main/java/interface_adapter/buy_view/BuyController.java index 608cbf93c..4b1406c64 100644 --- a/src/main/java/interface_adapter/buy_view/BuyController.java +++ b/src/main/java/interface_adapter/buy_view/BuyController.java @@ -22,7 +22,7 @@ public BuyController(BuyInputBoundary buyUseCaseInteractor) { * @param quantity the quantity of the stock */ - public void execute(String symbol, double price, int quantity) { + public void execute(String symbol, double price, double quantity) { final BuyInputData buyInputData = new BuyInputData( symbol, price, quantity); diff --git a/src/main/java/use_case/buy/BuyInputData.java b/src/main/java/use_case/buy/BuyInputData.java index 109d73a7d..8a4c2a370 100644 --- a/src/main/java/use_case/buy/BuyInputData.java +++ b/src/main/java/use_case/buy/BuyInputData.java @@ -7,9 +7,9 @@ public class BuyInputData { private final String symbol; private final double price; - private final int quantity; + private final double quantity; - public BuyInputData(String symbol, double price, int quantity) { + public BuyInputData(String symbol, double price, double quantity) { this.symbol = symbol; this.price = price; this.quantity = quantity; @@ -21,7 +21,7 @@ public double getPrice() { return price; } - public int getQuantity() { + public double getQuantity() { return quantity; } diff --git a/src/main/java/use_case/buy/BuyInteractor.java b/src/main/java/use_case/buy/BuyInteractor.java index 34e078e50..e271f4d97 100644 --- a/src/main/java/use_case/buy/BuyInteractor.java +++ b/src/main/java/use_case/buy/BuyInteractor.java @@ -22,7 +22,7 @@ public BuyInteractor(BuyOutputBoundary buyOutputBoundary, PortfolioDataAccessInt public void execute(BuyInputData buyInputData) { final String symbol = buyInputData.getSymbol(); final double price = buyInputData.getPrice(); - final int quantity = buyInputData.getQuantity(); + final double quantity = buyInputData.getQuantity(); final CommonSimulatedHoldingFactory commonSimulatedHoldingFactory = new CommonSimulatedHoldingFactory(); final SimulatedHolding simulatedHolding = commonSimulatedHoldingFactory.create(symbol, price, quantity); diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index 32c47671c..1c0f0390d 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -53,7 +53,8 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { final LabelTextPanel quantityInfo = new LabelTextPanel( new JLabel("Quantity"), quantityInputField); - priceInputField.setEditable(false); + // Decide if the price can be changed by the user. + priceInputField.setEditable(true); priceInputField.setText(buyViewModel.getState().getPrice()); final JPanel buttons = new JPanel(); @@ -66,12 +67,21 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { buy.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt) { - final BuyState buyState = buyViewModel.getState(); - // Get the price from the status, not from the input field - buyController.execute( - buyState.getSymbol(), - Double.parseDouble(buyState.getPrice()), - Integer.parseInt(quantityInputField.getText())); + try { + double quantity = Double.parseDouble(quantityInputField.getText()); + if (quantity > 0) { + final BuyState buyState = buyViewModel.getState(); + // Get the price from the status, not from the input field + buyController.execute( + buyState.getSymbol(), + Double.parseDouble(buyState.getPrice()), + quantity); + } else { + JOptionPane.showMessageDialog(null, "Please enter a positive quantity."); + } + } catch (NumberFormatException e) { + JOptionPane.showMessageDialog(null, "Please enter a valid quantity."); + } } } ); diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index 6a9660906..f1ef90269 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -109,7 +109,7 @@ public PortfolioView(PortfolioViewModel portfolioViewModel, ViewManagerModel vie add(scrollPane, BorderLayout.CENTER); } - private void addStockItem(String code, double closePrice, double purchasePrice, int amount, double openPrice) { + private void addStockItem(String code, double closePrice, double purchasePrice, double amount, double openPrice) { final JPanel stockPanel = new JPanel(new BorderLayout()); stockPanel.setBackground(Color.WHITE); // Create a matte border and an empty border From bb5f8bb744df784c1467f679fc504d2d00d4e3c3 Mon Sep 17 00:00:00 2001 From: ryanjin333 <85974950+ryanjin333@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:02:02 -0500 Subject: [PATCH 082/113] Update README.md Added the titles of the info still needed on the readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 54ec781e1..982176f24 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,16 @@ Stock MarketPlace ## Project Description Introducing our Stock Marketplace Application, a user-friendly platform designed for both casual viewers and active investors. With this app, users can quickly look up stock information by entering ticker symbols without needing to log in, providing immediate access to essential market data. For personalized experience, users can create an account to build a watchlist of their favorite stocks, making it easy to track preferred investments. Additionally, the application features a simulated holdings section where logged-in users can add stocks along with their purchase amounts and prices, allowing for real-time calculations of day gains and total gains. +## Table of Contents + +ADD TABLE OF CONTENTS HERE + +## Installation Instructions + +## Usage Guide + +## Feedback + ## User Stories - As a visitor, I want to view stock information by entering a ticker symbol, so that I can quickly access market data without needing to create an account. (kairanzh) - As a registered user, I want to log in to my account, so that I can save my favorite stocks in a personalized watchlist. (huan4000) @@ -63,6 +73,14 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed `Watchlist` image +## Contributing + +add sum here + +## License + +CC0-1.0 license + From 752bb8c9144e2a016183ff32e58fbcca021d62df Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sat, 30 Nov 2024 17:55:27 -0500 Subject: [PATCH 083/113] The cancel button in BuyView returns to the stock view. --- src/main/java/app/AppBuilder.java | 3 ++- .../interface_adapter/buy_view/BuyController.java | 7 +++++++ .../interface_adapter/buy_view/BuyPresenter.java | 15 ++++++++++++++- src/main/java/use_case/buy/BuyInputBoundary.java | 5 +++++ src/main/java/use_case/buy/BuyInteractor.java | 7 +++++++ src/main/java/use_case/buy/BuyOutputBoundary.java | 5 +++++ src/main/java/view/BuyView.java | 3 +-- src/main/java/view/StockView.java | 3 ++- 8 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 46a07d2ef..c92fb89af 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -320,7 +320,8 @@ public AppBuilder addStockUseCase() { * @return this builder */ public AppBuilder addBuyUseCase() { - final BuyOutputBoundary buyOutputBoundary = new BuyPresenter(buyViewModel, portfolioViewModel, viewManagerModel, homeViewModel); + final BuyOutputBoundary buyOutputBoundary = new BuyPresenter(buyViewModel, portfolioViewModel, + viewManagerModel, homeViewModel, stockViewModel); final BuyInputBoundary buyInteractor = new BuyInteractor( buyOutputBoundary, fileUserDataAccessObject); diff --git a/src/main/java/interface_adapter/buy_view/BuyController.java b/src/main/java/interface_adapter/buy_view/BuyController.java index 4b1406c64..d97feb9ff 100644 --- a/src/main/java/interface_adapter/buy_view/BuyController.java +++ b/src/main/java/interface_adapter/buy_view/BuyController.java @@ -35,4 +35,11 @@ public void execute(String symbol, double price, double quantity) { public void switchToHomeView() { buyUseCaseInteractor.switchToHomeView(); } + + /** + * Executes the "switch to StockView" Use Case. + */ + public void switchToStockView() { + buyUseCaseInteractor.switchToStockView(); + } } diff --git a/src/main/java/interface_adapter/buy_view/BuyPresenter.java b/src/main/java/interface_adapter/buy_view/BuyPresenter.java index 0354b1b10..9db07f193 100644 --- a/src/main/java/interface_adapter/buy_view/BuyPresenter.java +++ b/src/main/java/interface_adapter/buy_view/BuyPresenter.java @@ -8,6 +8,7 @@ import interface_adapter.home_view.HomeViewModel; import interface_adapter.portfolio.PortfolioState; import interface_adapter.portfolio.PortfolioViewModel; +import interface_adapter.stock_view.StockViewModel; import use_case.buy.BuyOutputBoundary; import use_case.buy.BuyOutputData; import use_case.login.LoginOutputBoundary; @@ -20,12 +21,15 @@ public class BuyPresenter implements BuyOutputBoundary { private final PortfolioViewModel portfolioViewModel; private final ViewManagerModel viewManagerModel; private final HomeViewModel homeViewModel; + private final StockViewModel stockViewModel; - public BuyPresenter(BuyViewModel buyViewModel, PortfolioViewModel portfolioViewModel, ViewManagerModel viewManagerModel, HomeViewModel homeViewModel) { + public BuyPresenter(BuyViewModel buyViewModel, PortfolioViewModel portfolioViewModel, + ViewManagerModel viewManagerModel, HomeViewModel homeViewModel, StockViewModel stockViewModel) { this.buyViewModel = buyViewModel; this.portfolioViewModel = portfolioViewModel; this.viewManagerModel = viewManagerModel; this.homeViewModel = homeViewModel; + this.stockViewModel = stockViewModel; } @Override @@ -39,5 +43,14 @@ public void prepareSuccessView(BuyOutputData response) { public void switchToHomeView() { viewManagerModel.setState(homeViewModel.getViewName()); viewManagerModel.firePropertyChanged(); + + } + + @Override + public void switchToStockView() { + viewManagerModel.setState(stockViewModel.getViewName()); + viewManagerModel.firePropertyChanged("switchToStockView"); + + viewManagerModel.pushView("StockView"); } } \ No newline at end of file diff --git a/src/main/java/use_case/buy/BuyInputBoundary.java b/src/main/java/use_case/buy/BuyInputBoundary.java index bedacbb99..fbcdf9695 100644 --- a/src/main/java/use_case/buy/BuyInputBoundary.java +++ b/src/main/java/use_case/buy/BuyInputBoundary.java @@ -15,4 +15,9 @@ public interface BuyInputBoundary { * Executes the switch to home view use case. */ void switchToHomeView(); + + /** + * Executes the switch to stock view use case. + */ + void switchToStockView(); } diff --git a/src/main/java/use_case/buy/BuyInteractor.java b/src/main/java/use_case/buy/BuyInteractor.java index e271f4d97..d02617b90 100644 --- a/src/main/java/use_case/buy/BuyInteractor.java +++ b/src/main/java/use_case/buy/BuyInteractor.java @@ -40,6 +40,13 @@ public void execute(BuyInputData buyInputData) { @Override public void switchToHomeView() { + buyPresenter.switchToHomeView(); } + + @Override + public void switchToStockView() { + + buyPresenter.switchToStockView(); + } } diff --git a/src/main/java/use_case/buy/BuyOutputBoundary.java b/src/main/java/use_case/buy/BuyOutputBoundary.java index abaad9057..0d7a670b0 100644 --- a/src/main/java/use_case/buy/BuyOutputBoundary.java +++ b/src/main/java/use_case/buy/BuyOutputBoundary.java @@ -15,4 +15,9 @@ public interface BuyOutputBoundary { * Switches to the Home View. */ void switchToHomeView(); + + /** + * Switches to the Stock View. + */ + void switchToStockView(); } diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index 1c0f0390d..72c0bf630 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -89,8 +89,7 @@ public void actionPerformed(ActionEvent evt) { cancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - viewManagerModel.setState("home view"); - viewManagerModel.firePropertyChanged(); + buyController.switchToStockView(); } }); diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index d8be71d6b..05c16c46f 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -18,6 +18,7 @@ * The View for displaying detailed information about a single stock. */ public class StockView extends AbstractViewWithBackButton implements PropertyChangeListener { + private final String viewName = "StockView"; private final ViewManagerModel viewManagerModel; private final StockViewModel stockViewModel; @@ -174,7 +175,7 @@ private JLabel createLabel(String text, int fontSize, int fontStyle) { } public String getViewName() { - return "StockView"; + return viewName; } @Override From e0c583b173f338d0d42d81aefed78fe53c8af131 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Sat, 30 Nov 2024 20:27:04 -0500 Subject: [PATCH 084/113] Update README.md Update readme structure --- README.md | 76 ++++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 54ec781e1..f2c46d354 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,43 @@ -# Course Project Blueprint - -## Domain - -Stock MarketPlace +# Stock Marketplace + +## Contents +- Authors and Contributors +- Project Description +- Features +- Installation Instructions +- Usage Guide +- License +- Feedback Submition +- Contribution Submition + +## Authors and Contributors +- Kairan Zhai +- Ziming Huang +- Zifan Guo +- Yitao Huang +- Ryan Jin ## Project Description Introducing our Stock Marketplace Application, a user-friendly platform designed for both casual viewers and active investors. With this app, users can quickly look up stock information by entering ticker symbols without needing to log in, providing immediate access to essential market data. For personalized experience, users can create an account to build a watchlist of their favorite stocks, making it easy to track preferred investments. Additionally, the application features a simulated holdings section where logged-in users can add stocks along with their purchase amounts and prices, allowing for real-time calculations of day gains and total gains. -## User Stories -- As a visitor, I want to view stock information by entering a ticker symbol, so that I can quickly access market data without needing to create an account. (kairanzh) -- As a registered user, I want to log in to my account, so that I can save my favorite stocks in a personalized watchlist. (huan4000) -- As a registered user, I want to log in to my account, so that I can access my watchlist and view my favourite stocks’ information. (guozifa1) -- As a registered user, I want to log in to my account, so that I can access add any stock in my simulated holding. (huan4066) -- As a registered user, I want to log in to my account, so that I can access my simulated holding and view day’s gain and total gain. (jinryan1) -- As a registered user, I want to log out of my account, so that my information remains secure when I’m not using the application. (huan4066) - -## Proposed Entities -`User` -- Name string -- Password string -- Watchlist array of string -- SimulatedHoldings array of `SimulatedHolding` - -`SimulatedHolding (Stock)` -- Amount int -- Purchased Price int - -`Stock` -- Symbol string -- OpenPrice int -- ClosePrice int -- Volume int -- High int -- Low int - -## External APIs -- User related: http://vm003.teach.cs.toronto.edu:20112/user -- Stock information: https://api.marketstack.com/v1/eod?symbols=AAPL - -## Documentation -`Link`: https://marketstack.com/documentation - -`access_key`: 3847d86b56ca461a0da759024332c06a +## Features +- Users can search stock information by entering stock symbol. +- Users can add favourite stock to their watchlists. +- Users can view their favourite stocks in their watchlists. +- Users can add simulated purchase to their portfolio list. +- Users can check their performance in portfolio list view. +- Users can sign up by entering their usernames and passwords. +- Users can log in by entering their usernames and passwors. + +## Installation Instructions + +## Usage Guide + +## License + +## Feedback Submition + +## Contribution Submition ## Project Blueprint image From fd0e96a1840055ee17ae62f0a5ef57338cb25a9c Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Sat, 30 Nov 2024 20:33:44 -0500 Subject: [PATCH 085/113] Update README.md update mit license --- README.md | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f2c46d354..f7269b8e8 100644 --- a/README.md +++ b/README.md @@ -33,31 +33,21 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed ## Usage Guide -## License +## Feedbacks -## Feedback Submition +## Contributions -## Contribution Submition +## License +The MIT License (MIT) -## Project Blueprint -image -image -image -image -image +Copyright (C) <2024> -## Week 2 Progress -`Home Page` -image +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -`Stock View` -image +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -`Portfolio` -image -`Watchlist` -image +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From e5dd1d90fb0d76d2871db9ec772ba418efa671a4 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Sat, 30 Nov 2024 20:36:22 -0500 Subject: [PATCH 086/113] Update README.md Update license section --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index f7269b8e8..82b8acece 100644 --- a/README.md +++ b/README.md @@ -38,16 +38,7 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed ## Contributions ## License -The MIT License (MIT) - -Copyright (C) <2024> - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Stock Marketplace is under CC0-1.0 license. See the LICENSE file for more info. From 8410b5cbe80d7c4bdd10cc80a33a11d073ae9797 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Sat, 30 Nov 2024 20:49:25 -0500 Subject: [PATCH 087/113] Update README.md Update installation and usage section --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 82b8acece..3545e7b1f 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,10 @@ - Authors and Contributors - Project Description - Features -- Installation Instructions -- Usage Guide +- Installation and Usage Instructions +- Feedbacks +- Contributions - License -- Feedback Submition -- Contribution Submition ## Authors and Contributors - Kairan Zhai @@ -29,13 +28,18 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed - Users can sign up by entering their usernames and passwords. - Users can log in by entering their usernames and passwors. -## Installation Instructions +## Installation and Usage Instruction +To run this project, follow these steps: -## Usage Guide +1. Clone the repository to your local machine. +2. Navigate to the `src/main/java/app` directory: +3. Click the green arrow on the top right section in your Intellij.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) ## Feedbacks +You are welcomed to submit a issue! ## Contributions +Any pull requests are welcomed! 💖 ## License Stock Marketplace is under CC0-1.0 license. See the LICENSE file for more info. From ff71add80047f69c348d655da3c29114a4534d71 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Sat, 30 Nov 2024 21:46:21 -0500 Subject: [PATCH 088/113] Update README.md Add hyperlink --- README.md | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8ce446965..fc3435319 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Stock Marketplace ## Contents -- Authors and Contributors -- Project Description -- Features -- Installation and Usage Instructions -- Feedbacks -- Contributions -- License +- [Authors and Contributors](#authors-and-contributors) +- [Project Description](#project-description) +- [Features](#features) +- [Installation and Usage Instructions](#installation-and-usage-instructions) +- [Feedbacks](#feedbacks) +- [Contributions](#contributions) +- [License](#license) ## Authors and Contributors - Kairan Zhai @@ -16,9 +16,13 @@ - Yitao Huang - Ryan Jin +[Back to Top](#contents) + ## Project Description Introducing our Stock Marketplace Application, a user-friendly platform designed for both casual viewers and active investors. With this app, users can quickly look up stock information by entering ticker symbols without needing to log in, providing immediate access to essential market data. For personalized experience, users can create an account to build a watchlist of their favorite stocks, making it easy to track preferred investments. Additionally, the application features a simulated holdings section where logged-in users can add stocks along with their purchase amounts and prices, allowing for real-time calculations of day gains and total gains. +[Back to Top](#contents) + ## Features - Users can search stock information by entering stock symbol. - Users can add favourite stock to their watchlists. @@ -26,28 +30,30 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed - Users can add simulated purchase to their portfolio list. - Users can check their performance in portfolio list view. - Users can sign up by entering their usernames and passwords. -- Users can log in by entering their usernames and passwors. +- Users can log in by entering their usernames and passwords. -## Installation and Usage Instruction +[Back to Top](#contents) + +## Installation and Usage Instructions To run this project, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the `src/main/java/app` directory: -3. Click the green arrow on the top right section in your Intellij.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) +3. Click the green arrow on the top right section in your IntelliJ.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) + +[Back to Top](#contents) ## Feedbacks -You are welcomed to submit a issue! +You are welcomed to submit an issue! + +[Back to Top](#contents) ## Contributions Any pull requests are welcomed! 💖 +[Back to Top](#contents) + ## License Stock Marketplace is under CC0-1.0 license. See the LICENSE file for more info. - - - - - - - +[Back to Top](#contents) From 3c72dc823daa384a1ec41e3af4f95698e453e126 Mon Sep 17 00:00:00 2001 From: misaka Date: Sat, 30 Nov 2024 22:01:04 -0500 Subject: [PATCH 089/113] Fix the Structure of WatchListView --- src/main/java/app/AppBuilder.java | 4 +- src/main/java/data_access/userIsLoggedIn.txt | 2 +- src/main/java/data_access/watchlist.txt | 2 +- .../home_view/HomePresenter.java | 6 +- .../stock_view/StockPresenter.java | 4 +- .../watchlist_view/WatchListViewModel.java | 8 +++ .../watchlist_view/WatchListViewState.java | 60 ++++++++++++------- .../watchlist_view/WatchlistController.java | 15 ++++- .../use_case/home_view/HomeInteractor.java | 11 +++- .../home_view/HomeOutputBoundary.java | 2 +- .../watchlist/WatchlistInputBoundary.java | 13 +++- .../watchlist/WatchlistInteractor.java | 15 ++++- src/main/java/view/WatchListView.java | 33 ++++------ 13 files changed, 117 insertions(+), 58 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 46a07d2ef..00c4e5665 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -160,7 +160,7 @@ public AppBuilder addLoggedInView() { */ public AppBuilder addWatchListView() { watchListViewModel = new WatchListViewModel(); - watchListView = new WatchListView(watchListViewModel, viewManagerModel, dbUserDataAccessObject); + watchListView = new WatchListView(watchListViewModel, viewManagerModel); cardPanel.add(watchListView, watchListView.getViewName()); return this; } @@ -229,7 +229,7 @@ public AppBuilder addWatchlistUseCase() { viewManagerModel, stockViewModel, watchListViewModel); - final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary); + final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary, dbUserDataAccessObject); final WatchlistController controller = new WatchlistController(watchlistInteractor); watchListView.setwatchlistController(controller); diff --git a/src/main/java/data_access/userIsLoggedIn.txt b/src/main/java/data_access/userIsLoggedIn.txt index 02e4a84d6..f32a5804e 100644 --- a/src/main/java/data_access/userIsLoggedIn.txt +++ b/src/main/java/data_access/userIsLoggedIn.txt @@ -1 +1 @@ -false \ No newline at end of file +true \ No newline at end of file diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index 12395cbeb..31021bcbb 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPL,COST,NVDA \ No newline at end of file +NVDA \ No newline at end of file diff --git a/src/main/java/interface_adapter/home_view/HomePresenter.java b/src/main/java/interface_adapter/home_view/HomePresenter.java index 6fba92b33..cacf4affe 100644 --- a/src/main/java/interface_adapter/home_view/HomePresenter.java +++ b/src/main/java/interface_adapter/home_view/HomePresenter.java @@ -73,10 +73,10 @@ public void switchToPortfolio() { } @Override - public void switchToWatchList(ArrayList watchListSymbols) { + public void switchToWatchList(ArrayList watchListStocks) { // Pass watchList symbols to watchList view final WatchListViewState watchListViewState = new WatchListViewState(); - watchListViewState.setWatchlist(watchListSymbols); + watchListViewState.setWatchlist(watchListStocks); watchListViewModel.setState(watchListViewState); watchListViewModel.firePropertyChanged("watchList"); @@ -111,7 +111,7 @@ public void deleteLocalData() { homeViewModel.firePropertyChanged("getWatchList"); final WatchListViewState watchListViewState = this.watchListViewModel.getState(); - watchListViewState.resetWatchlist(); + watchListViewState.resetWatchList(); watchListViewModel.setState(watchListViewState); watchListViewModel.firePropertyChanged("watchList"); } diff --git a/src/main/java/interface_adapter/stock_view/StockPresenter.java b/src/main/java/interface_adapter/stock_view/StockPresenter.java index 9c86e70f4..e656e4dd3 100644 --- a/src/main/java/interface_adapter/stock_view/StockPresenter.java +++ b/src/main/java/interface_adapter/stock_view/StockPresenter.java @@ -57,7 +57,7 @@ public void presentAddToWatchlist(Stock stock) { homeViewModel.firePropertyChanged("getWatchList"); // Pass new watchlist data to watchListView - watchListViewState.add(stock.getSymbol()); + watchListViewState.add(stock); watchListViewModel.firePropertyChanged("getWatchList"); } @@ -71,7 +71,7 @@ public void presentRemoveFromWatchlist(Stock stock) { homeViewModel.firePropertyChanged("getWatchList"); // Pass new watchlist data to watchListView - watchListViewState.remove(stock.getSymbol()); + watchListViewState.remove(stock); watchListViewModel.firePropertyChanged("watchList"); } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java index f2aa3d0a5..354655b37 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewModel.java @@ -1,5 +1,6 @@ package interface_adapter.watchlist_view; +import entity.Stock; import interface_adapter.ViewModel; import interface_adapter.stock_view.StockViewState; import interface_adapter.stock_view.WatchListViewState; @@ -16,4 +17,11 @@ public WatchListViewModel() { super("watch list view"); setState(new WatchListViewState()); } + + public void updateWatchlist(ArrayList stocks) { + WatchListViewState newState = getState(); + newState.setWatchlist(stocks); + setState(newState); + } + } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java index f12809125..8631cf117 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchListViewState.java @@ -3,6 +3,8 @@ import entity.Stock; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; /** * The state for the Stock View Model. @@ -10,7 +12,7 @@ public class WatchListViewState { private String symbol; private Stock stock; - private ArrayList watchlist; + private ArrayList watchlist; public String getSymbol() { return symbol; @@ -20,7 +22,7 @@ public Stock getStock() { return stock; } - public ArrayList getWatchlist() { + public ArrayList getWatchlist() { return watchlist; } @@ -28,7 +30,7 @@ public void setStock(Stock newStock) { this.stock = newStock; } - public void setWatchlist(ArrayList newWatchlist) { + public void setWatchlist(ArrayList newWatchlist) { this.watchlist = newWatchlist; } @@ -36,29 +38,47 @@ public void setSymbol(String symbol) { this.symbol = symbol; } - public void add(String ticker) { - if (this.watchlist != null) { - this.watchlist.add(ticker); + public void add(Stock stock) { + if (this.watchlist == null) { + this.watchlist = new ArrayList<>(); } + this.watchlist.add(stock); + Collections.sort(this.watchlist, new Comparator() { + public int compare(Stock o1, Stock o2) { + return o1.getSymbol().compareTo(o2.getSymbol()); + } + }); + System.out.println(watchlist.size()); } - public void remove(String ticker) { - if (this.watchlist != null) { - this.watchlist.remove(ticker); + public void remove(Stock stock) { + if (this.watchlist == null) { + return; + } + int i = 0; + for (Stock s : watchlist) { + if (s.getSymbol().equals(stock.getSymbol())) { + break; + } + i++; + } + if (i < watchlist.size()) { + this.watchlist.remove(i); + System.out.println(watchlist.size()); } } - public void resetWatchlist() { - if (this.watchlist != null) { - final ArrayList temp = new ArrayList<>(); - for (String ticker : this.watchlist) { - if (ticker.equals("AAPL") || ticker.equals("COST") || ticker.equals("NVDA")) { - temp.add(ticker); - } + public void resetWatchList() { + if (this.watchlist == null) { + return; + } + final ArrayList temp = new ArrayList<>(); + for (Stock s : watchlist) { + if (s.getSymbol().equals("AAPL") || s.getSymbol().equals("COST") || s.getSymbol().equals("NVDA")) { + temp.add(s); } - - this.watchlist.clear(); - this.watchlist.addAll(temp); } - } + this.watchlist.clear(); + this.watchlist.addAll(temp); + } } diff --git a/src/main/java/interface_adapter/watchlist_view/WatchlistController.java b/src/main/java/interface_adapter/watchlist_view/WatchlistController.java index 3184d72ce..80df2a952 100644 --- a/src/main/java/interface_adapter/watchlist_view/WatchlistController.java +++ b/src/main/java/interface_adapter/watchlist_view/WatchlistController.java @@ -27,7 +27,18 @@ public void search(String symbol) { watchlistInteractor.search(searchInputData); } - public void getWatchList() { - watchlistInteractor.getWatchListData(); + + /** + * Try to get WatchlistData as Stock information. + * @param stockCodes the code of the stock. + */ + public void loadWatchlistData(ArrayList stockCodes) { + ArrayList stocks = new ArrayList<>(); + for (String stockCode : stockCodes) { + final Stock stock = watchlistInteractor.getStockData(stockCode); + stocks.add(stock); + } + watchlistInteractor.updateWatchlist(stocks); } + } \ No newline at end of file diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 22d05e709..a15a3113f 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -64,7 +64,16 @@ public void switchToPortfolio() { @Override public void switchToWatchList() { final ArrayList watchListData = watchListDataAccessInterface.getWatchList(); - homePresenter.switchToWatchList(watchListData); + final ArrayList watchList = new ArrayList<>(); + + final StockFactory stockFactory = new CommonStockFactory(); + + for (String symbol : watchListData) { + final Stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); + watchList.add(stock); + } + + homePresenter.switchToWatchList(watchList); } @Override diff --git a/src/main/java/use_case/home_view/HomeOutputBoundary.java b/src/main/java/use_case/home_view/HomeOutputBoundary.java index afef013e6..5c0ecb5c2 100644 --- a/src/main/java/use_case/home_view/HomeOutputBoundary.java +++ b/src/main/java/use_case/home_view/HomeOutputBoundary.java @@ -30,7 +30,7 @@ public interface HomeOutputBoundary { * Switches to the watch list View. * @param watchListSymbols the symbols of whole watch list */ - void switchToWatchList(ArrayList watchListSymbols); + void switchToWatchList(ArrayList watchListSymbols); /** * Switches to the Login View. diff --git a/src/main/java/use_case/watchlist/WatchlistInputBoundary.java b/src/main/java/use_case/watchlist/WatchlistInputBoundary.java index 9373b1e3b..a53600bb8 100644 --- a/src/main/java/use_case/watchlist/WatchlistInputBoundary.java +++ b/src/main/java/use_case/watchlist/WatchlistInputBoundary.java @@ -12,10 +12,19 @@ public interface WatchlistInputBoundary { */ void search(SearchInputData searchInputData); + /** * Executes the search use case. - * @param searchInputData the input data + * @param stocks the input data + */ + + void updateWatchlist(ArrayList stocks); + + + /** + * Executes the search use case. + * @param stockCode the input data */ - void getWatchListData(); + Stock getStockData(String stockCode); } diff --git a/src/main/java/use_case/watchlist/WatchlistInteractor.java b/src/main/java/use_case/watchlist/WatchlistInteractor.java index 8ecd5bc11..0647a9473 100644 --- a/src/main/java/use_case/watchlist/WatchlistInteractor.java +++ b/src/main/java/use_case/watchlist/WatchlistInteractor.java @@ -1,5 +1,6 @@ package use_case.home_view; +import data_access.DBUserDataAccessObject; import entity.CommonStockFactory; import entity.Stock; import entity.StockFactory; @@ -8,6 +9,8 @@ import use_case.home_view.WatchlistInputBoundary; import use_case.watchlist.WatchListDataAccessInterface; +import java.util.ArrayList; + /** * The Home View Interactor. */ @@ -15,11 +18,13 @@ public class WatchlistInteractor implements WatchlistInputBoundary { private final WatchListDataAccessInterface watchlistDataAccessInterface; private final WatchlistOutputBoundary watchlistPresenter; + private final DBUserDataAccessObject dbUserDataAccessObject; public WatchlistInteractor(WatchListDataAccessInterface watchlistDataAccessInterface, - WatchlistOutputBoundary watchlistPresenter) { + WatchlistOutputBoundary watchlistPresenter, DBUserDataAccessObject dbUserDataAccessObject) { this.watchlistDataAccessInterface = watchlistDataAccessInterface; this.watchlistPresenter = watchlistPresenter; + this.dbUserDataAccessObject = dbUserDataAccessObject; } public void search(SearchInputData searchInputData) { @@ -40,9 +45,13 @@ public void search(SearchInputData searchInputData) { watchlistPresenter.prepareSuccessView(searchOutputData); } - @Override - public void getWatchListData() { + public Stock getStockData(String stockCode) { + return dbUserDataAccessObject.getStock(stockCode); + } + @Override + public void updateWatchlist(ArrayList stocks) { + System.out.println("Watchlist updated with " + stocks.size() + " stocks."); } } \ No newline at end of file diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 01e4199ea..544f56ccb 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -1,5 +1,6 @@ package view; +import entity.Stock; import view.HomeViewComponents.ScrollablePanel; import data_access.DBUserDataAccessObject; @@ -21,13 +22,11 @@ public class WatchListView extends JPanel implements PropertyChangeListener { private final ViewManagerModel viewManagerModel; private final WatchListViewModel watchListViewModel; - private final DBUserDataAccessObject dbUserDataAccessObject; private final ScrollablePanel contentPanel = new ScrollablePanel(); private WatchlistController watchlistController; - public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel, DBUserDataAccessObject dbUserDataAccessObject) { + public WatchListView(WatchListViewModel viewModel, ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; - this.dbUserDataAccessObject = dbUserDataAccessObject; setLayout(new BorderLayout()); setBackground(Color.WHITE); this.watchListViewModel = viewModel; @@ -59,10 +58,6 @@ public void actionPerformed(ActionEvent e) { contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(Color.WHITE); -// addStockItem(contentPanel, "AAPL", "$226.97", "−$0.26", "+0.11%", "+$44.09"); -// addStockItem(contentPanel, "COST", "$953.20", "+$39.27", "+4.30%", "+$386.00"); -// addStockItem(contentPanel, "QQQ", "$514.07", "+$0.56", "+0.60%", "+$141.25"); - JScrollPane scrollPane = contentPanel.wrapInScrollPane(); scrollPane.setBorder(BorderFactory.createEmptyBorder()); add(scrollPane, BorderLayout.CENTER); @@ -152,7 +147,7 @@ public void propertyChange(PropertyChangeEvent evt) { } } - private void updateWatchList(ArrayList updatedWatchList) { + private void updateWatchList(ArrayList updatedWatchList) { contentPanel.removeAll(); createWatchListview(updatedWatchList); contentPanel.revalidate(); @@ -170,16 +165,15 @@ private void goToStockInformation(String code) { viewManagerModel.pushView("StockView"); } - private void createWatchListview(ArrayList WatchList) { - for (String stockcode: WatchList) { -// final Stock stock = dbUserDataAccessObject.getStock(stockcode); -// final String price = Double.toString(stock.getClosePrice()); -// final String dailyChange = Double.toString(stock.getDailyChange()); -// final String dailyPercentage = Double.toString((stock.getDailyChange() / stock.getOpenPrice()) * 100) + "%"; -// final String volume = Double.toString(stock.getVolume()); + private void createWatchListview(ArrayList WatchList) { + for (Stock stock : WatchList) { + final String code = stock.getSymbol(); + final String price = "$" + stock.getClosePrice(); + final String dailyChange = String.valueOf(stock.getDailyChange()); + final String dailyPercentage = stock.getDailyPercentage() + "%"; + final String volume = String.valueOf(stock.getVolume()); -// addStockItem(contentPanel, stock.getSymbol(), "$" + price, dailyChange, dailyPercentage, volume); - addStockItem(contentPanel, stockcode, "$" + "123", "+$" + "234", "0.45%", "100000.23"); + addStockItem(contentPanel, code, price, dailyChange, dailyPercentage, volume); } } @@ -187,9 +181,8 @@ public String getViewName() { return "WatchListView"; } - public void setwatchlistController(WatchlistController controller) { - this.watchlistController = controller; - controller.getWatchList(); + public void setwatchlistController(WatchlistController watchlistController) { + this.watchlistController = watchlistController; } } From 187686158be0753c6b6e67b6aadae374f234447a Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sat, 30 Nov 2024 22:08:32 -0500 Subject: [PATCH 090/113] Update watchlist.txt --- src/main/java/data_access/watchlist.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index 31021bcbb..f2202784b 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -NVDA \ No newline at end of file +AAPL,COST,NVDA From ce9c3f29fb3d25841ef625fec59d75148988c805 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 1 Dec 2024 14:12:08 -0500 Subject: [PATCH 091/113] checkstyle --- .../data_access/FileUserDataAccessObject.java | 24 ++++++++++++++++--- src/main/java/use_case/buy/BuyInputData.java | 6 +++-- src/main/java/use_case/buy/BuyInteractor.java | 1 - src/main/java/view/BuyView.java | 22 ++++++++--------- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 326679c4a..552364adf 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -36,6 +36,7 @@ public class FileUserDataAccessObject implements WatchListDataAccessInterface, W private boolean userIsLoggedIn; + // Constructor prepared for userIsLoggedIn data. public FileUserDataAccessObject() { } @@ -207,7 +208,14 @@ public void removeFromPortfolioList(SimulatedHolding simulatedHolding) { savePortfolioList(); } - // Method to get the login status of the user + /** + * Determines if the user is currently logged in by checking the presence and content of a specific file. + * This method reads the login status from a file and returns true if the status indicates the user is logged in. + * If the file does not exist, it assumes the user is not logged in and saves this default status. + * + * @return true if the file exists and contains 'true', false otherwise. + * @throws RuntimeException if reading the file fails. + */ public boolean isUserLoggedIn() { // Check whether the file exists final File file = new File(mainFilePath + userIsLoggedInFilePath); @@ -229,7 +237,12 @@ public boolean isUserLoggedIn() { return userIsLoggedIn; } - // Method to save the login status of the user + /** + * Saves the current login status of the user to a file. + * This method writes the user's login status as a string ('true' or 'false') to a designated file. + * + * @throws RuntimeException if writing to the file fails. + */ public void saveUserLoginStatus() { try (BufferedWriter writer = new BufferedWriter(new FileWriter(mainFilePath + userIsLoggedInFilePath, false))) { // Converts a Boolean value to a string and writes it to a file @@ -239,7 +252,12 @@ public void saveUserLoginStatus() { } } - // Change the login status of the user + /** + * Updates and persists the login status of the user. + * This method sets the user's login status to the specified value and saves it to a file. + * + * @param loggedIn The new login status to set. true if the user is logging in, false otherwise. + */ public void setUserLoggedIn(boolean loggedIn) { this.userIsLoggedIn = loggedIn; saveUserLoginStatus(); diff --git a/src/main/java/use_case/buy/BuyInputData.java b/src/main/java/use_case/buy/BuyInputData.java index 8a4c2a370..45569812e 100644 --- a/src/main/java/use_case/buy/BuyInputData.java +++ b/src/main/java/use_case/buy/BuyInputData.java @@ -15,7 +15,9 @@ public BuyInputData(String symbol, double price, double quantity) { this.quantity = quantity; } - public String getSymbol() { return symbol; } + public String getSymbol() { + return symbol; + } public double getPrice() { return price; @@ -25,4 +27,4 @@ public double getQuantity() { return quantity; } -} \ No newline at end of file +} diff --git a/src/main/java/use_case/buy/BuyInteractor.java b/src/main/java/use_case/buy/BuyInteractor.java index d02617b90..6a7ddffb7 100644 --- a/src/main/java/use_case/buy/BuyInteractor.java +++ b/src/main/java/use_case/buy/BuyInteractor.java @@ -2,7 +2,6 @@ import entity.CommonSimulatedHoldingFactory; import entity.SimulatedHolding; -import entity.Stock; import use_case.portfolio.PortfolioDataAccessInterface; /** diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index 72c0bf630..9115c53b3 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -1,15 +1,17 @@ package view; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.*; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; @@ -41,26 +43,20 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; this.buyViewModel = viewModel; this.buyViewModel.addPropertyChangeListener(this); - setLayout(new BorderLayout()); setBackground(Color.WHITE); - final JLabel title = new JLabel("Buy Screen"); title.setAlignmentX(Component.CENTER_ALIGNMENT); - final LabelTextPanel priceInfo = new LabelTextPanel( new JLabel("Price"), priceInputField); final LabelTextPanel quantityInfo = new LabelTextPanel( new JLabel("Quantity"), quantityInputField); - // Decide if the price can be changed by the user. priceInputField.setEditable(true); priceInputField.setText(buyViewModel.getState().getPrice()); - final JPanel buttons = new JPanel(); buy = new JButton("Buy"); buttons.add(buy); - cancel = new JButton("Cancel"); buttons.add(cancel); @@ -68,7 +64,7 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { new ActionListener() { public void actionPerformed(ActionEvent evt) { try { - double quantity = Double.parseDouble(quantityInputField.getText()); + final double quantity = Double.parseDouble(quantityInputField.getText()); if (quantity > 0) { final BuyState buyState = buyViewModel.getState(); // Get the price from the status, not from the input field @@ -76,10 +72,12 @@ public void actionPerformed(ActionEvent evt) { buyState.getSymbol(), Double.parseDouble(buyState.getPrice()), quantity); - } else { + } + else { JOptionPane.showMessageDialog(null, "Please enter a positive quantity."); } - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, "Please enter a valid quantity."); } } @@ -180,4 +178,4 @@ public void setBuyController(BuyController buyController) { public void setPrice() { this.priceInputField.setText(buyViewModel.getState().getPrice()); } -} \ No newline at end of file +} From 4f71cead5b014d54c39949a9c7ba1e3fd975e7bf Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 1 Dec 2024 16:30:35 -0500 Subject: [PATCH 092/113] checkstyle --- src/main/java/app/AppBuilder.java | 46 +++++-- .../data_access/FileUserDataAccessObject.java | 23 ++-- .../interface_adapter/ViewManagerModel.java | 2 +- .../buy_view/BuyController.java | 3 +- .../buy_view/BuyPresenter.java | 13 +- .../interface_adapter/buy_view/BuyState.java | 14 +- .../buy_view/BuyViewModel.java | 2 - .../login/LoginPresenter.java | 3 +- .../ChangePasswordInteractor.java | 4 +- .../java/use_case/login/LoginInteractor.java | 2 - .../use_case/signup/SignupInteractor.java | 4 +- src/main/java/view/BuyView.java | 120 ++++++++---------- src/main/java/view/HomeView.java | 39 +++++- src/main/java/view/LoggedInView.java | 1 - src/main/java/view/LoginView.java | 2 - 15 files changed, 157 insertions(+), 121 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index c92fb89af..8580e7b35 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -9,7 +9,12 @@ import data_access.DBUserDataAccessObject; import data_access.FileUserDataAccessObject; -import entity.*; +import entity.CommonSimulatedHoldingFactory; +import entity.CommonStockFactory; +import entity.CommonUserFactory; +import entity.SimulatedHoldingFactory; +import entity.StockFactory; +import entity.UserFactory; import interface_adapter.ViewManagerModel; import interface_adapter.buy_view.BuyController; import interface_adapter.buy_view.BuyPresenter; @@ -17,7 +22,9 @@ import interface_adapter.change_password.ChangePasswordController; import interface_adapter.change_password.ChangePasswordPresenter; import interface_adapter.change_password.LoggedInViewModel; -import interface_adapter.home_view.*; +import interface_adapter.home_view.HomeController; +import interface_adapter.home_view.HomePresenter; +import interface_adapter.home_view.HomeViewModel; import interface_adapter.home_view.WatchlistController; import interface_adapter.home_view.WatchlistPresenter; import interface_adapter.login.LoginController; @@ -41,7 +48,9 @@ import use_case.change_password.ChangePasswordInputBoundary; import use_case.change_password.ChangePasswordInteractor; import use_case.change_password.ChangePasswordOutputBoundary; -import use_case.home_view.*; +import use_case.home_view.HomeInputBoundary; +import use_case.home_view.HomeInteractor; +import use_case.home_view.HomeOutputBoundary; import use_case.home_view.WatchlistInputBoundary; import use_case.home_view.WatchlistInteractor; import use_case.home_view.WatchlistOutputBoundary; @@ -60,7 +69,15 @@ import use_case.stock.StockInputBoundary; import use_case.stock.StockInteractor; import use_case.stock.StockOutputBoundary; -import view.*; +import view.BuyView; +import view.HomeView; +import view.LoggedInView; +import view.LoginView; +import view.PortfolioView; +import view.SignupView; +import view.StockView; +import view.ViewManager; +import view.WatchListView; /** * The AppBuilder class is responsible for putting together the pieces of @@ -85,7 +102,8 @@ public class AppBuilder { // thought question: is the hard dependency below a problem? private final DBUserDataAccessObject dbUserDataAccessObject = new DBUserDataAccessObject(userFactory, stockFactory); - private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(simulatedHoldingFactory); + private final FileUserDataAccessObject fileUserDataAccessObject = + new FileUserDataAccessObject(simulatedHoldingFactory); private HomeView homeView; private HomeViewModel homeViewModel; @@ -127,7 +145,7 @@ public AppBuilder addHomeView() { */ public AppBuilder addSignupView() { signupViewModel = new SignupViewModel(); - signupView = new SignupView(signupViewModel,viewManagerModel); + signupView = new SignupView(signupViewModel, viewManagerModel); cardPanel.add(signupView, signupView.getViewName()); return this; } @@ -148,7 +166,7 @@ public AppBuilder addLoginView() { * @return this builder */ public AppBuilder addLoggedInView() { - loggedInViewModel = new LoggedInViewModel(); + loggedInViewModel = new LoggedInViewModel(); loggedInView = new LoggedInView(loggedInViewModel); cardPanel.add(loggedInView, loggedInView.getViewName()); return this; @@ -229,7 +247,8 @@ public AppBuilder addWatchlistUseCase() { viewManagerModel, stockViewModel, watchListViewModel); - final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary); + final WatchlistInputBoundary watchlistInteractor = + new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary); final WatchlistController controller = new WatchlistController(watchlistInteractor); watchListView.setwatchlistController(controller); @@ -298,6 +317,7 @@ public AppBuilder addLogoutUseCase() { loggedInView.setLogoutController(logoutController); return this; } + /** * Adds the Stock Use Case to the application. * @return this builder @@ -308,7 +328,8 @@ public AppBuilder addStockUseCase() { homeViewModel, watchListViewModel, viewManagerModel); - final StockInputBoundary stockInteractor = new StockInteractor(stockPresenter, fileUserDataAccessObject, fileUserDataAccessObject); + final StockInputBoundary stockInteractor = + new StockInteractor(stockPresenter, fileUserDataAccessObject, fileUserDataAccessObject); final StockController stockController = new StockController(stockInteractor); stockView.setStockController(stockController); @@ -330,9 +351,14 @@ public AppBuilder addBuyUseCase() { return this; } + /** + * Adds the Portfolio Use Case to the application. + * @return this builder + */ public AppBuilder addPortfolioUseCase() { final PortfolioOutputBoundary portfolioPresenter = new PortfolioPresenter(portfolioViewModel); - final PortfolioInputBoundary portfolioInteractor = new PortfolioInteractor(fileUserDataAccessObject, dbUserDataAccessObject ,portfolioPresenter); + final PortfolioInputBoundary portfolioInteractor = + new PortfolioInteractor(fileUserDataAccessObject, dbUserDataAccessObject, portfolioPresenter); final PortfolioController portfolioController = new PortfolioController(portfolioInteractor); portfolioView.setController(portfolioController); diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java index 552364adf..5417eb6bf 100644 --- a/src/main/java/data_access/FileUserDataAccessObject.java +++ b/src/main/java/data_access/FileUserDataAccessObject.java @@ -12,17 +12,15 @@ import entity.*; import use_case.buy.BuyUserDataAccessInterface; -import use_case.change_password.ChangePasswordUserDataAccessInterface; -import use_case.login.LoginUserDataAccessInterface; import use_case.portfolio.PortfolioDataAccessInterface; -import use_case.signup.SignupUserDataAccessInterface; import use_case.watchlist.WatchListDataAccessInterface; import use_case.watchlist.WatchListModifyDataAccessInterface; /** * DAO for user data implemented using a File to persist the data. */ -public class FileUserDataAccessObject implements WatchListDataAccessInterface, WatchListModifyDataAccessInterface, PortfolioDataAccessInterface, BuyUserDataAccessInterface { +public class FileUserDataAccessObject implements WatchListDataAccessInterface, + WatchListModifyDataAccessInterface, PortfolioDataAccessInterface, BuyUserDataAccessInterface { private final ArrayList watchList = new ArrayList<>(); private final ArrayList portfolioList = new ArrayList<>(); @@ -182,8 +180,8 @@ public void addToPortfolioList(SimulatedHolding simulatedHolding) { for (SimulatedHolding data : new ArrayList<>(portfolioList)) { if (data.getSymbol().equals(simulatedHolding.getSymbol())) { - double newAmount = simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount(); - double newPrice = roundToOneDecimalPlace( + final double newAmount = simulatedHolding.getPurchaseAmount() + data.getPurchaseAmount(); + final double newPrice = roundToOneDecimalPlace( (simulatedHolding.getPurchasePrice() * simulatedHolding.getPurchaseAmount() + data.getPurchasePrice() * data.getPurchaseAmount()) / newAmount); @@ -226,10 +224,12 @@ public boolean isUserLoggedIn() { // Converts the read string to a Boolean value userIsLoggedIn = Boolean.parseBoolean(line); } - } catch (IOException ex) { + } + catch (IOException ex) { throw new RuntimeException(ex); } - } else { + } + else { // If the file does not exist, the default setting is not logged in userIsLoggedIn = false; saveUserLoginStatus(); @@ -247,7 +247,8 @@ public void saveUserLoginStatus() { try (BufferedWriter writer = new BufferedWriter(new FileWriter(mainFilePath + userIsLoggedInFilePath, false))) { // Converts a Boolean value to a string and writes it to a file writer.write(Boolean.toString(userIsLoggedIn)); - } catch (IOException ex) { + } + catch (IOException ex) { throw new RuntimeException(ex); } } @@ -274,7 +275,3 @@ private double roundToOneDecimalPlace(double value) { .doubleValue(); } } - - - - diff --git a/src/main/java/interface_adapter/ViewManagerModel.java b/src/main/java/interface_adapter/ViewManagerModel.java index c01f1b643..9c2ab0290 100644 --- a/src/main/java/interface_adapter/ViewManagerModel.java +++ b/src/main/java/interface_adapter/ViewManagerModel.java @@ -47,4 +47,4 @@ public void popView() { this.firePropertyChanged(); } } -} \ No newline at end of file +} diff --git a/src/main/java/interface_adapter/buy_view/BuyController.java b/src/main/java/interface_adapter/buy_view/BuyController.java index d97feb9ff..b3ff73b38 100644 --- a/src/main/java/interface_adapter/buy_view/BuyController.java +++ b/src/main/java/interface_adapter/buy_view/BuyController.java @@ -2,8 +2,6 @@ import use_case.buy.BuyInputBoundary; import use_case.buy.BuyInputData; -import use_case.login.LoginInputBoundary; -import use_case.login.LoginInputData; /** * The controller for the Buy Use Case. @@ -18,6 +16,7 @@ public BuyController(BuyInputBoundary buyUseCaseInteractor) { /** * Executes the Buy Use Case. + * @param symbol the symbol of the stock * @param price the price of the stock * @param quantity the quantity of the stock */ diff --git a/src/main/java/interface_adapter/buy_view/BuyPresenter.java b/src/main/java/interface_adapter/buy_view/BuyPresenter.java index 9db07f193..7c7d6f7c4 100644 --- a/src/main/java/interface_adapter/buy_view/BuyPresenter.java +++ b/src/main/java/interface_adapter/buy_view/BuyPresenter.java @@ -1,20 +1,15 @@ package interface_adapter.buy_view; -import entity.CommonSimulatedHoldingFactory; -import entity.SimulatedHolding; import interface_adapter.ViewManagerModel; -import interface_adapter.change_password.IsLoggedIn; -import interface_adapter.change_password.LoggedInState; import interface_adapter.home_view.HomeViewModel; -import interface_adapter.portfolio.PortfolioState; import interface_adapter.portfolio.PortfolioViewModel; import interface_adapter.stock_view.StockViewModel; import use_case.buy.BuyOutputBoundary; import use_case.buy.BuyOutputData; -import use_case.login.LoginOutputBoundary; -import use_case.login.LoginOutputData; -import view.BuyView; +/** + * The Presenter for the Buy Use Case. + */ public class BuyPresenter implements BuyOutputBoundary { private final BuyViewModel buyViewModel; @@ -53,4 +48,4 @@ public void switchToStockView() { viewManagerModel.pushView("StockView"); } -} \ No newline at end of file +} diff --git a/src/main/java/interface_adapter/buy_view/BuyState.java b/src/main/java/interface_adapter/buy_view/BuyState.java index 1fc64cb96..ea62e9642 100644 --- a/src/main/java/interface_adapter/buy_view/BuyState.java +++ b/src/main/java/interface_adapter/buy_view/BuyState.java @@ -12,17 +12,23 @@ public String getPrice() { return price; } - public String getQunatity() { + public String getQuantity() { return quantity; } - public String getSymbol() { return symbol; } + public String getSymbol() { + return symbol; + } public void setPrice(String price) { this.price = price; } - public void setQuantity(String qunatity) { this.quantity = qunatity; } + public void setQuantity(String qunatity) { + this.quantity = qunatity; + } - public void setSymbol(String symbol) { this.symbol = symbol; } + public void setSymbol(String symbol) { + this.symbol = symbol; + } } diff --git a/src/main/java/interface_adapter/buy_view/BuyViewModel.java b/src/main/java/interface_adapter/buy_view/BuyViewModel.java index 3d5e66e62..f04f8e665 100644 --- a/src/main/java/interface_adapter/buy_view/BuyViewModel.java +++ b/src/main/java/interface_adapter/buy_view/BuyViewModel.java @@ -1,8 +1,6 @@ package interface_adapter.buy_view; import interface_adapter.ViewModel; -import interface_adapter.signup.SignupState; -import interface_adapter.stock_view.StockViewState; /** * The ViewModel for the Buy View. diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java index c11b1320e..f373c6a98 100644 --- a/src/main/java/interface_adapter/login/LoginPresenter.java +++ b/src/main/java/interface_adapter/login/LoginPresenter.java @@ -2,14 +2,13 @@ import data_access.FileUserDataAccessObject; import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.IsLoggedIn; import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; -import interface_adapter.change_password.IsLoggedIn; import interface_adapter.home_view.HomeViewModel; import interface_adapter.signup.SignupViewModel; import use_case.login.LoginOutputBoundary; import use_case.login.LoginOutputData; -import use_case.login.LoginUserDataAccessInterface; /** * The Presenter for the Login Use Case. diff --git a/src/main/java/use_case/change_password/ChangePasswordInteractor.java b/src/main/java/use_case/change_password/ChangePasswordInteractor.java index 03dabcd7d..de048ee39 100644 --- a/src/main/java/use_case/change_password/ChangePasswordInteractor.java +++ b/src/main/java/use_case/change_password/ChangePasswordInteractor.java @@ -1,10 +1,10 @@ package use_case.change_password; +import java.util.ArrayList; + import entity.User; import entity.UserFactory; -import java.util.ArrayList; - /** * The Change Password Interactor. */ diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java index 8e6575742..27fa4bc92 100644 --- a/src/main/java/use_case/login/LoginInteractor.java +++ b/src/main/java/use_case/login/LoginInteractor.java @@ -2,8 +2,6 @@ import data_access.FileUserDataAccessObject; import entity.User; -import interface_adapter.change_password.IsLoggedIn; -import view.HomeView; /** * The Login Interactor. diff --git a/src/main/java/use_case/signup/SignupInteractor.java b/src/main/java/use_case/signup/SignupInteractor.java index f44f039fa..5f95f8ec7 100644 --- a/src/main/java/use_case/signup/SignupInteractor.java +++ b/src/main/java/use_case/signup/SignupInteractor.java @@ -1,10 +1,10 @@ package use_case.signup; +import java.util.ArrayList; + import entity.User; import entity.UserFactory; -import java.util.ArrayList; - /** * The Signup Interactor. */ diff --git a/src/main/java/view/BuyView.java b/src/main/java/view/BuyView.java index 9115c53b3..78237d5e7 100644 --- a/src/main/java/view/BuyView.java +++ b/src/main/java/view/BuyView.java @@ -30,28 +30,25 @@ public class BuyView extends JPanel implements ActionListener, PropertyChangeLis private final String viewName = "buy view"; private final ViewManagerModel viewManagerModel; private final BuyViewModel buyViewModel; - private final JTextField quantityInputField = new JTextField(15); private final JTextField priceInputField = new JTextField(15); - private final JButton buy; private final JButton cancel; - private BuyController buyController; - public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { + public BuyView(final BuyViewModel viewModel, final ViewManagerModel viewManagerModel) { this.viewManagerModel = viewManagerModel; this.buyViewModel = viewModel; this.buyViewModel.addPropertyChangeListener(this); + setLayout(new BorderLayout()); setBackground(Color.WHITE); final JLabel title = new JLabel("Buy Screen"); title.setAlignmentX(Component.CENTER_ALIGNMENT); - final LabelTextPanel priceInfo = new LabelTextPanel( - new JLabel("Price"), priceInputField); - final LabelTextPanel quantityInfo = new LabelTextPanel( - new JLabel("Quantity"), quantityInputField); - // Decide if the price can be changed by the user. + + final LabelTextPanel priceInfo = new LabelTextPanel(new JLabel("Price"), priceInputField); + final LabelTextPanel quantityInfo = new LabelTextPanel(new JLabel("Quantity"), quantityInputField); + priceInputField.setEditable(true); priceInputField.setText(buyViewModel.getState().getPrice()); final JPanel buttons = new JPanel(); @@ -60,122 +57,117 @@ public BuyView(BuyViewModel viewModel, ViewManagerModel viewManagerModel) { cancel = new JButton("Cancel"); buttons.add(cancel); - buy.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent evt) { - try { - final double quantity = Double.parseDouble(quantityInputField.getText()); - if (quantity > 0) { - final BuyState buyState = buyViewModel.getState(); - // Get the price from the status, not from the input field - buyController.execute( - buyState.getSymbol(), - Double.parseDouble(buyState.getPrice()), - quantity); - } - else { - JOptionPane.showMessageDialog(null, "Please enter a positive quantity."); - } - } - catch (NumberFormatException e) { - JOptionPane.showMessageDialog(null, "Please enter a valid quantity."); - } + setupBuyButton(); + setupCancelButton(); + setupQuantityInputFieldListener(); + setupPriceInputFieldListener(); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.add(title); + this.add(priceInfo); + this.add(quantityInfo); + this.add(buttons); + } + + private void setupBuyButton() { + buy.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + try { + final double quantity = Double.parseDouble(quantityInputField.getText()); + if (quantity > 0) { + final BuyState buyState = buyViewModel.getState(); + buyController.execute( + buyState.getSymbol(), + Double.parseDouble(buyState.getPrice()), + quantity); } + else { + JOptionPane.showMessageDialog(null, "Please enter a positive quantity."); + } + } + catch (final NumberFormatException event) { + JOptionPane.showMessageDialog(null, "Please enter a valid quantity."); } - ); + } + }); + } + private void setupCancelButton() { cancel.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent event) { buyController.switchToStockView(); } }); + } + private void setupQuantityInputFieldListener() { quantityInputField.getDocument().addDocumentListener(new DocumentListener() { - private void documentListenerHelper() { final BuyState currentState = buyViewModel.getState(); currentState.setQuantity(quantityInputField.getText()); buyViewModel.setState(currentState); } - @Override - public void insertUpdate(DocumentEvent e) { + @Override public void insertUpdate(DocumentEvent e) { documentListenerHelper(); } - @Override - public void removeUpdate(DocumentEvent e) { + @Override public void removeUpdate(DocumentEvent e) { documentListenerHelper(); } - @Override - public void changedUpdate(DocumentEvent e) { + @Override public void changedUpdate(DocumentEvent e) { documentListenerHelper(); } }); + } - this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - + private void setupPriceInputFieldListener() { priceInputField.getDocument().addDocumentListener(new DocumentListener() { - private void documentListenerHelper() { final BuyState currentState = buyViewModel.getState(); - currentState.setPrice(new String(priceInputField.getText())); + currentState.setPrice(priceInputField.getText()); buyViewModel.setState(currentState); } - @Override - public void insertUpdate(DocumentEvent e) { + @Override public void insertUpdate(DocumentEvent e) { documentListenerHelper(); } - @Override - public void removeUpdate(DocumentEvent e) { + @Override public void removeUpdate(DocumentEvent e) { documentListenerHelper(); } - @Override - public void changedUpdate(DocumentEvent e) { + @Override public void changedUpdate(DocumentEvent e) { documentListenerHelper(); } }); - - this.add(title); - this.add(priceInfo); - this.add(quantityInfo); - this.add(buttons); } /** * React to a button click that results in evt. * @param evt the ActionEvent to react to */ - public void actionPerformed(ActionEvent evt) { + public void actionPerformed(final ActionEvent evt) { System.out.println("Click " + evt.getActionCommand()); } @Override - public void propertyChange(PropertyChangeEvent evt) { + public void propertyChange(final PropertyChangeEvent evt) { final BuyState state = (BuyState) evt.getNewValue(); setFields(state); - setPrice(); } - private void setFields(BuyState state) { + private void setFields(final BuyState state) { priceInputField.setText(state.getPrice()); - quantityInputField.setText(state.getQunatity()); + quantityInputField.setText(state.getQuantity()); } public String getViewName() { return viewName; } - public void setBuyController(BuyController buyController) { + public void setBuyController(final BuyController buyController) { this.buyController = buyController; } - - public void setPrice() { - this.priceInputField.setText(buyViewModel.getState().getPrice()); - } } diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 72632a6d3..f0b5a33d9 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -185,7 +185,8 @@ public void actionPerformed(ActionEvent evt) { if (dataAccessObject.isUserLoggedIn()) { // If you are logged in, the logout dialog box is displayed showLogoutDialog(); - } else { + } + else { // If you are not logged in, switch to the login view homeController.switchToLoginView(); dataAccessObject.setUserLoggedIn(true); @@ -229,10 +230,21 @@ else if (evt.getPropertyName().equals("error")) { } } + /** + * Updates the style of a JLabel with the specified font size. + * + * @param label The JLabel to update. + * @param fontSize The desired font size. + */ public void updateLabelStyle(JLabel label, int fontSize) { label.setFont(new Font(FONTFAMILY, Font.BOLD, fontSize)); } + /** + * Updates the watch list components with the given list of stocks. + * + * @param watchList The list of stocks to display. + */ public void updateWatchListComponents(ArrayList watchList) { for (Stock stock : watchList) { final String symbol = stock.getSymbol(); @@ -243,7 +255,8 @@ public void updateWatchListComponents(ArrayList watchList) { } } - private void addWatchListItem(JPanel contentPanel, String code, String price, String dailyChange, String dailyPercentage) { + private void addWatchListItem(JPanel contentPanel, String code, + String price, String dailyChange, String dailyPercentage) { final JPanel stockPanel = new JPanel(new BorderLayout()); stockPanel.setBackground(Color.WHITE); @@ -283,7 +296,7 @@ public void actionPerformed(ActionEvent evt) { } }); -// rightPanel.add(viewStockButton); + // rightPanel.add(viewStockButton); // Add all to stockPanel stockPanel.add(leftPanel, BorderLayout.WEST); @@ -297,6 +310,11 @@ public String getViewName() { return viewName; } + /** + * Sets the HomeController for this view. + * + * @param homeController The HomeController to set. + */ public void setHomeController(HomeController homeController) { this.homeController = homeController; @@ -304,8 +322,12 @@ public void setHomeController(HomeController homeController) { homeController.getWatchList(); } + /** + * Displays a logout confirmation dialog and logs out if confirmed. + */ public void showLogoutDialog() { - final int response = JOptionPane.showConfirmDialog(this, "Do you want to logout?", "Confirm", JOptionPane.YES_NO_OPTION); + final int response = JOptionPane.showConfirmDialog(this, + "Do you want to logout?", "Confirm", JOptionPane.YES_NO_OPTION); if (response == JOptionPane.YES_OPTION) { dataAccessObject.setUserLoggedIn(false); @@ -315,15 +337,22 @@ public void showLogoutDialog() { } } + /** + * Updates the text of the login button based on the login status. + */ public void changeLoginButtonText() { if (dataAccessObject.isUserLoggedIn()) { loginButton.setText("Log Out"); - } else { + } + else { loginButton.setText("Log In"); } homeViewModel.firePropertyChanged(); } + /** + * Displays a dialog indicating that login is required and switches to the login view. + */ public void showLoginRequiredDialog() { final Object[] options = {"Go to Login"}; final int response = JOptionPane.showOptionDialog( diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java index 2a3ac190b..f9662ea41 100644 --- a/src/main/java/view/LoggedInView.java +++ b/src/main/java/view/LoggedInView.java @@ -49,7 +49,6 @@ public LoggedInView(LoggedInViewModel loggedInViewModel) { final JLabel usernameInfo = new JLabel("Currently logged in: "); username = new JLabel(); - //123 final JPanel buttons = new JPanel(); logOut = new JButton("Log Out"); buttons.add(logOut); diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java index ea98143b7..89a502a54 100644 --- a/src/main/java/view/LoginView.java +++ b/src/main/java/view/LoginView.java @@ -12,12 +12,10 @@ import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; -import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import interface_adapter.ViewManagerModel; -import interface_adapter.change_password.IsLoggedIn; import interface_adapter.login.LoginController; import interface_adapter.login.LoginState; import interface_adapter.login.LoginViewModel; From 94373a45c7f83bf3a3accb4c2846197984230eb4 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 1 Dec 2024 16:34:58 -0500 Subject: [PATCH 093/113] fix conflict --- src/main/java/app/AppBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 8580e7b35..5f6c3e0df 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -247,8 +247,8 @@ public AppBuilder addWatchlistUseCase() { viewManagerModel, stockViewModel, watchListViewModel); - final WatchlistInputBoundary watchlistInteractor = - new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary); + final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, + watchlistOutputBoundary, dbUserDataAccessObject); final WatchlistController controller = new WatchlistController(watchlistInteractor); watchListView.setwatchlistController(controller); From 25d625e8a094019319310b754772410caea46459 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Sun, 1 Dec 2024 16:35:53 -0500 Subject: [PATCH 094/113] fix conflict --- src/main/java/app/AppBuilder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 5f6c3e0df..df588f409 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -247,8 +247,7 @@ public AppBuilder addWatchlistUseCase() { viewManagerModel, stockViewModel, watchListViewModel); - final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, - watchlistOutputBoundary, dbUserDataAccessObject); + final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary, dbUserDataAccessObject); final WatchlistController controller = new WatchlistController(watchlistInteractor); watchListView.setwatchlistController(controller); From b45d91ba936807b8a135b96de7dc4bee35ef84dc Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:14:53 -0500 Subject: [PATCH 095/113] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fc3435319..d2566502a 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,10 @@ To run this project, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the `src/main/java/app` directory: -3. Click the green arrow on the top right section in your IntelliJ.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) +3. Click the green arrow on the top right section in your IntelliJ.
+ +![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) + [Back to Top](#contents) From 31765cfc49e97a7b0f779bc75adfb839f01e5d1c Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:15:12 -0500 Subject: [PATCH 096/113] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index d2566502a..d67555412 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,7 @@ To run this project, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the `src/main/java/app` directory: -3. Click the green arrow on the top right section in your IntelliJ.
- -![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) +3. Click the green arrow on the top right section in your IntelliJ.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) [Back to Top](#contents) From 12918ff9234949c27eafd837b58586f05e1577e8 Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:16:16 -0500 Subject: [PATCH 097/113] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d67555412..e82f66101 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,9 @@ To run this project, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the `src/main/java/app` directory: -3. Click the green arrow on the top right section in your IntelliJ.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) +3. Click the green arrow on the top right section in your IntelliJ. +image + [Back to Top](#contents) From 9a529e498f198a9afd0d92dc5659f21eceddda3a Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:21:57 -0500 Subject: [PATCH 098/113] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e82f66101..c8428f861 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,10 @@ To run this project, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the `src/main/java/app` directory: -3. Click the green arrow on the top right section in your IntelliJ. -image +3. Click the green arrow on the top right section in your IntelliJ.image + + + From 3155ef2a957052ff8894d92ab9ffca8163132cd8 Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:48:35 -0500 Subject: [PATCH 099/113] Update README.md --- README.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c8428f861..d2200db14 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,9 @@ - Yitao Huang - Ryan Jin -[Back to Top](#contents) - ## Project Description Introducing our Stock Marketplace Application, a user-friendly platform designed for both casual viewers and active investors. With this app, users can quickly look up stock information by entering ticker symbols without needing to log in, providing immediate access to essential market data. For personalized experience, users can create an account to build a watchlist of their favorite stocks, making it easy to track preferred investments. Additionally, the application features a simulated holdings section where logged-in users can add stocks along with their purchase amounts and prices, allowing for real-time calculations of day gains and total gains. -[Back to Top](#contents) - ## Features - Users can search stock information by entering stock symbol. - Users can add favourite stock to their watchlists. @@ -32,8 +28,6 @@ Introducing our Stock Marketplace Application, a user-friendly platform designed - Users can sign up by entering their usernames and passwords. - Users can log in by entering their usernames and passwords. -[Back to Top](#contents) - ## Installation and Usage Instructions To run this project, follow these steps: @@ -41,23 +35,17 @@ To run this project, follow these steps: 2. Navigate to the `src/main/java/app` directory: 3. Click the green arrow on the top right section in your IntelliJ.image +## Screenshots - - - - -[Back to Top](#contents) +截屏2024-12-01 18 31 51 +截屏2024-12-01 18 32 04 ## Feedbacks You are welcomed to submit an issue! -[Back to Top](#contents) - ## Contributions Any pull requests are welcomed! 💖 -[Back to Top](#contents) - ## License Stock Marketplace is under CC0-1.0 license. See the LICENSE file for more info. From 63f5d219d00e0ee6f760cd1c4c9efc3986984acd Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:50:27 -0500 Subject: [PATCH 100/113] Update README.md --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2200db14..4eb01dfb0 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,18 @@ To run this project, follow these steps: ## Screenshots -截屏2024-12-01 18 31 51 -截屏2024-12-01 18 32 04 + + + + + +
+ 截屏2024-12-01 18 31 51 +

Homepage

+
+ 截屏2024-12-01 18 32 04 +

Purchase Page

+
## Feedbacks You are welcomed to submit an issue! From 8cfcfea93d7ecb232ccfe4cb29f91b4d77eb39bc Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Mon, 2 Dec 2024 14:34:41 -0500 Subject: [PATCH 101/113] fix --- src/main/java/app/AppBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index df588f409..d057835fd 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -247,7 +247,7 @@ public AppBuilder addWatchlistUseCase() { viewManagerModel, stockViewModel, watchListViewModel); - final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary, dbUserDataAccessObject); + final WatchlistInputBoundary watchlistInteractor = new WatchlistInteractor(dbUserDataAccessObject, watchlistOutputBoundary); final WatchlistController controller = new WatchlistController(watchlistInteractor); watchListView.setwatchlistController(controller); From 86dbf5ec84af287fab072a48d9f1d8d1d9236a4c Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 2 Dec 2024 14:41:06 -0500 Subject: [PATCH 102/113] update test use case, and fix minor bugs --- src/main/java/data_access/portfolio.txt | 1 + src/main/java/data_access/watchlist.txt | 2 +- .../use_case/home_view/HomeInteractor.java | 33 +- .../portfolio/PortfolioInteractor.java | 2 +- src/main/java/view/HomeView.java | 7 +- src/main/java/view/PortfolioView.java | 1 + src/main/java/view/StockView.java | 4 +- .../home_view/HomeViewInteratorTest.java | 407 +++++++++++++++++- .../stock_view/StockViewInteractorTest.java | 284 ++++++++++++ 9 files changed, 715 insertions(+), 26 deletions(-) create mode 100644 src/test/java/use_case/stock_view/StockViewInteractorTest.java diff --git a/src/main/java/data_access/portfolio.txt b/src/main/java/data_access/portfolio.txt index e69de29bb..9a6ec80ee 100644 --- a/src/main/java/data_access/portfolio.txt +++ b/src/main/java/data_access/portfolio.txt @@ -0,0 +1 @@ +NVDA,138.3,104.0, diff --git a/src/main/java/data_access/watchlist.txt b/src/main/java/data_access/watchlist.txt index f2202784b..12395cbeb 100644 --- a/src/main/java/data_access/watchlist.txt +++ b/src/main/java/data_access/watchlist.txt @@ -1 +1 @@ -AAPL,COST,NVDA +AAPL,COST,NVDA \ No newline at end of file diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 0a92f947e..c248abb4e 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -31,7 +31,16 @@ public HomeInteractor(HomeDataAccessInterface homeDataAccessInterface, @Override public void search(SearchInputData searchInputData) { - final String stockSymbol = searchInputData.getStockSymbol(); + // Default search symbol is NVDA + String stockSymbol = searchInputData.getStockSymbol(); + if (stockSymbol == null || stockSymbol.isEmpty()) { + stockSymbol = "NVDA"; + } + else { + stockSymbol = stockSymbol.toUpperCase(); + } + + // Search stock data based on symbol try { final Stock stock = homeDataAccessInterface.getStock(stockSymbol); final SearchOutputData searchOutputData = new SearchOutputData(stock, false); @@ -41,14 +50,7 @@ public void search(SearchInputData searchInputData) { err.printStackTrace(); homePresenter.prepareFailView("Failed to fetch stock data"); } -// // Default search symbol is NVDA -// String stockSymbol = searchInputData.getStockSymbol(); -// if (stockSymbol == null || stockSymbol.isEmpty()) { -// stockSymbol = "NVDA"; -// } -// else { -// stockSymbol = stockSymbol.toUpperCase(); -// } + // final StockFactory stockFactory = new CommonStockFactory(); // final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); // @@ -91,8 +93,14 @@ public void getWatchListData() { final ArrayList watchListData = watchListDataAccessInterface.getWatchList(); final ArrayList watchList = new ArrayList<>(); - final StockFactory stockFactory = new CommonStockFactory(); +// // Using real data +// for (String symbol : watchListData) { +// final Stock stockData = homeDataAccessInterface.getStock(symbol); +// watchList.add(stockData); +// } + // Using fake data + final StockFactory stockFactory = new CommonStockFactory(); int i = 0; for (String symbol : watchListData) { final Stock stock = stockFactory.create(symbol, i, i, 100002322, 500.1, 100.23); @@ -100,11 +108,6 @@ public void getWatchListData() { i += 1; } -// for (String symbol : watchListData) { -// final Stock stockData = homeDataAccessInterface.getStock(symbol); -// watchList.add(stockData); -// } - homePresenter.getWatchListData(watchList); } diff --git a/src/main/java/use_case/portfolio/PortfolioInteractor.java b/src/main/java/use_case/portfolio/PortfolioInteractor.java index 3ae93281b..8d5504ebb 100644 --- a/src/main/java/use_case/portfolio/PortfolioInteractor.java +++ b/src/main/java/use_case/portfolio/PortfolioInteractor.java @@ -25,7 +25,7 @@ public void getPortfolioListData() { final ArrayList simulatedHoldings = portfolioDataAccessObject.getPortfolioList(); final ArrayList stockList = new ArrayList<>(); -// // Get data from API +// // Using real data // for (SimulatedHolding simulatedHolding : simulatedHoldings) { // final Stock stock = homeDataAccessObject.getStock(simulatedHolding.getSymbol()); // stockList.add(stock); diff --git a/src/main/java/view/HomeView.java b/src/main/java/view/HomeView.java index 72632a6d3..9ba18e522 100644 --- a/src/main/java/view/HomeView.java +++ b/src/main/java/view/HomeView.java @@ -210,11 +210,8 @@ public void actionPerformed(ActionEvent evt) { public void propertyChange(PropertyChangeEvent evt) { final HomeState state = (HomeState) evt.getNewValue(); if (evt.getPropertyName().equals("getWatchList")) { - // We only display first three stocks - ArrayList watchList = state.getWatchList(); - if (watchList.size() != 3) { - watchList = new ArrayList(state.getWatchList().subList(0, 3)); - } + // Display watchlist data + final ArrayList watchList = state.getWatchList(); watchListContentPanel.removeAll(); updateWatchListComponents(watchList); diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index 9cf283cfb..be66cfe7b 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -187,6 +187,7 @@ public void propertyChange(PropertyChangeEvent evt) { final PortfolioState portfolioState = portfolioViewModel.getState(); final ArrayList portfolioList = portfolioState.getSimulatedHoldings(); final ArrayList stockList = portfolioState.getStocks(); + contentPanel.removeAll(); double totalValue = 0; double totalDailyChange = 0; diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 05c16c46f..5360b0d88 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -149,7 +149,7 @@ public void updateStockData(Stock stock) { volumeLabel.setText("Volume: " + volumeText); // 星星按钮的可见性 - updateFavoriteButtonVisibility(stock.getSymbol()); +// updateFavoriteButtonVisibility(stock.getSymbol()); } private void updateFavoriteButtonVisibility(String stockSymbol) { @@ -200,6 +200,8 @@ else if ("updateFavouriteButton".equals(evt.getPropertyName())) { // Toggle button text between filled and empty star favoriteButton.setText(stockViewState.getIsFavorite() ? "★" : "☆"); } + + // Update login state setButtonVisible(fileUserDataAccessObject.isUserLoggedIn()); } diff --git a/src/test/java/use_case/home_view/HomeViewInteratorTest.java b/src/test/java/use_case/home_view/HomeViewInteratorTest.java index fca1d8a1d..09fb330e4 100644 --- a/src/test/java/use_case/home_view/HomeViewInteratorTest.java +++ b/src/test/java/use_case/home_view/HomeViewInteratorTest.java @@ -5,7 +5,11 @@ import entity.*; import org.junit.Test; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -39,7 +43,7 @@ public void switchToPortfolio() { } @Override - public void switchToWatchList(ArrayList watchList) { + public void switchToWatchList(ArrayList watchListSymbols) { // Do nothing right now } @@ -95,7 +99,7 @@ public void switchToPortfolio() { } @Override - public void switchToWatchList(ArrayList watchList) { + public void switchToWatchList(ArrayList watchListSymbols) { // Do nothing right now } @@ -152,7 +156,7 @@ public void switchToPortfolio() { } @Override - public void switchToWatchList(ArrayList watchList) { + public void switchToWatchList(ArrayList watchListSymbols) { // Do nothing right now } @@ -180,4 +184,401 @@ public void deleteLocalData() { final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); interactor.search(inputData); } + + @Test + public void switchToPortfolioListViewTest() { + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToPortfolio() { + assert true; + } + + @Override + public void switchToWatchList(ArrayList watchListSymbols) { + // Do nothing right now + } + + @Override + public void switchToLoginView() { + // Do nothing right now + } + + @Override + public void switchToSignupView() { + // Do nothing right now + } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.switchToPortfolio(); + } + + @Test + public void switchToWatchListViewTest() { + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToPortfolio() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToWatchList(ArrayList watchListSymbols) { + // Do nothing right now + assert true; + } + + @Override + public void switchToLoginView() { + // Do nothing right now + } + + @Override + public void switchToSignupView() { + // Do nothing right now + } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.switchToWatchList(); + } + + @Test + public void switchToLoginViewTest() { + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToPortfolio() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToWatchList(ArrayList watchListSymbols) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToLoginView() { + // Do nothing right now + assert true; + } + + @Override + public void switchToSignupView() { + // Do nothing right now + } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.switchToLoginView(); + } + + @Test + public void switchToSignupViewTest() { + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToPortfolio() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToWatchList(ArrayList watchListSymbols) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToLoginView() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToSignupView() { + // Do nothing right now + assert true; + } + + @Override + public void getWatchListData(ArrayList watchList) { + // Do nothing + } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.switchToSignupView(); + } + + @Test + public void getWatchlistDataTest() { + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToPortfolio() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToWatchList(ArrayList watchListSymbols) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToLoginView() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToSignupView() { + // Do nothing right now + assert true; + } + + @Override + public void getWatchListData(ArrayList watchList) { + int expected = 3; + int actual = watchList.size(); + assertEquals(expected, actual); + } + + @Override + public void deleteLocalData() { + // Do nothing + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.getWatchListData(); + } + + @Test + public void deleteLocalDataTest() { + final UserFactory userFactory = new CommonUserFactory(); + final StockFactory stockFactory = new CommonStockFactory(); + final SimulatedHoldingFactory simulatedHoldingFactory = new CommonSimulatedHoldingFactory(); + final FileUserDataAccessObject watchListData = new FileUserDataAccessObject(simulatedHoldingFactory); + final DBUserDataAccessObject stockData = new DBUserDataAccessObject(userFactory, stockFactory); + + // This creates a successPresenter that tests whether the test case is as we expect. + HomeOutputBoundary successPresenter = new HomeOutputBoundary() { + @Override + public void prepareSuccessView(SearchOutputData searchOutputData) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToPortfolio() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToWatchList(ArrayList watchListSymbols) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToLoginView() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void switchToSignupView() { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void getWatchListData(ArrayList watchList) { + // this should never be reached since the test case should fail + fail("Use case success is unexpected."); + } + + @Override + public void deleteLocalData() { + int watchListSizeExpected = 3; + int watchListSizeActual = 0; + int portfolioListSizeExpected = 0; + int portfolioListSizeActual = 0; + + final String mainFilePath = "src/main/java/data_access/"; + final String watchListFilePath = "watchlist.txt"; + final String portfolioFilePath = "portfolio.txt"; + + // Using FileReader and BufferedReader to read the file + try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + portfolioFilePath))) { + + String line; + while ((line = reader.readLine()) != null) { + portfolioListSizeActual++; + } + } + catch (IOException ex) { + // Normally, this means the file doesn't exist + assert false; + } + + // Using FileReader and BufferedReader to read the file + try (BufferedReader reader = new BufferedReader(new FileReader(mainFilePath + watchListFilePath))) { + final String line = reader.readLine(); + if (line != null) { + watchListSizeActual = line.split(",").length; + } + } + catch (IOException ex) { + // Normally, this means the file doesn't exist + assert false; + } + + assertEquals(watchListSizeExpected, watchListSizeActual); + assertEquals(portfolioListSizeExpected, portfolioListSizeActual); + } + }; + + final HomeInputBoundary interactor = new HomeInteractor(stockData, watchListData, watchListData, successPresenter); + interactor.deleteLocalData(); + } } diff --git a/src/test/java/use_case/stock_view/StockViewInteractorTest.java b/src/test/java/use_case/stock_view/StockViewInteractorTest.java new file mode 100644 index 000000000..a29c590c3 --- /dev/null +++ b/src/test/java/use_case/stock_view/StockViewInteractorTest.java @@ -0,0 +1,284 @@ +package use_case.stock; + +import entity.Stock; +import entity.CommonStock; +import org.junit.jupiter.api.Test; +import use_case.watchlist.WatchListDataAccessInterface; +import use_case.watchlist.WatchListModifyDataAccessInterface; +import java.util.ArrayList; +import static org.junit.jupiter.api.Assertions.*; + +public class StockViewInteractorTest { + + @Test + public void buyStockTest() { + // Arrange + StockOutputBoundary stockPresenter = new StockOutputBoundary() { + @Override + public void presentBuyView(Stock stock) { + assertEquals("AAPL", stock.getSymbol()); + } + + @Override + public void presentAddToWatchlist(Stock stock) {} + + @Override + public void presentRemoveFromWatchlist(Stock stock) {} + + @Override + public void updateFavouriteButton(Boolean isFavourite) { + + } + + public void updateFavouriteButton(boolean isFavourite) {} + }; + WatchListDataAccessInterface watchListDataAccess = new WatchListDataAccessInterface() { + @Override + public ArrayList getWatchList() { + return new ArrayList<>(); + } + + @Override + public void saveWatchList() { + + } + + @Override + public void createWatchList() { + + } + }; + WatchListModifyDataAccessInterface watchListModifyDataAccess = new WatchListModifyDataAccessInterface() { + @Override + public void addToWatchList(String symbol) {} + + @Override + public void removeFromWatchList(String symbol) {} + }; + StockInteractor stockInteractor = new StockInteractor(stockPresenter, watchListDataAccess, watchListModifyDataAccess); + Stock stock = new CommonStock("AAPL", 100.0, 120.0, 1000.0, 130.0, 95.0); + + // Act + stockInteractor.buyStock(stock); + } + + @Test + public void toggleWatchlist_AddToWatchlistTest() { + // Arrange + StockOutputBoundary stockPresenter = new StockOutputBoundary() { + @Override + public void presentBuyView(Stock stock) {} + + @Override + public void presentAddToWatchlist(Stock stock) { + assertEquals("AAPL", stock.getSymbol()); + } + + @Override + public void presentRemoveFromWatchlist(Stock stock) {} + + @Override + public void updateFavouriteButton(Boolean isFavourite) { + + } + + public void updateFavouriteButton(boolean isFavourite) { + assertTrue(isFavourite); + } + }; + WatchListDataAccessInterface watchListDataAccess = new WatchListDataAccessInterface() { + @Override + public ArrayList getWatchList() { + return new ArrayList<>(); + } + + @Override + public void saveWatchList() { + + } + + @Override + public void createWatchList() { + + } + }; + WatchListModifyDataAccessInterface watchListModifyDataAccess = new WatchListModifyDataAccessInterface() { + @Override + public void addToWatchList(String symbol) { + assertEquals("AAPL", symbol); + } + + @Override + public void removeFromWatchList(String symbol) {} + }; + StockInteractor stockInteractor = new StockInteractor(stockPresenter, watchListDataAccess, watchListModifyDataAccess); + Stock stock = new CommonStock("AAPL", 100.0, 120.0, 1000.0, 130.0, 95.0); + + // Act + stockInteractor.toggleWatchlist(stock, true); + } + + @Test + public void toggleWatchlist_RemoveFromWatchlistTest() { + // Arrange + StockOutputBoundary stockPresenter = new StockOutputBoundary() { + @Override + public void presentBuyView(Stock stock) {} + + @Override + public void presentAddToWatchlist(Stock stock) {} + + @Override + public void presentRemoveFromWatchlist(Stock stock) { + assertEquals("AAPL", stock.getSymbol()); + } + + @Override + public void updateFavouriteButton(Boolean isFavourite) { + + } + + public void updateFavouriteButton(boolean isFavourite) { + assertFalse(isFavourite); + } + }; + WatchListDataAccessInterface watchListDataAccess = new WatchListDataAccessInterface() { + @Override + public ArrayList getWatchList() { + ArrayList watchList = new ArrayList<>(); + watchList.add("AAPL"); + return watchList; + } + + @Override + public void saveWatchList() { + + } + + @Override + public void createWatchList() { + + } + }; + WatchListModifyDataAccessInterface watchListModifyDataAccess = new WatchListModifyDataAccessInterface() { + @Override + public void addToWatchList(String symbol) {} + + @Override + public void removeFromWatchList(String symbol) { + assertEquals("AAPL", symbol); + } + }; + StockInteractor stockInteractor = new StockInteractor(stockPresenter, watchListDataAccess, watchListModifyDataAccess); + Stock stock = new CommonStock("AAPL", 100.0, 120.0, 1000.0, 130.0, 95.0); + + // Act + stockInteractor.toggleWatchlist(stock, true); + } + + @Test + public void toggleWatchlist_UpdateFavouriteButtonWithoutModifyingData_AddToWatchlistTest() { + // Arrange + StockOutputBoundary stockPresenter = new StockOutputBoundary() { + @Override + public void presentBuyView(Stock stock) {} + + @Override + public void presentAddToWatchlist(Stock stock) {} + + @Override + public void presentRemoveFromWatchlist(Stock stock) {} + + @Override + public void updateFavouriteButton(Boolean isFavourite) { + + } + + public void updateFavouriteButton(boolean isFavourite) { + assertFalse(isFavourite); + } + }; + WatchListDataAccessInterface watchListDataAccess = new WatchListDataAccessInterface() { + @Override + public ArrayList getWatchList() { + return new ArrayList<>(); + } + + @Override + public void saveWatchList() { + + } + + @Override + public void createWatchList() { + + } + }; + WatchListModifyDataAccessInterface watchListModifyDataAccess = new WatchListModifyDataAccessInterface() { + @Override + public void addToWatchList(String symbol) {} + + @Override + public void removeFromWatchList(String symbol) {} + }; + StockInteractor stockInteractor = new StockInteractor(stockPresenter, watchListDataAccess, watchListModifyDataAccess); + Stock stock = new CommonStock("AAPL", 100.0, 120.0, 1000.0, 130.0, 95.0); + + // Act + stockInteractor.toggleWatchlist(stock, false); + } + + @Test + public void toggleWatchlist_UpdateFavouriteButtonWithoutModifyingData_RemoveFromWatchlistTest() { + // Arrange + StockOutputBoundary stockPresenter = new StockOutputBoundary() { + @Override + public void presentBuyView(Stock stock) {} + + @Override + public void presentAddToWatchlist(Stock stock) {} + + @Override + public void presentRemoveFromWatchlist(Stock stock) {} + + @Override + public void updateFavouriteButton(Boolean isFavourite) { + + } + + public void updateFavouriteButton(boolean isFavourite) { + assertTrue(isFavourite); + } + }; + WatchListDataAccessInterface watchListDataAccess = new WatchListDataAccessInterface() { + @Override + public ArrayList getWatchList() { + ArrayList watchList = new ArrayList<>(); + watchList.add("AAPL"); + return watchList; + } + + @Override + public void saveWatchList() { + + } + + @Override + public void createWatchList() { + + } + }; + WatchListModifyDataAccessInterface watchListModifyDataAccess = new WatchListModifyDataAccessInterface() { + @Override + public void addToWatchList(String symbol) {} + + @Override + public void removeFromWatchList(String symbol) {} + }; + StockInteractor stockInteractor = new StockInteractor(stockPresenter, watchListDataAccess, watchListModifyDataAccess); + Stock stock = new CommonStock("AAPL", 100.0, 120.0, 1000.0, 130.0, 95.0); + + // Act + stockInteractor.toggleWatchlist(stock, false); + } +} \ No newline at end of file From e0f4594a81c10f5087d91f830317068e6e402e99 Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 2 Dec 2024 15:04:34 -0500 Subject: [PATCH 103/113] WatchList use API data --- src/main/java/use_case/home_view/HomeInteractor.java | 2 +- src/main/java/view/WatchListView.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 0a92f947e..d0f56bcf6 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -69,7 +69,7 @@ public void switchToWatchList() { final StockFactory stockFactory = new CommonStockFactory(); for (String symbol : watchListData) { - final Stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); + final Stock stock = homeDataAccessInterface.getStock(symbol); watchList.add(stock); } diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 544f56ccb..164ed90a4 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -121,7 +121,7 @@ public void mouseExited(java.awt.event.MouseEvent evt) { final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - final JLabel volumeLabel = new JLabel("Volume: " + volume); + final JLabel volumeLabel = new JLabel(volume); volumeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); rightPanel.add(Box.createVerticalGlue()); From 6e1026c7a4e512903a6c7066faf169c027598fd4 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 2 Dec 2024 15:16:01 -0500 Subject: [PATCH 104/113] Using fake data in main branch --- src/main/java/data_access/portfolio.txt | 2 ++ .../buy_view/BuyPresenter.java | 2 +- .../use_case/home_view/HomeInteractor.java | 35 +++++++++---------- src/main/java/view/PortfolioView.java | 7 ++-- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/main/java/data_access/portfolio.txt b/src/main/java/data_access/portfolio.txt index 9a6ec80ee..06c5eeab2 100644 --- a/src/main/java/data_access/portfolio.txt +++ b/src/main/java/data_access/portfolio.txt @@ -1 +1,3 @@ NVDA,138.3,104.0, +AAPL,322.1,123.0, +COST,322.1,123.0, diff --git a/src/main/java/interface_adapter/buy_view/BuyPresenter.java b/src/main/java/interface_adapter/buy_view/BuyPresenter.java index 9db07f193..2c97faec0 100644 --- a/src/main/java/interface_adapter/buy_view/BuyPresenter.java +++ b/src/main/java/interface_adapter/buy_view/BuyPresenter.java @@ -34,7 +34,7 @@ public BuyPresenter(BuyViewModel buyViewModel, PortfolioViewModel portfolioViewM @Override public void prepareSuccessView(BuyOutputData response) { - portfolioViewModel.firePropertyChanged("getPortfolioList"); + portfolioViewModel.firePropertyChanged("addNewStock"); switchToHomeView(); } diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index c248abb4e..2fe5a4620 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -40,22 +40,23 @@ public void search(SearchInputData searchInputData) { stockSymbol = stockSymbol.toUpperCase(); } - // Search stock data based on symbol - try { - final Stock stock = homeDataAccessInterface.getStock(stockSymbol); - final SearchOutputData searchOutputData = new SearchOutputData(stock, false); - homePresenter.prepareSuccessView(searchOutputData); - } - catch (Exception err) { - err.printStackTrace(); - homePresenter.prepareFailView("Failed to fetch stock data"); - } +// // Using real data to search stock data based on symbol +// try { +// final Stock stock = homeDataAccessInterface.getStock(stockSymbol); +// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); +// homePresenter.prepareSuccessView(searchOutputData); +// } +// catch (Exception err) { +// err.printStackTrace(); +// homePresenter.prepareFailView("Failed to fetch stock data"); +// } + + // Using fake data to search stock data based on symbol + final StockFactory stockFactory = new CommonStockFactory(); + final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); -// final StockFactory stockFactory = new CommonStockFactory(); -// final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); -// -// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); -// homePresenter.prepareSuccessView(searchOutputData); + final SearchOutputData searchOutputData = new SearchOutputData(stock, false); + homePresenter.prepareSuccessView(searchOutputData); } @Override @@ -101,11 +102,9 @@ public void getWatchListData() { // Using fake data final StockFactory stockFactory = new CommonStockFactory(); - int i = 0; for (String symbol : watchListData) { - final Stock stock = stockFactory.create(symbol, i, i, 100002322, 500.1, 100.23); + final Stock stock = stockFactory.create(symbol, 0, 0, 100002322, 500.1, 100.23); watchList.add(stock); - i += 1; } homePresenter.getWatchListData(watchList); diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index be66cfe7b..80fd221e7 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -124,8 +124,8 @@ private void addStockItem(String code, double closePrice, double purchasePrice, double currentValue = closePrice * amount; double dailyChange = (closePrice - openPrice) * amount; double allTimeChange = (closePrice - purchasePrice) * amount; - double dailyPercentage = openPrice != 0 ? ((closePrice - openPrice) / openPrice) * 100 : 0; - double allTimePercentage = purchasePrice != 0 ? ((closePrice - purchasePrice) / purchasePrice) * 100 : 0; + double dailyPercentage = openPrice != 0 ? ((closePrice - openPrice) / openPrice) * 100 : 100; + double allTimePercentage = purchasePrice != 0 ? ((closePrice - purchasePrice) / purchasePrice) * 100 : 100; // Left part: Stock code and price final JPanel leftPanel = new JPanel(); @@ -210,5 +210,8 @@ public void propertyChange(PropertyChangeEvent evt) { contentPanel.revalidate(); contentPanel.repaint(); } + else if (evt.getPropertyName().equals("addNewStock")) { + portfolioController.getPortfolioList(); + } } } From 284d5739480bfefd8d93be51afc096ee437c65b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 2 Dec 2024 15:26:22 -0500 Subject: [PATCH 105/113] add fake data for watchlist --- src/main/java/use_case/home_view/HomeInteractor.java | 5 +++++ src/main/java/view/WatchListView.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 2fe5a4620..17c136fb1 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -72,7 +72,12 @@ public void switchToWatchList() { final StockFactory stockFactory = new CommonStockFactory(); for (String symbol : watchListData) { +// // Using real data +// final Stock stock = homeDataAccessInterface.getStock(symbol); + + // Using fake data final Stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); + watchList.add(stock); } diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 544f56ccb..164ed90a4 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -121,7 +121,7 @@ public void mouseExited(java.awt.event.MouseEvent evt) { final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - final JLabel volumeLabel = new JLabel("Volume: " + volume); + final JLabel volumeLabel = new JLabel(volume); volumeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); rightPanel.add(Box.createVerticalGlue()); From 5fcc10806d5830d70b7314a4f9a825a555dd0001 Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:31:05 -0500 Subject: [PATCH 106/113] Update HomeInteractor.java --- src/main/java/use_case/home_view/HomeInteractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index d0f56bcf6..52dab6d19 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -69,7 +69,7 @@ public void switchToWatchList() { final StockFactory stockFactory = new CommonStockFactory(); for (String symbol : watchListData) { - final Stock stock = homeDataAccessInterface.getStock(symbol); + final stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); watchList.add(stock); } From 03f9e30de4291d06ddbf33e3f5b3c9f8a98e1694 Mon Sep 17 00:00:00 2001 From: Reiuy <52073159+Re1uy@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:31:32 -0500 Subject: [PATCH 107/113] Update HomeInteractor.java --- src/main/java/use_case/home_view/HomeInteractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 52dab6d19..0a92f947e 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -69,7 +69,7 @@ public void switchToWatchList() { final StockFactory stockFactory = new CommonStockFactory(); for (String symbol : watchListData) { - final stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); + final Stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); watchList.add(stock); } From 6c467b14c9bf27665d8b5438bc894c61869af86f Mon Sep 17 00:00:00 2001 From: misaka Date: Mon, 2 Dec 2024 15:39:09 -0500 Subject: [PATCH 108/113] WatchList UI change --- src/main/java/view/WatchListView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 164ed90a4..8a1d7ad79 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -119,10 +119,14 @@ public void mouseExited(java.awt.event.MouseEvent evt) { rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); rightPanel.setBackground(Color.WHITE); + rightPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); + final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + dailyChangeLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); final JLabel volumeLabel = new JLabel(volume); volumeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); + volumeLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); rightPanel.add(Box.createVerticalGlue()); rightPanel.add(dailyChangeLabel); From 87da04dc8abf4184f0bc114f096f38e7e70cdd0e Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 2 Dec 2024 15:39:17 -0500 Subject: [PATCH 109/113] add debugMode variable for debuging purpose --- src/main/java/entity/DebugMode.java | 5 ++ .../use_case/home_view/HomeInteractor.java | 77 +++++++++++-------- .../portfolio/PortfolioInteractor.java | 42 +++++----- src/main/java/view/WatchListView.java | 2 +- 4 files changed, 73 insertions(+), 53 deletions(-) create mode 100644 src/main/java/entity/DebugMode.java diff --git a/src/main/java/entity/DebugMode.java b/src/main/java/entity/DebugMode.java new file mode 100644 index 000000000..bd59ad5c4 --- /dev/null +++ b/src/main/java/entity/DebugMode.java @@ -0,0 +1,5 @@ +package entity; + +public class DebugMode { + public static boolean debugMode = true; +} diff --git a/src/main/java/use_case/home_view/HomeInteractor.java b/src/main/java/use_case/home_view/HomeInteractor.java index 17c136fb1..7b27eee7a 100644 --- a/src/main/java/use_case/home_view/HomeInteractor.java +++ b/src/main/java/use_case/home_view/HomeInteractor.java @@ -1,6 +1,7 @@ package use_case.home_view; import entity.CommonStockFactory; +import entity.DebugMode; import entity.Stock; import entity.StockFactory; import use_case.portfolio.PortfolioDataAccessInterface; @@ -40,23 +41,26 @@ public void search(SearchInputData searchInputData) { stockSymbol = stockSymbol.toUpperCase(); } -// // Using real data to search stock data based on symbol -// try { -// final Stock stock = homeDataAccessInterface.getStock(stockSymbol); -// final SearchOutputData searchOutputData = new SearchOutputData(stock, false); -// homePresenter.prepareSuccessView(searchOutputData); -// } -// catch (Exception err) { -// err.printStackTrace(); -// homePresenter.prepareFailView("Failed to fetch stock data"); -// } - - // Using fake data to search stock data based on symbol - final StockFactory stockFactory = new CommonStockFactory(); - final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); + if (DebugMode.debugMode) { + // Using fake data to search stock data based on symbol + final StockFactory stockFactory = new CommonStockFactory(); + final Stock stock = stockFactory.create(stockSymbol, 128.2, 322.1, 100002322, 500.1, 100.23); - final SearchOutputData searchOutputData = new SearchOutputData(stock, false); - homePresenter.prepareSuccessView(searchOutputData); + final SearchOutputData searchOutputData = new SearchOutputData(stock, false); + homePresenter.prepareSuccessView(searchOutputData); + } + else { + // Using real data to search stock data based on symbol + try { + final Stock stock = homeDataAccessInterface.getStock(stockSymbol); + final SearchOutputData searchOutputData = new SearchOutputData(stock, false); + homePresenter.prepareSuccessView(searchOutputData); + } + catch (Exception err) { + err.printStackTrace(); + homePresenter.prepareFailView("Failed to fetch stock data"); + } + } } @Override @@ -72,13 +76,17 @@ public void switchToWatchList() { final StockFactory stockFactory = new CommonStockFactory(); for (String symbol : watchListData) { -// // Using real data -// final Stock stock = homeDataAccessInterface.getStock(symbol); - - // Using fake data - final Stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); + if (DebugMode.debugMode) { + // Using fake data + final Stock stock = stockFactory.create(symbol, 0.0, 0.0, 0, 0.0, 0.0); + watchList.add(stock); + } + else { + // Using real data + final Stock stock = homeDataAccessInterface.getStock(symbol); + watchList.add(stock); + } - watchList.add(stock); } homePresenter.switchToWatchList(watchList); @@ -99,17 +107,20 @@ public void getWatchListData() { final ArrayList watchListData = watchListDataAccessInterface.getWatchList(); final ArrayList watchList = new ArrayList<>(); -// // Using real data -// for (String symbol : watchListData) { -// final Stock stockData = homeDataAccessInterface.getStock(symbol); -// watchList.add(stockData); -// } - - // Using fake data - final StockFactory stockFactory = new CommonStockFactory(); - for (String symbol : watchListData) { - final Stock stock = stockFactory.create(symbol, 0, 0, 100002322, 500.1, 100.23); - watchList.add(stock); + if (DebugMode.debugMode) { + // Using fake data + final StockFactory stockFactory = new CommonStockFactory(); + for (String symbol : watchListData) { + final Stock stock = stockFactory.create(symbol, 0, 0, 100002322, 500.1, 100.23); + watchList.add(stock); + } + } + else { + // Using real data + for (String symbol : watchListData) { + final Stock stockData = homeDataAccessInterface.getStock(symbol); + watchList.add(stockData); + } } homePresenter.getWatchListData(watchList); diff --git a/src/main/java/use_case/portfolio/PortfolioInteractor.java b/src/main/java/use_case/portfolio/PortfolioInteractor.java index 8d5504ebb..941110024 100644 --- a/src/main/java/use_case/portfolio/PortfolioInteractor.java +++ b/src/main/java/use_case/portfolio/PortfolioInteractor.java @@ -1,6 +1,7 @@ package use_case.portfolio; import entity.CommonStockFactory; +import entity.DebugMode; import entity.SimulatedHolding; import entity.Stock; import use_case.home_view.HomeDataAccessInterface; @@ -25,25 +26,28 @@ public void getPortfolioListData() { final ArrayList simulatedHoldings = portfolioDataAccessObject.getPortfolioList(); final ArrayList stockList = new ArrayList<>(); -// // Using real data -// for (SimulatedHolding simulatedHolding : simulatedHoldings) { -// final Stock stock = homeDataAccessObject.getStock(simulatedHolding.getSymbol()); -// stockList.add(stock); -// } - - // Using fake data - int i = 100; - final CommonStockFactory stockFactory = new CommonStockFactory(); - for (SimulatedHolding simulatedHolding : simulatedHoldings) { - final Stock stock = stockFactory.create(simulatedHolding.getSymbol(), - 0, - i + 50, - 10000000.2, - 0, - 0); - stockList.add(stock); - - i += 100; + if (DebugMode.debugMode) { + // Using fake data + int i = 100; + final CommonStockFactory stockFactory = new CommonStockFactory(); + for (SimulatedHolding simulatedHolding : simulatedHoldings) { + final Stock stock = stockFactory.create(simulatedHolding.getSymbol(), + 0, + i + 50, + 10000000.2, + 0, + 0); + stockList.add(stock); + + i += 100; + } + } + else { + // Using real data + for (SimulatedHolding simulatedHolding : simulatedHoldings) { + final Stock stock = homeDataAccessObject.getStock(simulatedHolding.getSymbol()); + stockList.add(stock); + } } portfolioPresenter.presentPortfolioListData(simulatedHoldings, stockList); diff --git a/src/main/java/view/WatchListView.java b/src/main/java/view/WatchListView.java index 164ed90a4..544f56ccb 100644 --- a/src/main/java/view/WatchListView.java +++ b/src/main/java/view/WatchListView.java @@ -121,7 +121,7 @@ public void mouseExited(java.awt.event.MouseEvent evt) { final JLabel dailyChangeLabel = new JLabel(dailyChange + " (" + dailyPercentage + ")"); dailyChangeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); - final JLabel volumeLabel = new JLabel(volume); + final JLabel volumeLabel = new JLabel("Volume: " + volume); volumeLabel.setFont(new Font("SansSerif", Font.PLAIN, 16)); rightPanel.add(Box.createVerticalGlue()); From d1c6ecd3fff6370fe28b017697df740dfb496047 Mon Sep 17 00:00:00 2001 From: KevinNeverGiveUp Date: Mon, 2 Dec 2024 15:45:14 -0500 Subject: [PATCH 110/113] Update README.md Add debugMode explaination --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fc3435319..6e934114b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ To run this project, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the `src/main/java/app` directory: 3. Click the green arrow on the top right section in your IntelliJ.![image](https://github.com/user-attachments/assets/a31de5dc-624e-4e24-938c-1cc258b78c23) +4. Due to API usage limitation, we are currently using mock data for our application. If you want to use real-time data, please go to `src/main/java/entity/DebugMode` and change debugMode to **false** [Back to Top](#contents) From ee15c7253b1859f547720a03a5f6de76654f8326 Mon Sep 17 00:00:00 2001 From: Allen Huang Date: Mon, 2 Dec 2024 15:45:21 -0500 Subject: [PATCH 111/113] usecase test (account, buy) --- .../java/use_case/buy/BuyInteractorTest.java | 151 ++++++++++ .../use_case/login/LoginInteractorTest.java | 269 +++++++++++++----- .../use_case/logout/LogoutInteractorTest.java | 130 ++++++--- .../use_case/signup/SignupInteractorTest.java | 203 +++++++++---- 4 files changed, 587 insertions(+), 166 deletions(-) create mode 100644 src/test/java/use_case/buy/BuyInteractorTest.java diff --git a/src/test/java/use_case/buy/BuyInteractorTest.java b/src/test/java/use_case/buy/BuyInteractorTest.java new file mode 100644 index 000000000..b45be735a --- /dev/null +++ b/src/test/java/use_case/buy/BuyInteractorTest.java @@ -0,0 +1,151 @@ +package use_case.buy; + +import entity.CommonSimulatedHoldingFactory; +import entity.SimulatedHolding; +import entity.SimulatedHoldingFactory; +import org.junit.jupiter.api.Test; +import use_case.portfolio.PortfolioDataAccessInterface; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.*; + +class BuyInteractorTest { + + @Test + void testBuyInputData() { + // Test the BuyInputData class + BuyInputData inputData = new BuyInputData("AAPL", 150.0, 10.0); + assertEquals("AAPL", inputData.getSymbol()); + assertEquals(150.0, inputData.getPrice()); + assertEquals(10.0, inputData.getQuantity()); + } + + @Test + void testBuyOutputData() { + // Test the BuyOutputData class + SimulatedHoldingFactory holdingFactory = new CommonSimulatedHoldingFactory(); + SimulatedHolding simulatedHolding = holdingFactory.create("AAPL", 150.0, 10.0); + BuyOutputData outputData = new BuyOutputData(simulatedHolding); + assertEquals(simulatedHolding, outputData.getSimulatedHolding()); + } + + @Test + void testExecute() { + // Prepare input data + BuyInputData inputData = new BuyInputData("AAPL", 150.0, 10.0); + + // Use in-memory portfolio data access object + InMemoryPortfolioDataAccess portfolioDataAccessObject = new InMemoryPortfolioDataAccess(); + + // Create a test presenter + TestBuyOutputBoundary buyPresenter = new TestBuyOutputBoundary(); + + // Create Interactor instance + BuyInteractor buyInteractor = new BuyInteractor(buyPresenter, portfolioDataAccessObject); + + // Execute test + buyInteractor.execute(inputData); + + // Verify that the SimulatedHolding was added to the portfolio + assertEquals(1, portfolioDataAccessObject.getPortfolioList().size()); + SimulatedHolding holding = portfolioDataAccessObject.getPortfolioList().get(0); + assertEquals("AAPL", holding.getSymbol()); + assertEquals(150.0, holding.getPurchasePrice()); + assertEquals(10.0, holding.getPurchaseAmount()); + + // Verify that the presenter was called correctly + assertTrue(buyPresenter.successViewCalled); + assertEquals(holding, buyPresenter.outputData.getSimulatedHolding()); + } + + @Test + void testSwitchToHomeView() { + // Use in-memory portfolio data access object + InMemoryPortfolioDataAccess portfolioDataAccessObject = new InMemoryPortfolioDataAccess(); + + // Create a test presenter + TestBuyOutputBoundary buyPresenter = new TestBuyOutputBoundary(); + + // Create Interactor instance + BuyInteractor buyInteractor = new BuyInteractor(buyPresenter, portfolioDataAccessObject); + + // Execute test + buyInteractor.switchToHomeView(); + + // Verify that switchToHomeView was called + assertTrue(buyPresenter.switchToHomeViewCalled); + } + + @Test + void testSwitchToStockView() { + // Use in-memory portfolio data access object + InMemoryPortfolioDataAccess portfolioDataAccessObject = new InMemoryPortfolioDataAccess(); + + // Create a test presenter + TestBuyOutputBoundary buyPresenter = new TestBuyOutputBoundary(); + + // Create Interactor instance + BuyInteractor buyInteractor = new BuyInteractor(buyPresenter, portfolioDataAccessObject); + + // Execute test + buyInteractor.switchToStockView(); + + // Verify that switchToStockView was called + assertTrue(buyPresenter.switchToStockViewCalled); + } + + // In-memory portfolio data access object implementation + static class InMemoryPortfolioDataAccess implements PortfolioDataAccessInterface { + private final ArrayList portfolioList = new ArrayList<>(); + + @Override + public ArrayList getPortfolioList() { + return portfolioList; + } + + @Override + public void savePortfolioList() { + // Not needed for the test + } + + @Override + public void addToPortfolioList(SimulatedHolding simulatedHolding) { + portfolioList.add(simulatedHolding); + } + + @Override + public void removeFromPortfolioList(SimulatedHolding simulatedHolding) { + portfolioList.remove(simulatedHolding); + } + + @Override + public void createPortfolioList() { + // Not needed for the test + } + } + + // Test presenter class + static class TestBuyOutputBoundary implements BuyOutputBoundary { + boolean successViewCalled = false; + boolean switchToHomeViewCalled = false; + boolean switchToStockViewCalled = false; + BuyOutputData outputData; + + @Override + public void prepareSuccessView(BuyOutputData outputData) { + this.successViewCalled = true; + this.outputData = outputData; + } + + @Override + public void switchToHomeView() { + this.switchToHomeViewCalled = true; + } + + @Override + public void switchToStockView() { + this.switchToStockViewCalled = true; + } + } +} diff --git a/src/test/java/use_case/login/LoginInteractorTest.java b/src/test/java/use_case/login/LoginInteractorTest.java index 3f4c442e3..7580145da 100644 --- a/src/test/java/use_case/login/LoginInteractorTest.java +++ b/src/test/java/use_case/login/LoginInteractorTest.java @@ -1,145 +1,256 @@ package use_case.login; -import data_access.InMemoryUserDataAccessObject; -import entity.CommonUserFactory; +import data_access.FileUserDataAccessObject; import entity.User; import entity.UserFactory; +import entity.CommonUserFactory; import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import static org.junit.jupiter.api.Assertions.*; class LoginInteractorTest { @Test - void successTest() { - LoginInputData inputData = new LoginInputData("Paul", "password"); - LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); + void testLoginInputData() { + // Test the LoginInputData class + LoginInputData inputData = new LoginInputData("testUser", "testPassword"); + assertEquals("testUser", inputData.getUsername()); + assertEquals("testPassword", inputData.getPassword()); + } - // For the success test, we need to add Paul to the data access repository before we log in. - UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); - userRepository.save(user); + @Test + void testLoginOutputData() { + // Test the LoginOutputData class + LoginOutputData outputData = new LoginOutputData("testUser", false); + assertEquals("testUser", outputData.getUsername()); + } - // This creates a successPresenter that tests whether the test case is as we expect. - LoginOutputBoundary successPresenter = new LoginOutputBoundary() { + @Test + void testUserDoesNotExist() { + // Prepare input data + LoginInputData inputData = new LoginInputData("nonexistentUser", "password123"); + + // Use in-memory user data access object with a UserFactory + UserFactory userFactory = new CommonUserFactory(); + InMemoryLoginUserDataAccess userDataAccessObject = new InMemoryLoginUserDataAccess(userFactory); + + // Create a test presenter + LoginOutputBoundary loginPresenter = new LoginOutputBoundary() { @Override - public void prepareSuccessView(LoginOutputData user) { - assertEquals("Paul", user.getUsername()); + public void prepareSuccessView(LoginOutputData outputData) { + fail("Expected login failure, but login succeeded."); } @Override - public void prepareFailView(String error) { - fail("Use case failure is unexpected."); + public void prepareFailView(String errorMessage) { + assertEquals("nonexistentUser: Account does not exist.", errorMessage); } @Override public void switchToSignUpView() { - // Do nothing + fail("switchToSignUpView should not be called in this test."); } }; - LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); - interactor.execute(inputData); + // Create Interactor instance + LoginInteractor loginInteractor = new LoginInteractor(userDataAccessObject, loginPresenter); + + // Execute test + loginInteractor.execute(inputData); } @Test - void successUserLoggedInTest() { - LoginInputData inputData = new LoginInputData("Paul", "password"); - LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); + void testIncorrectPassword() { + // Prepare input data + LoginInputData inputData = new LoginInputData("existingUser", "wrongPassword"); - // For the success test, we need to add Paul to the data access repository before we log in. - UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); - userRepository.save(user); + // Use in-memory user data access object and add a user using UserFactory + UserFactory userFactory = new CommonUserFactory(); + InMemoryLoginUserDataAccess userDataAccessObject = new InMemoryLoginUserDataAccess(userFactory); + User user = userFactory.create("existingUser", "correctPassword", new ArrayList<>(), new ArrayList<>()); + userDataAccessObject.save(user); - // This creates a successPresenter that tests whether the test case is as we expect. - LoginOutputBoundary successPresenter = new LoginOutputBoundary() { + // Create a test presenter + LoginOutputBoundary loginPresenter = new LoginOutputBoundary() { @Override - public void prepareSuccessView(LoginOutputData user) { - assertEquals("Paul", userRepository.getCurrentUsername()); + public void prepareSuccessView(LoginOutputData outputData) { + fail("Expected login failure, but login succeeded."); } @Override - public void prepareFailView(String error) { - fail("Use case failure is unexpected."); + public void prepareFailView(String errorMessage) { + assertEquals("Incorrect password for \"existingUser\".", errorMessage); } @Override public void switchToSignUpView() { - // Do nothing + fail("switchToSignUpView should not be called in this test."); } }; - LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter); - assertEquals(null, userRepository.getCurrentUsername()); + // Create Interactor instance + LoginInteractor loginInteractor = new LoginInteractor(userDataAccessObject, loginPresenter); - interactor.execute(inputData); + // Execute test + loginInteractor.execute(inputData); } @Test - void failurePasswordMismatchTest() { - LoginInputData inputData = new LoginInputData("Paul", "wrong"); - LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); - - // For this failure test, we need to add Paul to the data access repository before we log in, and - // the passwords should not match. - UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); - userRepository.save(user); - - // This creates a presenter that tests whether the test case is as we expect. - LoginOutputBoundary failurePresenter = new LoginOutputBoundary() { + void testSuccessfulLogin() { + // Prepare input data + LoginInputData inputData = new LoginInputData("existingUser", "correctPassword"); + + // Use in-memory user data access object and add a user using UserFactory + UserFactory userFactory = new CommonUserFactory(); + InMemoryLoginUserDataAccess userDataAccessObject = new InMemoryLoginUserDataAccess(userFactory); + User user = userFactory.create("existingUser", "correctPassword", new ArrayList<>(), new ArrayList<>()); + userDataAccessObject.save(user); + + // Create a test presenter + LoginOutputBoundary loginPresenter = new LoginOutputBoundary() { @Override - public void prepareSuccessView(LoginOutputData user) { - // this should never be reached since the test case should fail - fail("Use case success is unexpected."); + public void prepareSuccessView(LoginOutputData outputData) { + assertEquals("existingUser", outputData.getUsername()); } @Override - public void prepareFailView(String error) { - assertEquals("Incorrect password for \"Paul\".", error); + public void prepareFailView(String errorMessage) { + fail("Expected login success, but login failed: " + errorMessage); } @Override public void switchToSignUpView() { - // Do nothing + fail("switchToSignUpView should not be called in this test."); } }; - LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter); - interactor.execute(inputData); + // Create a mock FileUserDataAccessObject + MockFileUserDataAccessObject dataAccessObject = new MockFileUserDataAccessObject(); + + // Create Interactor instance + LoginInteractor loginInteractor = new LoginInteractor(userDataAccessObject, loginPresenter); + + // Use reflection to set the private final field 'dataAccessObject' + try { + Field dataAccessObjectField = LoginInteractor.class.getDeclaredField("dataAccessObject"); + dataAccessObjectField.setAccessible(true); + dataAccessObjectField.set(loginInteractor, dataAccessObject); + } catch (NoSuchFieldException | IllegalAccessException e) { + fail("Failed to set dataAccessObject via reflection: " + e.getMessage()); + } + + // Execute test + loginInteractor.execute(inputData); + + // Verify that dataAccessObject methods were called + assertTrue(dataAccessObject.isUserLoggedIn()); + assertTrue(dataAccessObject.isSaveUserLoginStatusCalled()); + + // Verify that the current username was set + assertEquals("existingUser", userDataAccessObject.getCurrentUsername()); } @Test - void failureUserDoesNotExistTest() { - LoginInputData inputData = new LoginInputData("Paul", "password"); - LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); + void testSwitchToSignUpView() { + // Create a test presenter + TestLoginOutputBoundary loginPresenter = new TestLoginOutputBoundary(); - // Add Paul to the repo so that when we check later they already exist + // Use in-memory user data access object + UserFactory userFactory = new CommonUserFactory(); + InMemoryLoginUserDataAccess userDataAccessObject = new InMemoryLoginUserDataAccess(userFactory); - // This creates a presenter that tests whether the test case is as we expect. - LoginOutputBoundary failurePresenter = new LoginOutputBoundary() { - @Override - public void prepareSuccessView(LoginOutputData user) { - // this should never be reached since the test case should fail - fail("Use case success is unexpected."); - } + // Create Interactor instance + LoginInteractor loginInteractor = new LoginInteractor(userDataAccessObject, loginPresenter); - @Override - public void prepareFailView(String error) { - assertEquals("Paul: Account does not exist.", error); - } + // Execute test + loginInteractor.switchToSignUpView(); - @Override - public void switchToSignUpView() { - // Do nothing - } - }; + // Verify that switchToSignUpView was called + assertTrue(loginPresenter.switchCalled); + } + + // In-memory user data access object implementation + static class InMemoryLoginUserDataAccess implements LoginUserDataAccessInterface { + private final Map users = new HashMap<>(); + private String currentUsername; + private final UserFactory userFactory; + + public InMemoryLoginUserDataAccess(UserFactory userFactory) { + this.userFactory = userFactory; + } + + @Override + public boolean existsByName(String username) { + return users.containsKey(username); + } + + @Override + public User get(String username) { + return users.get(username); + } + + @Override + public void save(User user) { + users.put(user.getName(), user); + } + + @Override + public void setCurrentUsername(String username) { + this.currentUsername = username; + } + + @Override + public String getCurrentUsername() { + return currentUsername; + } + } + + // Mock FileUserDataAccessObject + static class MockFileUserDataAccessObject extends FileUserDataAccessObject { + private boolean userLoggedIn = false; + private boolean saveUserLoginStatusCalled = false; + + @Override + public void setUserLoggedIn(boolean loggedIn) { + this.userLoggedIn = loggedIn; + } + + @Override + public void saveUserLoginStatus() { + this.saveUserLoginStatusCalled = true; + } + + public boolean isUserLoggedIn() { + return userLoggedIn; + } + + public boolean isSaveUserLoginStatusCalled() { + return saveUserLoginStatusCalled; + } + } + + // Test presenter class for switchToSignUpView test + static class TestLoginOutputBoundary implements LoginOutputBoundary { + boolean switchCalled = false; + + @Override + public void prepareSuccessView(LoginOutputData outputData) { + fail("Expected switchToSignUpView to be called, but prepareSuccessView was called."); + } + + @Override + public void prepareFailView(String errorMessage) { + fail("Expected switchToSignUpView to be called, but prepareFailView was called."); + } - LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter); - interactor.execute(inputData); + @Override + public void switchToSignUpView() { + switchCalled = true; + } } -} \ No newline at end of file +} diff --git a/src/test/java/use_case/logout/LogoutInteractorTest.java b/src/test/java/use_case/logout/LogoutInteractorTest.java index 5906b27cd..a38a4ba26 100644 --- a/src/test/java/use_case/logout/LogoutInteractorTest.java +++ b/src/test/java/use_case/logout/LogoutInteractorTest.java @@ -1,46 +1,106 @@ package use_case.logout; -import data_access.InMemoryUserDataAccessObject; -import entity.CommonUserFactory; -import entity.User; -import entity.UserFactory; import org.junit.jupiter.api.Test; -import java.util.ArrayList; - import static org.junit.jupiter.api.Assertions.*; class LogoutInteractorTest { @Test - void successTest() { - LogoutInputData inputData = new LogoutInputData("Paul"); - InMemoryUserDataAccessObject userRepository = new InMemoryUserDataAccessObject(); - - // For the success test, we need to add Paul to the data access repository before we log in. - UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "password", new ArrayList<>(), new ArrayList<>()); - userRepository.save(user); - userRepository.setCurrentUsername("Paul"); - - // This creates a successPresenter that tests whether the test case is as we expect. - LogoutOutputBoundary successPresenter = new LogoutOutputBoundary() { - @Override - public void prepareSuccessView(LogoutOutputData user) { - // check that the output data contains the username of who logged out - assertEquals("Paul", user.getUsername()); - } - - @Override - public void prepareFailView(String error) { - fail("Use case failure is unexpected."); - } - }; - - LogoutInputBoundary interactor = new LogoutInteractor(userRepository, successPresenter); - interactor.execute(inputData); - // check that the user was logged out - assertNull(userRepository.getCurrentUsername()); + void testLogoutInputData() { + // Test the LogoutInputData class + LogoutInputData inputData = new LogoutInputData("testUser"); + assertEquals("testUser", inputData.getUsername()); + } + + @Test + void testLogoutOutputData() { + // Test the LogoutOutputData class + LogoutOutputData outputData = new LogoutOutputData("testUser", false); + assertEquals("testUser", outputData.getUsername()); + assertFalse(outputData.isUseCaseFailed()); } -} \ No newline at end of file + @Test + void testSuccessfulLogout() { + // Prepare input data + LogoutInputData inputData = new LogoutInputData("existingUser"); + + // Use in-memory user data access object + InMemoryLogoutUserDataAccess userDataAccessObject = new InMemoryLogoutUserDataAccess(); + userDataAccessObject.setCurrentUsername("existingUser"); + + // Create a test presenter + TestLogoutOutputBoundary logoutPresenter = new TestLogoutOutputBoundary(); + + // Create Interactor instance + LogoutInteractor logoutInteractor = new LogoutInteractor(userDataAccessObject, logoutPresenter); + + // Execute test + logoutInteractor.execute(inputData); + + // Verify that the current username is set to null + assertNull(userDataAccessObject.getCurrentUsername()); + + // Verify that the presenter was called correctly + assertTrue(logoutPresenter.successViewCalled); + assertEquals("existingUser", logoutPresenter.outputData.getUsername()); + } + + @Test + void testLogoutWhenNoUserLoggedIn() { + // Prepare input data with no user logged in + LogoutInputData inputData = new LogoutInputData(null); + + // Use in-memory user data access object + InMemoryLogoutUserDataAccess userDataAccessObject = new InMemoryLogoutUserDataAccess(); + + // Create a test presenter + TestLogoutOutputBoundary logoutPresenter = new TestLogoutOutputBoundary(); + + // Create Interactor instance + LogoutInteractor logoutInteractor = new LogoutInteractor(userDataAccessObject, logoutPresenter); + + // Execute test + logoutInteractor.execute(inputData); + + // Verify that the current username is still null + assertNull(userDataAccessObject.getCurrentUsername()); + + // Verify that the presenter was called correctly + assertTrue(logoutPresenter.successViewCalled); + assertNull(logoutPresenter.outputData.getUsername()); + } + + // In-memory user data access object implementation + static class InMemoryLogoutUserDataAccess implements LogoutUserDataAccessInterface { + private String currentUsername; + + @Override + public String getCurrentUsername() { + return currentUsername; + } + + @Override + public void setCurrentUsername(String username) { + this.currentUsername = username; + } + } + + // Test presenter class + static class TestLogoutOutputBoundary implements LogoutOutputBoundary { + boolean successViewCalled = false; + LogoutOutputData outputData; + + @Override + public void prepareSuccessView(LogoutOutputData outputData) { + this.successViewCalled = true; + this.outputData = outputData; + } + + @Override + public void prepareFailView(String errorMessage) { + fail("Expected success, but failure view was called with message: " + errorMessage); + } + } +} diff --git a/src/test/java/use_case/signup/SignupInteractorTest.java b/src/test/java/use_case/signup/SignupInteractorTest.java index 224fe78c0..40d5a7c4d 100644 --- a/src/test/java/use_case/signup/SignupInteractorTest.java +++ b/src/test/java/use_case/signup/SignupInteractorTest.java @@ -1,12 +1,10 @@ package use_case.signup; -import data_access.InMemoryUserDataAccessObject; import entity.CommonUserFactory; import entity.User; import entity.UserFactory; import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; import java.util.ArrayList; import static org.junit.jupiter.api.Assertions.*; @@ -14,92 +12,193 @@ class SignupInteractorTest { @Test - void successTest() { - SignupInputData inputData = new SignupInputData("Paul", "password", "password"); - SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); + void testSignupInputData() { + // Test the SignupInputData class + SignupInputData inputData = new SignupInputData("testUser", "testPassword", "testPassword"); + assertEquals("testUser", inputData.getUsername()); + assertEquals("testPassword", inputData.getPassword()); + assertEquals("testPassword", inputData.getRepeatPassword()); + } + + @Test + void testSignupOutputData() { + // Test the SignupOutputData class + SignupOutputData outputData = new SignupOutputData("testUser", false); + assertEquals("testUser", outputData.getUsername()); + assertFalse(outputData.isUseCaseFailed()); + } + + @Test + void testUserAlreadyExists() { + // Prepare input data + SignupInputData inputData = new SignupInputData("existingUser", "password123", "password123"); + + // Use in-memory user data access object with a UserFactory + UserFactory userFactory = new CommonUserFactory(); + InMemorySignupUserDataAccess userDataAccessObject = new InMemorySignupUserDataAccess(userFactory); + + // Add an existing user + User existingUser = userFactory.create("existingUser", "oldPassword", new ArrayList<>(), new ArrayList<>()); + userDataAccessObject.save(existingUser); - // This creates a successPresenter that tests whether the test case is as we expect. - SignupOutputBoundary successPresenter = new SignupOutputBoundary() { + // Create a test presenter + SignupOutputBoundary userPresenter = new SignupOutputBoundary() { @Override - public void prepareSuccessView(SignupOutputData user) { - // 2 things to check: the output data is correct, and the user has been created in the DAO. - assertEquals("Paul", user.getUsername()); - assertTrue(userRepository.existsByName("Paul")); + public void prepareSuccessView(SignupOutputData outputData) { + fail("Expected failure due to existing user, but success view was called."); } @Override - public void prepareFailView(String error) { - fail("Use case failure is unexpected."); + public void prepareFailView(String errorMessage) { + assertEquals("User already exists.", errorMessage); } @Override public void switchToLoginView() { - // This is expected + fail("switchToLoginView should not be called in this test."); } }; - SignupInputBoundary interactor = new SignupInteractor(userRepository, successPresenter, new CommonUserFactory()); + // Create Interactor instance + SignupInteractor interactor = new SignupInteractor(userDataAccessObject, userPresenter, userFactory); + + // Execute test interactor.execute(inputData); + + // No additional assertions needed; logic is verified in the presenter } @Test - void failurePasswordMismatchTest() { - SignupInputData inputData = new SignupInputData("Paul", "password", "wrong"); - SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); + void testPasswordsDoNotMatch() { + // Prepare input data + SignupInputData inputData = new SignupInputData("newUser", "password123", "password456"); - // This creates a presenter that tests whether the test case is as we expect. - SignupOutputBoundary failurePresenter = new SignupOutputBoundary() { + // Use in-memory user data access object with a UserFactory + UserFactory userFactory = new CommonUserFactory(); + InMemorySignupUserDataAccess userDataAccessObject = new InMemorySignupUserDataAccess(userFactory); + + // Create a test presenter + SignupOutputBoundary userPresenter = new SignupOutputBoundary() { @Override - public void prepareSuccessView(SignupOutputData user) { - // this should never be reached since the test case should fail - fail("Use case success is unexpected."); + public void prepareSuccessView(SignupOutputData outputData) { + fail("Expected failure due to password mismatch, but success view was called."); } @Override - public void prepareFailView(String error) { - assertEquals("Passwords don't match.", error); + public void prepareFailView(String errorMessage) { + assertEquals("Passwords don't match.", errorMessage); } @Override public void switchToLoginView() { - // This is expected + fail("switchToLoginView should not be called in this test."); } }; - SignupInputBoundary interactor = new SignupInteractor(userRepository, failurePresenter, new CommonUserFactory()); + // Create Interactor instance + SignupInteractor interactor = new SignupInteractor(userDataAccessObject, userPresenter, userFactory); + + // Execute test interactor.execute(inputData); + + // No additional assertions needed; logic is verified in the presenter } @Test - void failureUserExistsTest() { - SignupInputData inputData = new SignupInputData("Paul", "password", "wrong"); - SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject(); + void testSuccessfulSignup() { + // Prepare input data + SignupInputData inputData = new SignupInputData("newUser", "password123", "password123"); - // Add Paul to the repo so that when we check later they already exist - UserFactory factory = new CommonUserFactory(); - User user = factory.create("Paul", "pwd", new ArrayList<>(), new ArrayList<>()); - userRepository.save(user); + // Use in-memory user data access object with a UserFactory + UserFactory userFactory = new CommonUserFactory(); + InMemorySignupUserDataAccess userDataAccessObject = new InMemorySignupUserDataAccess(userFactory); - // This creates a presenter that tests whether the test case is as we expect. - SignupOutputBoundary failurePresenter = new SignupOutputBoundary() { - @Override - public void prepareSuccessView(SignupOutputData user) { - // this should never be reached since the test case should fail - fail("Use case success is unexpected."); - } + // Create a test presenter + TestSignupOutputBoundary userPresenter = new TestSignupOutputBoundary(); - @Override - public void prepareFailView(String error) { - assertEquals("User already exists.", error); - } + // Create Interactor instance + SignupInteractor interactor = new SignupInteractor(userDataAccessObject, userPresenter, userFactory); - @Override - public void switchToLoginView() { - // This is expected + // Execute test + interactor.execute(inputData); + + // Verify that the user was saved + assertTrue(userDataAccessObject.existsByName("newUser")); + User savedUser = userDataAccessObject.get("newUser"); + assertEquals("password123", savedUser.getPassword()); + + // Verify that the presenter was called correctly + assertTrue(userPresenter.successViewCalled); + assertEquals("newUser", userPresenter.outputData.getUsername()); + } + + @Test + void testSwitchToLoginView() { + // Create a test presenter + TestSignupOutputBoundary userPresenter = new TestSignupOutputBoundary(); + + // Use in-memory user data access object + UserFactory userFactory = new CommonUserFactory(); + InMemorySignupUserDataAccess userDataAccessObject = new InMemorySignupUserDataAccess(userFactory); + + // Create Interactor instance + SignupInteractor interactor = new SignupInteractor(userDataAccessObject, userPresenter, userFactory); + + // Execute test + interactor.switchToLoginView(); + + // Verify that switchToLoginView was called + assertTrue(userPresenter.switchCalled); + } + + // In-memory user data access object implementation + static class InMemorySignupUserDataAccess implements SignupUserDataAccessInterface { + private final UserFactory userFactory; + private final ArrayList users = new ArrayList<>(); + + public InMemorySignupUserDataAccess(UserFactory userFactory) { + this.userFactory = userFactory; + } + + public void save(User user) { + users.add(user); + } + + public User get(String username) { + for (User user : users) { + if (user.getName().equals(username)) { + return user; + } } - }; + return null; + } - SignupInputBoundary interactor = new SignupInteractor(userRepository, failurePresenter, new CommonUserFactory()); - interactor.execute(inputData); + @Override + public boolean existsByName(String username) { + return get(username) != null; + } + } + + // Test presenter class + static class TestSignupOutputBoundary implements SignupOutputBoundary { + boolean successViewCalled = false; + boolean switchCalled = false; + SignupOutputData outputData; + + @Override + public void prepareSuccessView(SignupOutputData outputData) { + this.successViewCalled = true; + this.outputData = outputData; + } + + @Override + public void prepareFailView(String errorMessage) { + fail("Expected success, but failure view was called with message: " + errorMessage); + } + + @Override + public void switchToLoginView() { + this.switchCalled = true; + } } -} \ No newline at end of file +} From ccf6c9697b2f3de04fd9afadcc2e10c83c73536c Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 2 Dec 2024 21:50:34 -0500 Subject: [PATCH 112/113] some checkstyle fix of portfolio and stock --- .../PortfolioDataAccessInterface.java | 47 ++++++++++++++----- .../portfolio/PortfolioInputBoundary.java | 3 ++ .../portfolio/PortfolioInteractor.java | 28 +++++++---- .../portfolio/PortfolioOutputBoundary.java | 3 ++ .../use_case/stock/StockInputBoundary.java | 3 ++ .../java/use_case/stock/StockInteractor.java | 9 ++-- .../use_case/stock/StockOutputBoundary.java | 3 ++ src/main/java/view/PortfolioView.java | 42 ++++++++++++----- src/main/java/view/StockView.java | 2 - 9 files changed, 99 insertions(+), 41 deletions(-) diff --git a/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java b/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java index df3fee059..7067a148a 100644 --- a/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java +++ b/src/main/java/use_case/portfolio/PortfolioDataAccessInterface.java @@ -1,19 +1,42 @@ package use_case.portfolio; -import entity.SimulatedHolding; - import java.util.ArrayList; -public interface PortfolioDataAccessInterface { - - public ArrayList getPortfolioList(); - - public void savePortfolioList(); - - public void addToPortfolioList(SimulatedHolding simulatedHolding); - - public void removeFromPortfolioList(SimulatedHolding simulatedHolding); +import entity.SimulatedHolding; - public void createPortfolioList(); +/** + * Interface for managing portfolio data. + */ +public interface PortfolioDataAccessInterface { + /** + * Get the portfolio list. + * + * @return the portfolio list + */ + ArrayList getPortfolioList(); + + /** + * Save the portfolio list. + */ + void savePortfolioList(); + + /** + * Add a holding to the portfolio list. + * + * @param simulatedHolding the holding to add + */ + void addToPortfolioList(SimulatedHolding simulatedHolding); + + /** + * Remove a holding from the portfolio list. + * + * @param simulatedHolding the holding to remove + */ + void removeFromPortfolioList(SimulatedHolding simulatedHolding); + + /** + * Create a new portfolio list. + */ + void createPortfolioList(); } diff --git a/src/main/java/use_case/portfolio/PortfolioInputBoundary.java b/src/main/java/use_case/portfolio/PortfolioInputBoundary.java index 9a8f8ea3e..9c68778c9 100644 --- a/src/main/java/use_case/portfolio/PortfolioInputBoundary.java +++ b/src/main/java/use_case/portfolio/PortfolioInputBoundary.java @@ -1,5 +1,8 @@ package use_case.portfolio; +/** + * Input Boundary for actions which are related to portfolio. + */ public interface PortfolioInputBoundary { /** * Get portfolio list data. diff --git a/src/main/java/use_case/portfolio/PortfolioInteractor.java b/src/main/java/use_case/portfolio/PortfolioInteractor.java index 941110024..fe0b49af9 100644 --- a/src/main/java/use_case/portfolio/PortfolioInteractor.java +++ b/src/main/java/use_case/portfolio/PortfolioInteractor.java @@ -1,14 +1,22 @@ package use_case.portfolio; +import java.util.ArrayList; + import entity.CommonStockFactory; import entity.DebugMode; import entity.SimulatedHolding; import entity.Stock; import use_case.home_view.HomeDataAccessInterface; -import java.util.ArrayList; +/** + * The Portfolio Interactor. + */ +public class PortfolioInteractor implements PortfolioInputBoundary { + private static final int INITIAL_STOCK_PRICE = 100; + private static final int STOCK_PRICE_INCREMENT = 100; + private static final int ADDITIONAL_PRICE = 50; + private static final double FAKE_VOLUME = 10000000.2; -public class PortfolioInteractor implements PortfolioInputBoundary{ private final PortfolioDataAccessInterface portfolioDataAccessObject; private final HomeDataAccessInterface homeDataAccessObject; private final PortfolioOutputBoundary portfolioPresenter; @@ -28,18 +36,20 @@ public void getPortfolioListData() { if (DebugMode.debugMode) { // Using fake data - int i = 100; + int stockPrice = INITIAL_STOCK_PRICE; final CommonStockFactory stockFactory = new CommonStockFactory(); for (SimulatedHolding simulatedHolding : simulatedHoldings) { - final Stock stock = stockFactory.create(simulatedHolding.getSymbol(), + final Stock stock = stockFactory.create( + simulatedHolding.getSymbol(), 0, - i + 50, - 10000000.2, + stockPrice + ADDITIONAL_PRICE, + FAKE_VOLUME, 0, - 0); + 0 + ); stockList.add(stock); - i += 100; + stockPrice += STOCK_PRICE_INCREMENT; } } else { @@ -49,7 +59,5 @@ public void getPortfolioListData() { stockList.add(stock); } } - - portfolioPresenter.presentPortfolioListData(simulatedHoldings, stockList); } } diff --git a/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java b/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java index 879182309..b791bea1a 100644 --- a/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java +++ b/src/main/java/use_case/portfolio/PortfolioOutputBoundary.java @@ -5,6 +5,9 @@ import entity.SimulatedHolding; import entity.Stock; +/** + * Output Data for the portfolio Use Case. + */ public interface PortfolioOutputBoundary { /** * Present portfolio list data for Portfolio View. diff --git a/src/main/java/use_case/stock/StockInputBoundary.java b/src/main/java/use_case/stock/StockInputBoundary.java index 940f78478..0330cac3d 100644 --- a/src/main/java/use_case/stock/StockInputBoundary.java +++ b/src/main/java/use_case/stock/StockInputBoundary.java @@ -2,6 +2,9 @@ import entity.Stock; +/** + * Input Boundary for actions which are related to Stock. + */ public interface StockInputBoundary { /** * Handles the logic when a user wants to buy a stock. diff --git a/src/main/java/use_case/stock/StockInteractor.java b/src/main/java/use_case/stock/StockInteractor.java index e7889c46d..ee160c8a0 100644 --- a/src/main/java/use_case/stock/StockInteractor.java +++ b/src/main/java/use_case/stock/StockInteractor.java @@ -1,11 +1,14 @@ package use_case.stock; +import java.util.ArrayList; + import entity.Stock; import use_case.watchlist.WatchListDataAccessInterface; import use_case.watchlist.WatchListModifyDataAccessInterface; -import java.util.ArrayList; - +/** + * The Stock Interactor. + */ public class StockInteractor implements StockInputBoundary { private final StockOutputBoundary stockPresenter; @@ -27,8 +30,6 @@ public void buyStock(Stock stock) { @Override public void toggleWatchlist(Stock stock, Boolean shouldModifyData) { - // When user comes from HomeView or WatchlistView, we should only update favourite button UI based on watchlist Data. - // We update watchlist data iff when user clicked favourite button. final ArrayList watchList = watchListDataAccessInterface.getWatchList(); if (watchList.contains(stock.getSymbol())) { if (shouldModifyData) { diff --git a/src/main/java/use_case/stock/StockOutputBoundary.java b/src/main/java/use_case/stock/StockOutputBoundary.java index 85f82d92d..a946a3716 100644 --- a/src/main/java/use_case/stock/StockOutputBoundary.java +++ b/src/main/java/use_case/stock/StockOutputBoundary.java @@ -2,6 +2,9 @@ import entity.Stock; +/** + * Output Boundary for actions which are related to stock. + */ public interface StockOutputBoundary { /** * Presents the Buy View when a user wants to buy a stock. diff --git a/src/main/java/view/PortfolioView.java b/src/main/java/view/PortfolioView.java index 80fd221e7..8db0b6544 100644 --- a/src/main/java/view/PortfolioView.java +++ b/src/main/java/view/PortfolioView.java @@ -1,5 +1,22 @@ package view; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.Border; + import entity.SimulatedHolding; import entity.Stock; import interface_adapter.ViewManagerModel; @@ -7,17 +24,19 @@ import interface_adapter.portfolio.PortfolioState; import interface_adapter.portfolio.PortfolioViewModel; -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; - -import javax.swing.border.Border; -import javax.swing.BorderFactory; - +/** + * The view for displaying portfolio details. + */ public class PortfolioView extends JPanel implements PropertyChangeListener { + private static final int PADDING_SMALL = 10; + private static final int PADDING_LARGE = 20; + private static final int FONT_SIZE_TITLE = 24; + private static final int FONT_SIZE_SUBTITLE = 18; + private static final int FONT_SIZE_LABEL = 16; + private static final int SCROLL_WIDTH = 400; + private static final int SCROLL_HEIGHT = 300; + private final PortfolioViewModel portfolioViewModel; private final ViewManagerModel viewManagerModel; private PortfolioController portfolioController; @@ -33,18 +52,15 @@ public PortfolioView(PortfolioViewModel portfolioViewModel, ViewManagerModel vie setLayout(new BorderLayout()); setBackground(Color.WHITE); - // panel setup final JPanel statisticsPanel = new JPanel(); statisticsPanel.setLayout(new BoxLayout(statisticsPanel, BoxLayout.Y_AXIS)); - statisticsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 20, 10)); + statisticsPanel.setBorder(BorderFactory.createEmptyBorder(PADDING_SMALL, PADDING_SMALL, PADDING_LARGE, PADDING_SMALL)); statisticsPanel.setBackground(Color.WHITE); - // Container for return button and current amount final JPanel topRowPanel = new JPanel(); topRowPanel.setLayout(new BoxLayout(topRowPanel, BoxLayout.Y_AXIS)); topRowPanel.setBackground(Color.WHITE); - // Return button final JButton backButton = new JButton("\u2190"); backButton.setFont(new Font("SansSerif", Font.PLAIN, 20)); backButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index 5360b0d88..ed31757a9 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -9,8 +9,6 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; From 3f27e882a141e76bd76b66c35482f943c2caa627 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 3 Dec 2024 03:23:30 -0500 Subject: [PATCH 113/113] some checkstyle fix of portfolio and stock --- .../stock_view/StockController.java | 12 ++ .../stock_view/StockPresenter.java | 7 +- .../stock_view/StockViewState.java | 8 +- src/main/java/view/StockView.java | 122 +++++++++++------- 4 files changed, 95 insertions(+), 54 deletions(-) diff --git a/src/main/java/interface_adapter/stock_view/StockController.java b/src/main/java/interface_adapter/stock_view/StockController.java index eb6f6a641..4f01ab4a2 100644 --- a/src/main/java/interface_adapter/stock_view/StockController.java +++ b/src/main/java/interface_adapter/stock_view/StockController.java @@ -3,6 +3,9 @@ import entity.Stock; import use_case.stock.StockInputBoundary; +/** + * Controller for the Stock Use Case. + */ public class StockController { private final StockInputBoundary stockInteractor; @@ -11,10 +14,19 @@ public StockController(StockInputBoundary stockInteractor) { this.stockInteractor = stockInteractor; } + /** + * Executes the buy Stock. + * @param stock the stock to buy + */ public void buyStock(Stock stock) { stockInteractor.buyStock(stock); } + /** + * Verify watchlist. + * @param stock the username to sign up + * @param shouldModifyData the username to sign up + */ public void toggleWatchlist(Stock stock, Boolean shouldModifyData) { stockInteractor.toggleWatchlist(stock, shouldModifyData); } diff --git a/src/main/java/interface_adapter/stock_view/StockPresenter.java b/src/main/java/interface_adapter/stock_view/StockPresenter.java index e656e4dd3..73288f7b0 100644 --- a/src/main/java/interface_adapter/stock_view/StockPresenter.java +++ b/src/main/java/interface_adapter/stock_view/StockPresenter.java @@ -6,13 +6,14 @@ import interface_adapter.buy_view.BuyViewModel; import interface_adapter.home_view.HomeState; import interface_adapter.home_view.HomeViewModel; -import interface_adapter.watchlist_view.WatchListViewModel; import interface_adapter.stock_view.WatchListViewState; +import interface_adapter.watchlist_view.WatchListViewModel; import use_case.stock.StockOutputBoundary; import view.StockView; -import java.util.ArrayList; - +/** + * The Presenter for the stockview Use Case. + */ public class StockPresenter implements StockOutputBoundary { private final StockViewModel stockViewModel; diff --git a/src/main/java/interface_adapter/stock_view/StockViewState.java b/src/main/java/interface_adapter/stock_view/StockViewState.java index 130b6618d..ce1798c6b 100644 --- a/src/main/java/interface_adapter/stock_view/StockViewState.java +++ b/src/main/java/interface_adapter/stock_view/StockViewState.java @@ -13,11 +13,15 @@ public Stock getStock() { return stock; } - public Boolean getIsFavorite() { return isFavorite; } + public Boolean getIsFavorite() { + return isFavorite; + } public void setStock(Stock newStock) { this.stock = newStock; } - public void setIsFavorite(Boolean isFavorite) { this.isFavorite = isFavorite; } + public void setIsFavorite(Boolean isFavorite) { + this.isFavorite = isFavorite; + } } diff --git a/src/main/java/view/StockView.java b/src/main/java/view/StockView.java index ed31757a9..0d3015961 100644 --- a/src/main/java/view/StockView.java +++ b/src/main/java/view/StockView.java @@ -1,22 +1,38 @@ package view; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + import data_access.FileUserDataAccessObject; import entity.Stock; import interface_adapter.ViewManagerModel; -import interface_adapter.stock_view.StockViewModel; import interface_adapter.stock_view.StockController; +import interface_adapter.stock_view.StockViewModel; import interface_adapter.stock_view.StockViewState; -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - /** * The View for displaying detailed information about a single stock. */ public class StockView extends AbstractViewWithBackButton implements PropertyChangeListener { - private final String viewName = "StockView"; + private static final String VIEW_NAME = "StockView"; + private static final String FONT_FAMILY = "SansSerif"; + private static final int FONT_SIZE_TITLE = 24; + private static final int FONT_SIZE_SUBTITLE = 18; + private static final int FONT_SIZE_LABEL = 16; + private static final int PADDING_LARGE = 20; + private static final int PADDING_SMALL = 10; + private static final int MILLION = 1_000_000; + private static final int THOUSAND = 1_000; private final ViewManagerModel viewManagerModel; private final StockViewModel stockViewModel; @@ -33,7 +49,7 @@ public class StockView extends AbstractViewWithBackButton implements PropertyCha private JButton buyButton; private String previousView; - private FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(); + private final FileUserDataAccessObject fileUserDataAccessObject = new FileUserDataAccessObject(); public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerModel) { fileUserDataAccessObject.isUserLoggedIn(); @@ -55,7 +71,7 @@ public StockView(StockViewModel stockViewModel, ViewManagerModel viewManagerMode add(Box.createVerticalGlue()); add(contentPanel); - add(Box.createRigidArea(new Dimension(0, 20))); + add(Box.createRigidArea(new Dimension(0, PADDING_LARGE))); add(createActionButtonsPanel()); add(Box.createVerticalGlue()); } @@ -65,8 +81,8 @@ public void setStockController(StockController stockController) { } private void initializeComponents(JPanel contentPanel) { - stockSymbolLabel = createLabel("", 24, Font.BOLD); - stockPriceLabel = createLabel("", 18, Font.PLAIN); + stockSymbolLabel = createLabel(FONT_SIZE_TITLE, Font.BOLD); + stockPriceLabel = createLabel(FONT_SIZE_SUBTITLE, Font.PLAIN); final JPanel stockInfoPanel = new JPanel(); stockInfoPanel.setLayout(new BoxLayout(stockInfoPanel, BoxLayout.Y_AXIS)); @@ -74,14 +90,14 @@ private void initializeComponents(JPanel contentPanel) { stockInfoPanel.add(stockSymbolLabel); stockInfoPanel.add(stockPriceLabel); - openPriceLabel = createLabel("", 16, Font.PLAIN); - closePriceLabel = createLabel("", 16, Font.PLAIN); - lowPriceLabel = createLabel("", 16, Font.PLAIN); - highPriceLabel = createLabel("", 16, Font.PLAIN); - volumeLabel = createLabel("", 16, Font.PLAIN); + openPriceLabel = createLabel(FONT_SIZE_LABEL, Font.PLAIN); + closePriceLabel = createLabel(FONT_SIZE_LABEL, Font.PLAIN); + lowPriceLabel = createLabel(FONT_SIZE_LABEL, Font.PLAIN); + highPriceLabel = createLabel(FONT_SIZE_LABEL, Font.PLAIN); + volumeLabel = createLabel(FONT_SIZE_LABEL, Font.PLAIN); contentPanel.add(stockInfoPanel); - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); + contentPanel.add(Box.createRigidArea(new Dimension(0, PADDING_SMALL))); contentPanel.add(openPriceLabel); contentPanel.add(closePriceLabel); contentPanel.add(lowPriceLabel); @@ -93,46 +109,46 @@ private void initializeComponents(JPanel contentPanel) { } private JPanel createActionButtonsPanel() { - JPanel actionPanel = new JPanel(); + final JPanel actionPanel = new JPanel(); actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS)); actionPanel.setBackground(Color.WHITE); actionPanel.setAlignmentX(Component.CENTER_ALIGNMENT); buyButton = new JButton("BUY"); - buyButton.setFont(new Font("SansSerif", Font.BOLD, 18)); + buyButton.setFont(new Font(FONT_FAMILY, Font.BOLD, FONT_SIZE_SUBTITLE)); buyButton.setFocusPainted(false); - - // Buy Button Action: Invoke the controller to buy stock - buyButton.addActionListener(e -> { - Stock currentStock = stockViewModel.getState().getStock(); + buyButton.addActionListener(event -> { + final Stock currentStock = stockViewModel.getState().getStock(); if (currentStock != null && stockController != null) { stockController.buyStock(currentStock); } }); // Star button for favorite - favoriteButton = new JButton("☆"); - favoriteButton.setFont(new Font("SansSerif", Font.BOLD, 18)); + favoriteButton = new JButton("\u2606"); + favoriteButton.setFont(new Font(FONT_FAMILY, Font.BOLD, FONT_SIZE_SUBTITLE)); favoriteButton.setFocusPainted(false); - - // Favorite Button Action: Add or remove stock from watchlist - favoriteButton.addActionListener(e -> { - Stock currentStock = stockViewModel.getState().getStock(); + favoriteButton.addActionListener(event -> { + final Stock currentStock = stockViewModel.getState().getStock(); if (currentStock != null && stockController != null) { - boolean isFavorite = "★".equals(favoriteButton.getText()); + final boolean isFavorite = "\u2605".equals(favoriteButton.getText()); stockController.toggleWatchlist(currentStock, true); } }); actionPanel.add(Box.createHorizontalGlue()); actionPanel.add(buyButton); - actionPanel.add(Box.createRigidArea(new Dimension(20, 0))); + actionPanel.add(Box.createRigidArea(new Dimension(PADDING_LARGE, 0))); actionPanel.add(favoriteButton); actionPanel.add(Box.createHorizontalGlue()); return actionPanel; } + /** + * The update data part . + * @param stock as the stock that should be shown + */ public void updateStockData(Stock stock) { stockSymbolLabel.setText(stock.getSymbol()); stockPriceLabel.setText("Current Price: $" + stock.getClosePrice()); @@ -141,23 +157,25 @@ public void updateStockData(Stock stock) { lowPriceLabel.setText("Low: $" + stock.getLow()); highPriceLabel.setText("High: $" + stock.getHigh()); - double volume = stock.getVolume(); - String volumeText = (volume >= 1_000_000) ? (volume / 1_000_000) + " M" : - (volume >= 1_000) ? (volume / 1_000) + " K" : String.valueOf(volume); - volumeLabel.setText("Volume: " + volumeText); - - // 星星按钮的可见性 -// updateFavoriteButtonVisibility(stock.getSymbol()); - } + final double volume = stock.getVolume(); + final String volumeText; - private void updateFavoriteButtonVisibility(String stockSymbol) { - // 隐藏 AAPL, COST, NVDA 的星星按钮 - if ("AAPL".equals(stockSymbol) || "COST".equals(stockSymbol) || "NVDA".equals(stockSymbol)) { - favoriteButton.setVisible(false); + if (volume >= MILLION) { + volumeText = (volume / MILLION) + " M"; + } + else if (volume >= THOUSAND) { + volumeText = (volume / THOUSAND) + " K"; } else { - favoriteButton.setVisible(true); + volumeText = String.valueOf(volume); } + + volumeLabel.setText("Volume: " + volumeText); + } + + private void updateFavoriteButtonVisibility(String stockSymbol) { + favoriteButton.setVisible(!"AAPL".equals(stockSymbol) && !"COST".equals(stockSymbol) + && !"NVDA".equals(stockSymbol)); } private void setButtonVisible(boolean visible) { @@ -165,15 +183,15 @@ private void setButtonVisible(boolean visible) { buyButton.setVisible(visible); } - private JLabel createLabel(String text, int fontSize, int fontStyle) { - final JLabel label = new JLabel(text); - label.setFont(new Font("SansSerif", fontStyle, fontSize)); + private JLabel createLabel(int fontSize, int fontStyle) { + final JLabel label = new JLabel(""); + label.setFont(new Font(FONT_FAMILY, fontStyle, fontSize)); label.setAlignmentX(Component.LEFT_ALIGNMENT); return label; } public String getViewName() { - return viewName; + return VIEW_NAME; } @Override @@ -196,7 +214,12 @@ public void propertyChange(PropertyChangeEvent evt) { } else if ("updateFavouriteButton".equals(evt.getPropertyName())) { // Toggle button text between filled and empty star - favoriteButton.setText(stockViewState.getIsFavorite() ? "★" : "☆"); + if (stockViewState.getIsFavorite()) { + favoriteButton.setText("\u2605"); + } + else { + favoriteButton.setText("\u2606"); + } } // Update login state @@ -207,3 +230,4 @@ public void setPreviousView(String viewName) { this.previousView = viewName; } } +