diff --git a/.gitignore b/.gitignore index 650c91720..1a73a2c8e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,7 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +### API KEY ### +.env \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4..35eb1ddfb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index 5e1d8b77c..7fa7754db 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,13 @@ -# Note Application +# Travel Advisors + +## Team Members + +- Teddy Yang (teddyyang8) +- Yasmina Mimassi (yasmatopia) +- Shirley Zhang (shirleyyzhang) +- Ray Fang (rayf5372) +- Sean Woo (seanwoo12) +## Starter Readme Below This is a minimal example demonstrating usage of the password-protected user part of the API used in lab 5. diff --git a/accessibility-report.md b/accessibility-report.md new file mode 100644 index 000000000..bb8c4a461 --- /dev/null +++ b/accessibility-report.md @@ -0,0 +1,33 @@ +The following paragraphs summarises how features in our program adhere to the Principle of Universal Design. + +Principle 1: Equitable Use + +The TravelAdvisor provides activity recommendations based on user-selected interests, benefiting all users equally. The activity suggestions are based solely on personal preferences in our profile creating feature, and our interest-selecting feature allows the users to enter any interest they may have, ensuring a diverse group of users can interact equitably. The profile creation and sign-up processes are identical for all users, fostering an equal and inclusive experience. + +Principle 2: Flexibility in Use + +Users can save places they are interested in and access them later, providing flexibility in how they interact with the app. The save places feature allows users to add different types of information, such as reviews and ratings, based on their preferences. Additionally, the interest-selection feature offers flexibility similar to a Google search, allowing users to input any interest they have. + +Principle 3: Simple and Intuitive Use + +The user interface follows a straightforward flow—users create a profile, select preferences, and receive personalized recommendations. Clear labels for text fields and buttons make the interface intuitive and easy to use. The platform delivers the intended service efficiently, without requiring extensive effort or comprehension from the user. + +Principle 4: Perceptible Information + +We use JSwing to present views, combining both textual and visual elements to enhance information accessibility. Labels, buttons, and other interface components are clearly labeled across all views, ensuring that users can easily perceive and understand the information and actions available. + +Principle 5: Tolerance in Error + +Error messages are displayed if, for instance, users try to create a profile with a username that already exists. This helps users easily identify and correct their mistakes. + +Principle 6: Low Physical Effort + +All elements of the user interface are designed to be simple to use and minimize repetitive actions. Users can navigate through different views intuitively, with large buttons and clear text fields. Features like the "New Search" button allow users to return to the search view directly after seeing suggested locations, reducing unnecessary steps and physical effort. + +Principle 7: Size and Space for Approach and Use + +All views in the TravelAdvisor provide a clean and well-organized layout, with adequate spacing between buttons and text fields, ensuring ease of interaction without feeling cramped. A future development goal is to integrate user accessibility tools, such as text-to-speech, to further enhance accessibility for all users. + +Our program is designed to be used by anyone interested in exploring new places or discovering interesting parts of their city. Our primary target audience for marketing would be frequent travelers, as they are more likely to be dissatisfied with generic interest recommendations found on platforms like Google Maps and would appreciate a more personalized experience. Another potential target audience could be local explorers—people who enjoy uncovering hidden gems within their own city. These users may be looking for unique recommendations that go beyond the popular tourist spots, tailored specifically to their personal interests. Additionally, our program could appeal to individuals who have specific niche hobbies, such as vinyl lovers seeking second-hand record stores, or history enthusiasts searching for lesser-known historical sites. + +When considering the medical model of disability, one demographic that is less likely to use our program would be individuals with visual impairment. In our program, individuals with visual impairments may face challenges since all features currently rely on visual cues, such as reading labels, navigating buttons, and being redirected to external pages. This reliance on visual interaction presents a significant barrier for visually impaired users, who might find it difficult or even impossible to use the program without external assistance. diff --git a/src/main/java/app/CalendarUseCaseFactory.java b/src/main/java/app/CalendarUseCaseFactory.java new file mode 100644 index 000000000..60dcc7f63 --- /dev/null +++ b/src/main/java/app/CalendarUseCaseFactory.java @@ -0,0 +1,55 @@ +package app; + +import interface_adapter.ViewManagerModel; +import interface_adapter.add_to_calendar.AddToCalendarController; +import interface_adapter.add_to_calendar.AddToCalendarPresenter; +import interface_adapter.add_to_calendar.AddToCalendarViewModel; +import interface_adapter.location.LocationViewModel; +import use_case.add_to_calendar.AddToCalendarDataAccessInterface; +import use_case.add_to_calendar.AddToCalendarInputBoundary; +import use_case.add_to_calendar.AddToCalendarInteractor; +import use_case.add_to_calendar.AddToCalendarOutputBoundary; +import view.CalendarView; + +/** + * This class contains the static factory function for creating the CalendarView. + */ +public class CalendarUseCaseFactory { + + /** Prevent instantiation. */ + private CalendarUseCaseFactory() { + + } + + /** + * Factory function for creating the CalendarView. + * @param viewManagerModel the ViewManagerModel to inject + * @param calendarViewModel the AddToCalendarViewModel to inject + * @param calendarDataAccessObject the AddToCalendarDataAccessObject to inject + * @param locationViewModel the LocationViewModel to inject + * @return the LocationView created for the provided input classes. + */ + public static CalendarView create(ViewManagerModel viewManagerModel, + AddToCalendarViewModel calendarViewModel, + AddToCalendarDataAccessInterface calendarDataAccessObject, + LocationViewModel locationViewModel) { + final AddToCalendarController calendarController = createCalendarUseCase(viewManagerModel, + calendarViewModel, calendarDataAccessObject, locationViewModel); + + return new CalendarView(calendarViewModel, calendarController); + } + + private static AddToCalendarController createCalendarUseCase( + ViewManagerModel viewManagerModel, + AddToCalendarViewModel calendarViewModel, + AddToCalendarDataAccessInterface calendarDataAccessObject, + LocationViewModel locationViewModel) { + final AddToCalendarOutputBoundary calendarOutputBoundary = new AddToCalendarPresenter(calendarViewModel, + viewManagerModel, locationViewModel); + final AddToCalendarInputBoundary calendarInteractor = new AddToCalendarInteractor(calendarDataAccessObject, + calendarOutputBoundary); + + return new AddToCalendarController(calendarInteractor); + } + +} diff --git a/src/main/java/app/LocationUseCaseFactory.java b/src/main/java/app/LocationUseCaseFactory.java new file mode 100644 index 000000000..6ec20dc30 --- /dev/null +++ b/src/main/java/app/LocationUseCaseFactory.java @@ -0,0 +1,62 @@ +package app; + +import interface_adapter.ViewManagerModel; +import interface_adapter.location.LocationController; +import interface_adapter.location.LocationPresenter; +import interface_adapter.location.LocationViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import use_case.locations.LocationDataAccessInterface; +import use_case.locations.LocationsInputBoundary; +import use_case.locations.LocationsInteractor; +import use_case.locations.LocationsOutputBoundary; +import use_case.user_profile.UserProfileDataAccessInterface; +import view.LocationView; + +/** + * This class contains the static factory function for creating the LocationView. + */ +public class LocationUseCaseFactory { + + /** Prevent instantiation. */ + private LocationUseCaseFactory() { + + } + + /** + * Factory function for creating the LocationView. + * @param viewManagerModel the ViewManagerModel to inject + * @param locationViewModel the LocationViewModel to inject + * @param suggestedLocationsViewModel the SuggestedLocationsViewModel to inject + * @param locationDataAccessObject the LocationDataAccessInterface to inject + * @param userDataAccessObject the UserProfileDataAccessInterface to inject + * @return the LocationView created for the provided input classes. + */ + public static LocationView create( + ViewManagerModel viewManagerModel, + LocationViewModel locationViewModel, + SuggestedLocationsViewModel suggestedLocationsViewModel, + LocationDataAccessInterface locationDataAccessObject, + UserProfileDataAccessInterface userDataAccessObject) { + + final LocationController locationController = createLocationUseCase(viewManagerModel, locationViewModel, + suggestedLocationsViewModel, locationDataAccessObject, userDataAccessObject); + + return new LocationView(locationViewModel, locationController); + } + + private static LocationController createLocationUseCase( + ViewManagerModel viewManagerModel, + LocationViewModel locationViewModel, + SuggestedLocationsViewModel suggestedLocationsViewModel, + LocationDataAccessInterface locationDataAccessObject, + UserProfileDataAccessInterface userDataAccessObject) { + + final LocationsOutputBoundary locationsOutputBoundary = new LocationPresenter(locationViewModel, + suggestedLocationsViewModel, viewManagerModel); + final LocationsInputBoundary locationInteractor = new LocationsInteractor(locationDataAccessObject, + locationsOutputBoundary, userDataAccessObject); + + return new LocationController(locationInteractor); + } + +} diff --git a/src/main/java/app/LoginUseCaseFactory.java b/src/main/java/app/LoginUseCaseFactory.java new file mode 100644 index 000000000..7d58eb83b --- /dev/null +++ b/src/main/java/app/LoginUseCaseFactory.java @@ -0,0 +1,66 @@ +package app; + +import interface_adapter.ViewManagerModel; +import interface_adapter.location.LocationViewModel; +import interface_adapter.signup.SignupViewModel; +import interface_adapter.user_profile.UserProfileViewModel; +import interface_adapter.login.LoginController; +import interface_adapter.login.LoginPresenter; +import interface_adapter.login.LoginViewModel; +import use_case.login.LoginInputBoundary; +import use_case.login.LoginInteractor; +import use_case.login.LoginOutputBoundary; +import use_case.login.LoginUserDataAccessInterface; +import view.LoginView; + +/** + * This class contains the static factory function for creating the LoginView. + */ +public final class LoginUseCaseFactory { + + /** Prevent instantiation. */ + private LoginUseCaseFactory() { + + } + + /** + * Factory function for creating the LoginView. + * @param viewManagerModel the ViewManagerModel to inject into the LoginView + * @param loginViewModel the LoginViewModel to inject into the LoginView + * @param userProfileViewModel the LoggedInViewModel to inject into the LoginView + * @param locationViewModel the LocationViewModel to inject into the LoginView + * @param userDataAccessObject the LoginUserDataAccessInterface to inject into the LoginView + * @param signupViewModel the SignupViewModel to inject into the LoginView + * @return the LoginView created for the provided input classes + */ + public static LoginView create( + ViewManagerModel viewManagerModel, + LoginViewModel loginViewModel, + UserProfileViewModel userProfileViewModel, + LocationViewModel locationViewModel, + LoginUserDataAccessInterface userDataAccessObject, + SignupViewModel signupViewModel) { + + final LoginController loginController = createLoginUseCase(viewManagerModel, loginViewModel, + userProfileViewModel, locationViewModel, userDataAccessObject, signupViewModel); + return new LoginView(loginViewModel, loginController); + + } + + private static LoginController createLoginUseCase( + ViewManagerModel viewManagerModel, + LoginViewModel loginViewModel, + UserProfileViewModel userProfileViewModel, + LocationViewModel locationViewModel, + LoginUserDataAccessInterface userDataAccessObject, + SignupViewModel signUpViewModel) { + + // Notice how we pass this method's parameters to the Presenter. + final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel, + userProfileViewModel, loginViewModel, locationViewModel, signUpViewModel); + final LoginInputBoundary loginInteractor = new LoginInteractor( + userDataAccessObject, loginOutputBoundary); + + return new LoginController(loginInteractor); + } +} diff --git a/src/main/java/app/MainNoteApplication.java b/src/main/java/app/MainNoteApplication.java deleted file mode 100644 index c37860156..000000000 --- a/src/main/java/app/MainNoteApplication.java +++ /dev/null @@ -1,56 +0,0 @@ -package app; - -import data_access.DBNoteDataAccessObject; -import use_case.note.NoteDataAccessInterface; - -/** - * An application where we can view and add to a note stored by a user. - *

- * This is a minimal example of using the password-protected user API from lab 5, - * but demonstrating the endpoint allowing you to store an arbitrary JSON object. - * This functionality could be used in any project where your team wants to persist - * data which is then accessible across devices.

- *

The code is intentionally somewhat incomplete to leave work to be done if your - * team were to choose to work on a project which would require similar functionality. - * For example, we have intentionally not created a full "Note" entity here, but - * rather just represented a note as a string. - *

- * The ViewManager code has also been removed, since this minimal program only requires a single - * view. Your team may wish to bring back the ViewManager or make your own implementation of supporting - * switching between views depending on your project. - */ -public class MainNoteApplication { - - /** - * The main entry point of the application. - *

- * The program will show you the note currently saved in the system. - * You are able to edit it and then save it to the system. You can refresh - * to update the note to reflect what was saved most recently. This - * uses the API from lab, so there is one database storing the note, - * which means that if anyone updates the note, that is what you will - * see when you refresh. - *

- * You can generalize the code to allow you to - * specify which "user" to save the note for, which will allow your team - * to store information specific to your team which is password-protected. - * The username and password used in this application are currently for - * user jonathan_calver2, but you can change that. As you did in lab 3, - * you will likely want to store password information locally rather than - * in your repo. Or you can require the user to enter their credentials - * in your application; it just depends on what your program's main - * functionality. - *

- * @param args commandline arguments are ignored - */ - public static void main(String[] args) { - - // create the data access and inject it into our builder! - final NoteDataAccessInterface noteDataAccess = new DBNoteDataAccessObject(); - - final NoteAppBuilder builder = new NoteAppBuilder(); - builder.addNoteDAO(noteDataAccess) - .addNoteView() - .addNoteUseCase().build().setVisible(true); - } -} diff --git a/src/main/java/app/MainWithDB.java b/src/main/java/app/MainWithDB.java new file mode 100644 index 000000000..d6d5ff536 --- /dev/null +++ b/src/main/java/app/MainWithDB.java @@ -0,0 +1,111 @@ +package app; + +import data_access.DBCoordinatesDataAccessObject; +import java.awt.*; + +import javax.swing.*; + +import data_access.DBLocationDataAccessObject; +import data_access.InMemoryCalendarDataAccessObject; +import data_access.InMemoryUserDataAccessObject; +import data_access.InMemoryCalendarDataAccessObject; +import entity.SuggestedPlaceFactory; +import interface_adapter.ViewManagerModel; +import interface_adapter.add_to_calendar.AddToCalendarViewModel; +import interface_adapter.location.LocationViewModel; +import interface_adapter.selectedlocation.SelectedLocationsViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import view.LocationView; +import view.SelectedLocationView; +import view.SuggestedLocationsView; +import view.ViewManager; + +import javax.swing.*; +import java.awt.*; +import interface_adapter.login.LoginViewModel; +import interface_adapter.signup.SignupViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import view.CalendarView; +import view.LocationView; +import view.SuggestedLocationsView; +import view.ViewManager; +import interface_adapter.user_profile.UserProfileViewModel; +import view.*; + +/** + * The version of Main with an external database used to persist user data. + */ +public class MainWithDB { + + /** + * The main method for starting the program with an external database used to persist user data. + * @param args input to main + */ + public static void main(String[] args) { + // Build the main program window, the main panel containing the + // various cards, and the layout, and stitch them together. + + // The main application window. + final JFrame application = new JFrame("Location Suggester"); + application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + final CardLayout cardLayout = new CardLayout(); + + final JPanel views = new JPanel(cardLayout); + application.add(views); + + final ViewManagerModel viewManagerModel = new ViewManagerModel(); + new ViewManager(views, cardLayout, viewManagerModel); + + final LoginViewModel loginViewModel = new LoginViewModel(); + final SignupViewModel signupViewModel = new SignupViewModel(); + final LocationViewModel locationViewModel = new LocationViewModel(); + final SuggestedLocationsViewModel suggestedLocationsViewModel = new SuggestedLocationsViewModel(); + final AddToCalendarViewModel calendarViewModel = new AddToCalendarViewModel(); + final SelectedLocationsViewModel selectedLocationsViewModel = new SelectedLocationsViewModel(); + final UserProfileViewModel userProfileViewModel = new UserProfileViewModel(); + // add any future view models here in the same way + + final DBLocationDataAccessObject locationDataAccessObject = new DBLocationDataAccessObject( + new SuggestedPlaceFactory()); + final InMemoryCalendarDataAccessObject calendarDataAccessObject = new InMemoryCalendarDataAccessObject(); + final DBCoordinatesDataAccessObject coordinatesDataAccessObject = new DBCoordinatesDataAccessObject(); + final InMemoryUserDataAccessObject userDataAccessObject = new InMemoryUserDataAccessObject(); + + final SignupView signupView = SignupUseCaseFactory.create(viewManagerModel, loginViewModel, + signupViewModel, userDataAccessObject); + views.add(signupView, signupView.getViewName()); + + final LoginView loginView = LoginUseCaseFactory.create(viewManagerModel, loginViewModel, + userProfileViewModel, locationViewModel, userDataAccessObject, signupViewModel); + views.add(loginView, loginView.getViewName()); + + final LocationView locationView = LocationUseCaseFactory.create(viewManagerModel, locationViewModel, + suggestedLocationsViewModel, locationDataAccessObject, userDataAccessObject); + views.add(locationView, locationView.getViewName()); + + final SuggestedLocationsView suggestedLocationsView = SuggestedLocationsUseCaseFactory.create(viewManagerModel, + suggestedLocationsViewModel, calendarViewModel, selectedLocationsViewModel, coordinatesDataAccessObject, + calendarDataAccessObject, locationViewModel); + views.add(suggestedLocationsView, suggestedLocationsView.getViewName()); + + final SelectedLocationView selectedLocationView = SelectedLocationsUseCaseFactory.create(viewManagerModel, + selectedLocationsViewModel, coordinatesDataAccessObject, locationViewModel); + views.add(selectedLocationView, selectedLocationView.getViewName()); + + final CalendarView calendarView = CalendarUseCaseFactory.create(viewManagerModel, calendarViewModel, + calendarDataAccessObject, locationViewModel); + views.add(calendarView, calendarView.getViewName()); + + final UserProfileView userProfileView = UserProfileUseCaseFactory.create(viewManagerModel, + userProfileViewModel, userDataAccessObject); + views.add(userProfileView, userProfileView.getViewName()); + + viewManagerModel.setState(signupView.getViewName()); + viewManagerModel.firePropertyChanged(); + + application.pack(); + application.setVisible(true); + } +} + diff --git a/src/main/java/app/NoteAppBuilder.java b/src/main/java/app/NoteAppBuilder.java deleted file mode 100644 index a68cb9ad6..000000000 --- a/src/main/java/app/NoteAppBuilder.java +++ /dev/null @@ -1,83 +0,0 @@ -package app; - -import javax.swing.JFrame; -import javax.swing.WindowConstants; - -import interface_adapter.note.NoteController; -import interface_adapter.note.NotePresenter; -import interface_adapter.note.NoteViewModel; -import use_case.note.NoteDataAccessInterface; -import use_case.note.NoteInteractor; -import use_case.note.NoteOutputBoundary; -import view.NoteView; - -/** - * Builder for the Note Application. - */ -public class NoteAppBuilder { - public static final int HEIGHT = 300; - public static final int WIDTH = 400; - private NoteDataAccessInterface noteDAO; - private NoteViewModel noteViewModel = new NoteViewModel(); - private NoteView noteView; - private NoteInteractor noteInteractor; - - /** - * Sets the NoteDAO to be used in this application. - * @param noteDataAccess the DAO to use - * @return this builder - */ - public NoteAppBuilder addNoteDAO(NoteDataAccessInterface noteDataAccess) { - noteDAO = noteDataAccess; - return this; - } - - /** - * Creates the objects for the Note Use Case and connects the NoteView to its - * controller. - *

This method must be called after addNoteView!

- * @return this builder - * @throws RuntimeException if this method is called before addNoteView - */ - public NoteAppBuilder addNoteUseCase() { - final NoteOutputBoundary noteOutputBoundary = new NotePresenter(noteViewModel); - noteInteractor = new NoteInteractor( - noteDAO, noteOutputBoundary); - - final NoteController controller = new NoteController(noteInteractor); - if (noteView == null) { - throw new RuntimeException("addNoteView must be called before addNoteUseCase"); - } - noteView.setNoteController(controller); - return this; - } - - /** - * Creates the NoteView and underlying NoteViewModel. - * @return this builder - */ - public NoteAppBuilder addNoteView() { - noteViewModel = new NoteViewModel(); - noteView = new NoteView(noteViewModel); - return this; - } - - /** - * Builds the application. - * @return the JFrame for the application - */ - public JFrame build() { - final JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - frame.setTitle("Note Application"); - frame.setSize(WIDTH, HEIGHT); - - frame.add(noteView); - - // refresh so that the note will be visible when we start the program - noteInteractor.executeRefresh(); - - return frame; - - } -} diff --git a/src/main/java/app/SelectedLocationsUseCaseFactory.java b/src/main/java/app/SelectedLocationsUseCaseFactory.java new file mode 100644 index 000000000..de2b0d3b4 --- /dev/null +++ b/src/main/java/app/SelectedLocationsUseCaseFactory.java @@ -0,0 +1,58 @@ +package app; + +import data_access.DBCoordinatesDataAccessObject; +import interface_adapter.ViewManagerModel; +import interface_adapter.location.LocationViewModel; +import interface_adapter.selectedlocation.SelectedLocationsController; +import interface_adapter.selectedlocation.SelectedLocationsPresenter; +import interface_adapter.selectedlocation.SelectedLocationsViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import use_case.selected_locations.SelectedLocationsInputBoundary; +import use_case.selected_locations.SelectedLocationsInteractor; +import use_case.selected_locations.SelectedLocationsOutputBoundary; +import view.SelectedLocationView; + +/** + * This class contains the static factory function for creating the SelectedLocationView. + */ +public class SelectedLocationsUseCaseFactory { + + /** Prevent instantiation. */ + private SelectedLocationsUseCaseFactory() { + + } + + /** + * Factory function for creating the LocationView. + * @param viewManagerModel the ViewManagerModel to inject + * @param selectedLocationsViewModel the SelectedLocationsViewModel to inject + * @param locationViewModel the LocationViewModel to inject + * @param coordinatesDataAccessObject the DBCoordinatesDataAccessObject to inject. + * @return the LocationView created for the provided input classes. + */ + public static SelectedLocationView create( + ViewManagerModel viewManagerModel, + SelectedLocationsViewModel selectedLocationsViewModel, + DBCoordinatesDataAccessObject coordinatesDataAccessObject, + LocationViewModel locationViewModel) { + + final SelectedLocationsController selectedLocationsController = + createSelectedLocationUseCase(viewManagerModel, selectedLocationsViewModel, + coordinatesDataAccessObject, locationViewModel); + return new SelectedLocationView(selectedLocationsViewModel, + selectedLocationsController); + } + + private static SelectedLocationsController createSelectedLocationUseCase( + ViewManagerModel viewManagerModel, + SelectedLocationsViewModel selectedLocationsViewModel, + DBCoordinatesDataAccessObject coordinatesDataAccessObject, + LocationViewModel locationViewModel) { + final SelectedLocationsOutputBoundary selectedLocationsOutputBoundary = new SelectedLocationsPresenter( + selectedLocationsViewModel, viewManagerModel, locationViewModel); + final SelectedLocationsInputBoundary selectedLocationsInteractor = new SelectedLocationsInteractor( + selectedLocationsOutputBoundary, coordinatesDataAccessObject); + + return new SelectedLocationsController(selectedLocationsInteractor); + } +} diff --git a/src/main/java/app/SignupUseCaseFactory.java b/src/main/java/app/SignupUseCaseFactory.java new file mode 100644 index 000000000..fafc1303b --- /dev/null +++ b/src/main/java/app/SignupUseCaseFactory.java @@ -0,0 +1,61 @@ +package app; + +import entity.UserFactory; +import interface_adapter.login.LoginViewModel; +import interface_adapter.signup.SignupController; +import interface_adapter.signup.SignupPresenter; +import interface_adapter.signup.SignupViewModel; +import interface_adapter.ViewManagerModel; +import use_case.signup.SignupInputBoundary; +import use_case.signup.SignupInteractor; +import use_case.signup.SignupOutputBoundary; +import use_case.signup.SignupUserDataAccessInterface; +import view.SignupView; + +/** + * Factory for creating the Signup use case. + */ +public final class SignupUseCaseFactory { + + /** Prevent instantiation. */ + private SignupUseCaseFactory() { + + } + + /** + * Factory method for creating the SignupView. + * + * @param viewManagerModel the ViewManagerModel + * @param loginViewModel the log in view model + * @param signupViewModel the sign up view model + * @param userDataAccessObject the user DAO + * @return a SignupView instance + */ + public static SignupView create(ViewManagerModel viewManagerModel, LoginViewModel loginViewModel, + SignupViewModel signupViewModel, + SignupUserDataAccessInterface userDataAccessObject) { + + final SignupController signupController = createUserSignupUseCase(viewManagerModel, signupViewModel, + loginViewModel, userDataAccessObject); + return new SignupView(signupController, signupViewModel); + } + + private static SignupController createUserSignupUseCase(ViewManagerModel viewManagerModel, + SignupViewModel signupViewModel, + LoginViewModel loginViewModel, + SignupUserDataAccessInterface userDataAccessObject) { + + // Notice how we pass this method's parameters to the Presenter. + final SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel, + signupViewModel, loginViewModel); + + final UserFactory userFactory = new UserFactory(); + + final SignupInputBoundary userSignupInteractor = new SignupInteractor( + userDataAccessObject, signupOutputBoundary, userFactory); + + return new SignupController(userSignupInteractor); + } +} + + diff --git a/src/main/java/app/SuggestedLocationsUseCaseFactory.java b/src/main/java/app/SuggestedLocationsUseCaseFactory.java new file mode 100644 index 000000000..4006503a0 --- /dev/null +++ b/src/main/java/app/SuggestedLocationsUseCaseFactory.java @@ -0,0 +1,117 @@ +package app; + +import interface_adapter.ViewManagerModel; +import interface_adapter.add_to_calendar.AddToCalendarPresenter; +import interface_adapter.location.LocationViewModel; +import interface_adapter.selectedlocation.SelectedLocationsPresenter; +import interface_adapter.selectedlocation.SelectedLocationsViewModel; +import interface_adapter.add_to_calendar.AddToCalendarController; +import interface_adapter.add_to_calendar.AddToCalendarViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsController; +import interface_adapter.suggestlocation.SuggestedLocationsPresenter; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import use_case.add_to_calendar.AddToCalendarDataAccessInterface; +import use_case.add_to_calendar.AddToCalendarInputBoundary; +import use_case.add_to_calendar.AddToCalendarInteractor; +import use_case.add_to_calendar.AddToCalendarOutputBoundary; +import use_case.selected_locations.CoordinateDataAccessInterface; +import use_case.selected_locations.SelectedLocationsInputBoundary; +import use_case.selected_locations.SelectedLocationsInteractor; +import use_case.selected_locations.SelectedLocationsOutputBoundary; +import use_case.suggested_locations.SuggestedLocationsInteractor; +import use_case.suggested_locations.SuggestedLocationsInputBoundary; +import interface_adapter.selectedlocation.SelectedLocationsController; +import use_case.suggested_locations.SuggestedLocationsOutputBoundary; +import view.SuggestedLocationsView; + +/** + * This class contains the static factory function for creating the SuggestedLocationView. + */ +public class SuggestedLocationsUseCaseFactory { + + /** Prevent instantiation. */ + private SuggestedLocationsUseCaseFactory() { + + } + + /** + * Factory function for creating the LocationView. + * @param viewManagerModel the ViewManagerModel to inject + * @param suggestedLocationsViewModel the SuggestedLocationsViewModel to inject + * @param selectedLocationsViewModel the SelectedLocationsViewModel to inject + * @param calendarViewModel the CalendarViewModel to inject + * @param coordinateDataAccessObject the CoordinateDataAccessInterface to inject + * @param calendarDataAccessObject the AddToCalendarDataAccessInterface to inject + * @param locationViewModel the LocationViewModel to inject + * @return the LocationView created for the provided input classes. + */ + public static SuggestedLocationsView create( + ViewManagerModel viewManagerModel, + SuggestedLocationsViewModel suggestedLocationsViewModel, + AddToCalendarViewModel calendarViewModel, + SelectedLocationsViewModel selectedLocationsViewModel, + CoordinateDataAccessInterface coordinateDataAccessObject, + AddToCalendarDataAccessInterface calendarDataAccessObject, + LocationViewModel locationViewModel) { + + final SelectedLocationsController selectedLocationController = createSelectedLocationUseCase(viewManagerModel, + selectedLocationsViewModel, coordinateDataAccessObject, locationViewModel); + + final SuggestedLocationsController suggestedLocationsController = createSuggestedLocationUseCase( + viewManagerModel, + suggestedLocationsViewModel, + calendarViewModel, + selectedLocationsViewModel); + + final AddToCalendarController calendarController = createAddToCalendarUseCase(viewManagerModel, + calendarViewModel, calendarDataAccessObject, locationViewModel); + + return new SuggestedLocationsView(suggestedLocationsViewModel, suggestedLocationsController, calendarViewModel, + calendarController, selectedLocationsViewModel, selectedLocationController); + } + + private static SuggestedLocationsController createSuggestedLocationUseCase( + ViewManagerModel viewManagerModel, + SuggestedLocationsViewModel suggestedLocationsViewModel, + AddToCalendarViewModel calendarViewModel, + SelectedLocationsViewModel selectedLocationsViewModel) { + + final SuggestedLocationsOutputBoundary suggestedLocationsOutputBoundary = new SuggestedLocationsPresenter( + viewManagerModel, suggestedLocationsViewModel, calendarViewModel, selectedLocationsViewModel); + + final SuggestedLocationsInputBoundary suggestedLocationsInteractor = new SuggestedLocationsInteractor( + suggestedLocationsOutputBoundary); + + return new SuggestedLocationsController(suggestedLocationsInteractor); + } + + private static SelectedLocationsController createSelectedLocationUseCase( + ViewManagerModel viewManagerModel, + SelectedLocationsViewModel selectedLocationsViewModel, + CoordinateDataAccessInterface coordinatesDataAccessObject, + LocationViewModel locationViewModel) { + final SelectedLocationsOutputBoundary selectedLocationsOutputBoundary = new SelectedLocationsPresenter( + selectedLocationsViewModel, viewManagerModel, locationViewModel); + final SelectedLocationsInputBoundary selectedLocationsInteractor = new SelectedLocationsInteractor( + selectedLocationsOutputBoundary, coordinatesDataAccessObject); + + return new SelectedLocationsController(selectedLocationsInteractor); + } + + private static AddToCalendarController createAddToCalendarUseCase( + ViewManagerModel viewManagerModel, + AddToCalendarViewModel calendarViewModel, + AddToCalendarDataAccessInterface calendarDataAccessObject, + LocationViewModel locationViewModel) { + + final AddToCalendarOutputBoundary calendarOutputBoundary = new AddToCalendarPresenter(calendarViewModel, + viewManagerModel, locationViewModel); + + final AddToCalendarInputBoundary calendarInteractor = new AddToCalendarInteractor(calendarDataAccessObject, + calendarOutputBoundary); + + return new AddToCalendarController(calendarInteractor); + } +} + + diff --git a/src/main/java/app/UserProfileUseCaseFactory.java b/src/main/java/app/UserProfileUseCaseFactory.java new file mode 100644 index 000000000..8b89f5dc6 --- /dev/null +++ b/src/main/java/app/UserProfileUseCaseFactory.java @@ -0,0 +1,54 @@ +package app; + +import data_access.InMemoryUserDataAccessObject; +import interface_adapter.ViewManagerModel; +import interface_adapter.user_profile.UserProfileController; +import interface_adapter.user_profile.UserProfilePresenter; +import interface_adapter.user_profile.UserProfileViewModel; +import use_case.user_profile.UserProfileDataAccessInterface; +import use_case.user_profile.UserProfileInputBoundary; +import use_case.user_profile.UserProfileInteractor; +import use_case.user_profile.UserProfileOutputBoundary; +import view.SuggestedLocationsView; +import view.UserProfileView; + +/** + * Factory for creating the User Profile use case. + */ +public final class UserProfileUseCaseFactory { + + /** Prevent instantiation. */ + private UserProfileUseCaseFactory() { + } + + /** + * Factory function for creating the LocationView. + * @param viewManagerModel the ViewManagerModel to inject + * @param userProfileViewModel the SuggestedLocationsViewModel to inject + * @param userDataAccessObject the In memory user data access object + * @return the LocationView created for the provided input classes. + */ + public static UserProfileView create( + ViewManagerModel viewManagerModel, + UserProfileViewModel userProfileViewModel, + UserProfileDataAccessInterface userDataAccessObject) { + + final UserProfileController userProfileController = createUserProfileUseCase(viewManagerModel, + userProfileViewModel, userDataAccessObject); + + return new UserProfileView(userProfileController, userProfileViewModel); + } + + private static UserProfileController createUserProfileUseCase( + ViewManagerModel viewManagerModel, + UserProfileViewModel userProfileViewModel, + UserProfileDataAccessInterface userDataAccessObject) { + + final UserProfileOutputBoundary userProfileOutputBoundary = new UserProfilePresenter( + viewManagerModel, userProfileViewModel); + final UserProfileInputBoundary userProfileInteractor = new UserProfileInteractor(userDataAccessObject, + userProfileOutputBoundary); + + return new UserProfileController(userProfileInteractor); + } +} diff --git a/src/main/java/data_access/DBCoordinatesDataAccessObject.java b/src/main/java/data_access/DBCoordinatesDataAccessObject.java new file mode 100644 index 000000000..1581e28a1 --- /dev/null +++ b/src/main/java/data_access/DBCoordinatesDataAccessObject.java @@ -0,0 +1,64 @@ +package data_access; + +import java.io.IOException; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import entity.Place; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import use_case.DataAccessException; +import use_case.selected_locations.CoordinateDataAccessInterface; + +/** + * The DAO for accessing places using Google Places API. + */ +public class DBCoordinatesDataAccessObject implements CoordinateDataAccessInterface { + private static final String API_KEY = System.getenv("API_KEY"); + + @Override + public String searchCoordinates(Place place) throws DataAccessException { + final OkHttpClient client = new OkHttpClient().newBuilder() + .build(); + final HttpUrl url = HttpUrl.parse("https://maps.googleapis" + + ".com/maps/api/geocode/json").newBuilder() + .addQueryParameter("key", API_KEY) + .addQueryParameter("address", place.getAddress()) + .build(); + // POST METHOD + final Request request = new Request.Builder() + .url(url) + .build(); + try (Response response = client.newCall(request).execute()) { + final JSONObject responseBody = new JSONObject(response.body().string()); + + if (responseBody.has("results")) { + final StringBuilder coordinates = new StringBuilder(); + final JSONArray jsonArray = responseBody.getJSONArray("results"); + final JSONObject firstResult = jsonArray.getJSONObject(0); + final JSONObject geometry = firstResult.getJSONObject("geometry"); + final JSONObject location = geometry.getJSONObject("location"); + + final double lat = location.getDouble("lat"); + final double lng = location.getDouble("lng"); + + coordinates.append(lat).append(",").append(lng); + return coordinates.toString(); + } + else if (responseBody.has("error_message")) { + throw new DataAccessException("API Error: " + responseBody.getJSONObject("error_message") + .getString("message")); + } + else { + throw new DataAccessException("Unexpected API response format."); + } + } + catch (IOException | JSONException | DataAccessException ex) { + throw new DataAccessException(ex.getMessage()); + } + } +} diff --git a/src/main/java/data_access/DBLocationDataAccessObject.java b/src/main/java/data_access/DBLocationDataAccessObject.java new file mode 100644 index 000000000..c1ec0328d --- /dev/null +++ b/src/main/java/data_access/DBLocationDataAccessObject.java @@ -0,0 +1,94 @@ +package data_access; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import entity.Place; +import entity.PlaceFactory; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import use_case.DataAccessException; +import use_case.locations.LocationDataAccessInterface; + +/** + * The DAO for accessing places using Google Places API. + */ +public class DBLocationDataAccessObject implements LocationDataAccessInterface { + private static final String API_HEADER = "X-Goog-Api-Key"; + private static final String API_FIELD = "X-Goog-FieldMask"; + private static final String FIELDS = "places.displayName,places.formattedAddress"; + private static final String CONTENT_TYPE_LABEL = "Content-Type"; + private static final String CONTENT_TYPE_JSON = "application/json"; + private static final String API_KEY = System.getenv("API_KEY"); + private static final String API_URL = "https://places.googleapis.com/v1/places:searchText"; + private static final String LEFT_ARROW = "<"; + private static final String RIGHT_ARROW = ">"; + private final PlaceFactory placeFactory; + + public DBLocationDataAccessObject(PlaceFactory placeFactory) { + this.placeFactory = placeFactory; + } + + @Override + public List searchLocation(String address, String locationType) throws DataAccessException { + final OkHttpClient client = new OkHttpClient().newBuilder() + .build(); + + final JSONObject requestBody = new JSONObject(); + requestBody.put("textQuery", locationType + " near " + address); + final RequestBody body = RequestBody.create( + requestBody.toString(), MediaType.parse(CONTENT_TYPE_JSON)); + // POST METHOD + final Request request = new Request.Builder() + .url(API_URL) + .post(body) + .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON) + .addHeader(API_HEADER, API_KEY) + .addHeader(API_FIELD, FIELDS) + .build(); + try (Response response = client.newCall(request).execute()) { + + final JSONObject responseBody = new JSONObject(response.body().string()); + + if (responseBody.has("places")) { + final StringBuilder places = new StringBuilder(); + final JSONArray jsonArray = responseBody.getJSONArray("places"); + for (int i = 0; i < jsonArray.length(); i++) { + final JSONObject jsonObject = + jsonArray.getJSONObject(i); + places.append(jsonObject.getString("formattedAddress")).append(RIGHT_ARROW).append(jsonObject + .getJSONObject("displayName").getString("text")).append(LEFT_ARROW); + } + final String placesString = places.toString(); + + final String[] locationsList = placesString.split(LEFT_ARROW); + final List suggestedPlaces = new ArrayList<>(); + for (String location : locationsList) { + final Place place = placeFactory.create(location.split(RIGHT_ARROW)[1], + location.split(RIGHT_ARROW)[0]); + suggestedPlaces.add(place); + } + return suggestedPlaces; + + } + else if (responseBody.has("error")) { + throw new DataAccessException("API Error: " + responseBody.getJSONObject("error").getString("message")); + } + else { + throw new DataAccessException("Unexpected API response format."); + } + } + catch (IOException | JSONException | DataAccessException ex) { + throw new DataAccessException(ex.getMessage()); + } + } + +} diff --git a/src/main/java/data_access/DBNoteDataAccessObject.java b/src/main/java/data_access/DBNoteDataAccessObject.java deleted file mode 100644 index dadb0cab0..000000000 --- a/src/main/java/data_access/DBNoteDataAccessObject.java +++ /dev/null @@ -1,107 +0,0 @@ -package data_access; - -import java.io.IOException; - -import org.json.JSONException; -import org.json.JSONObject; - -import entity.User; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import use_case.note.DataAccessException; -import use_case.note.NoteDataAccessInterface; - -/** - * The DAO for accessing notes stored in the database. - *

This class demonstrates how your group can use the password-protected user - * endpoints of the API used in lab 5 to store persistent data in your program. - *

- *

You can also refer to the lab 5 code for signing up a new user and other use cases. - *

- * See - * - * the documentation - * of the API for more details. - */ -public class DBNoteDataAccessObject implements NoteDataAccessInterface { - private static final int SUCCESS_CODE = 200; - private static final int CREDENTIAL_ERROR = 401; - 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"; - private static final String USERNAME = "username"; - private static final String PASSWORD = "password"; - private static final String MESSAGE = "message"; - - @Override - public String saveNote(User user, String note) throws DataAccessException { - final OkHttpClient client = new OkHttpClient().newBuilder() - .build(); - - // POST METHOD - final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON); - final JSONObject requestBody = new JSONObject(); - requestBody.put(USERNAME, user.getName()); - requestBody.put(PASSWORD, user.getPassword()); - final JSONObject extra = new JSONObject(); - extra.put("note", note); - requestBody.put("info", extra); - final RequestBody body = RequestBody.create(requestBody.toString(), mediaType); - final Request request = new Request.Builder() - .url("http://vm003.teach.cs.toronto.edu:20112/modifyUserInfo") - .method("PUT", body) - .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON) - .build(); - try { - final Response response = client.newCall(request).execute(); - - final JSONObject responseBody = new JSONObject(response.body().string()); - - if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) { - return loadNote(user); - } - else if (responseBody.getInt(STATUS_CODE_LABEL) == CREDENTIAL_ERROR) { - throw new DataAccessException("message could not be found or password was incorrect"); - } - else { - throw new DataAccessException("database error: " + responseBody.getString(MESSAGE)); - } - } - catch (IOException | JSONException ex) { - throw new DataAccessException(ex.getMessage()); - } - } - - @Override - public String loadNote(User user) throws DataAccessException { - // Make an API call to get the user object. - final String username = user.getName(); - final OkHttpClient client = new OkHttpClient().newBuilder().build(); - final Request request = new Request.Builder() - .url(String.format("http://vm003.teach.cs.toronto.edu:20112/user?username=%s", username)) - .addHeader("Content-Type", CONTENT_TYPE_JSON) - .build(); - try { - final Response response = client.newCall(request).execute(); - - final JSONObject responseBody = new JSONObject(response.body().string()); - - if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) { - final JSONObject userJSONObject = responseBody.getJSONObject("user"); - final JSONObject data = userJSONObject.getJSONObject("info"); - return data.getString("note"); - } - else { - throw new DataAccessException(responseBody.getString(MESSAGE)); - } - } - catch (IOException | JSONException ex) { - throw new RuntimeException(ex); - } - } -} diff --git a/src/main/java/data_access/InMemoryCalendarDataAccessObject.java b/src/main/java/data_access/InMemoryCalendarDataAccessObject.java new file mode 100644 index 000000000..372312d8f --- /dev/null +++ b/src/main/java/data_access/InMemoryCalendarDataAccessObject.java @@ -0,0 +1,31 @@ +package data_access; + +import java.util.HashMap; +import java.util.Map; + +import entity.Place; +import use_case.add_to_calendar.AddToCalendarDataAccessInterface; + +/** + * In-memory implementation of the DAO for storing calendar data. This implementation does + * NOT persist data between runs of the program. + */ +public class InMemoryCalendarDataAccessObject implements AddToCalendarDataAccessInterface { + + private final Map calendarItems = new HashMap<>(); + + @Override + public boolean hasLocation(Place location) { + return calendarItems.containsKey(location); + } + + @Override + public boolean hasTime(String time) { + return calendarItems.containsValue(time); + } + + @Override + public void save(Place location, String time) { + calendarItems.put(location, time); + } +} diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java new file mode 100644 index 000000000..1faa11526 --- /dev/null +++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java @@ -0,0 +1,74 @@ +package data_access; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import entity.Place; +import entity.User; +import use_case.DataAccessException; +import use_case.login.LoginUserDataAccessInterface; +import use_case.signup.SignupUserDataAccessInterface; +import use_case.user_profile.UserProfileDataAccessInterface; + +/** + * In-memory implementation for storing user data for both sign-up and user profile. + */ +public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterface, + LoginUserDataAccessInterface, UserProfileDataAccessInterface { + + private static final String USER_NOT_FOUND = "User not found."; + private final Map userDatabase = new HashMap<>(); + + @Override + public void save(User user) { + userDatabase.put(user.getName(), user); + } + + @Override + public User get(String username) { + return userDatabase.get(username); + } + + @Override + public User getUser(String username) throws DataAccessException { + final User user = userDatabase.get(username); + if (user == null) { + throw new DataAccessException(USER_NOT_FOUND); + } + return user; + } + + @Override + public boolean existsByName(String username) { + return userDatabase.containsKey(username); + } + + @Override + public void savePlaces(String username, Map> places) throws DataAccessException { + final User user = getUser(username); + if (user != null) { + user.getSavedPlaces().putAll(places); + } + else { + throw new DataAccessException(USER_NOT_FOUND); + } + } + + @Override + public Map> getSavedPlaces(String username) throws DataAccessException { + final User user = getUser(username); + if (user != null) { + return user.getSavedPlaces(); + } + else { + throw new DataAccessException(USER_NOT_FOUND); + } + } + + // This method is added to fulfill the requirement of SignupUserDataAccessInterface + @Override + public User getSignupUser(String username) throws DataAccessException { + return getUser(username); + } +} diff --git a/src/main/java/entity/DislikedLocationFilter.java b/src/main/java/entity/DislikedLocationFilter.java new file mode 100644 index 000000000..62c1ea342 --- /dev/null +++ b/src/main/java/entity/DislikedLocationFilter.java @@ -0,0 +1,16 @@ +package entity; + +import java.util.Set; + +public class DislikedLocationFilter implements LocationFilterStrategy { + private Set dislikedLocations; + + public DislikedLocationFilter(Set dislikedLocations) { + this.dislikedLocations = dislikedLocations; + } + + @Override + public boolean filter(String location) { + return !dislikedLocations.contains(location); + } +} diff --git a/src/main/java/entity/LocationFilterContext.java b/src/main/java/entity/LocationFilterContext.java new file mode 100644 index 000000000..cef58a9a9 --- /dev/null +++ b/src/main/java/entity/LocationFilterContext.java @@ -0,0 +1,17 @@ +package entity; + +public class LocationFilterContext { + private LocationFilterStrategy strategy; + + public LocationFilterContext(LocationFilterStrategy strategy) { + this.strategy = strategy; + } + + public void setStrategy(LocationFilterStrategy strategy) { + this.strategy = strategy; + } + + public boolean applyFilter(String location) { + return strategy.filter(location); + } +} diff --git a/src/main/java/entity/LocationFilterStrategy.java b/src/main/java/entity/LocationFilterStrategy.java new file mode 100644 index 000000000..d0b27e733 --- /dev/null +++ b/src/main/java/entity/LocationFilterStrategy.java @@ -0,0 +1,8 @@ +package entity; + +/** + * The representation of a filter in our program. + */ +public interface LocationFilterStrategy { + boolean filter(String location); +} diff --git a/src/main/java/entity/Place.java b/src/main/java/entity/Place.java new file mode 100644 index 000000000..e916838d0 --- /dev/null +++ b/src/main/java/entity/Place.java @@ -0,0 +1,20 @@ +package entity; + +/** + * The representation of a place in our program. + */ +public interface Place { + /** + * Returns the address of the place. + * + * @return the address of the place. + */ + String getName(); + + /** + * Returns the address of the place. + * + * @return the address of the place. + */ + String getAddress(); +} diff --git a/src/main/java/entity/PlaceFactory.java b/src/main/java/entity/PlaceFactory.java new file mode 100644 index 000000000..135c32c1c --- /dev/null +++ b/src/main/java/entity/PlaceFactory.java @@ -0,0 +1,14 @@ +package entity; + +/** + * Factory for creating places. + */ +public interface PlaceFactory { + /** + * Creates a new Place. + * @param name the name of the new place + * @param address the address of the new place + * @return the new place + */ + Place create(String name, String address); +} diff --git a/src/main/java/entity/SavedLocationFilter.java b/src/main/java/entity/SavedLocationFilter.java new file mode 100644 index 000000000..02948ec99 --- /dev/null +++ b/src/main/java/entity/SavedLocationFilter.java @@ -0,0 +1,16 @@ +package entity; + +import java.util.Set; + +public class SavedLocationFilter implements LocationFilterStrategy { + private Set savedLocations; + + public SavedLocationFilter(Set savedLocations) { + this.savedLocations = savedLocations; + } + + @Override + public boolean filter(String location) { + return savedLocations.contains(location); + } +} diff --git a/src/main/java/entity/SavedPlace.java b/src/main/java/entity/SavedPlace.java new file mode 100644 index 000000000..9b74e9cf0 --- /dev/null +++ b/src/main/java/entity/SavedPlace.java @@ -0,0 +1,38 @@ + +package entity; + +/** + * An implementation of the Place interface. + */ +public class SavedPlace implements Place { + + private final String name; + private final String address; + private final String review; + private final boolean rating; + + public SavedPlace(String name, String address, String review, boolean rating) { + this.name = name; + this.address = address; + this.review = review; + this.rating = rating; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getAddress() { + return address; + } + + public String getReview() { + return review; + } + + public boolean getRating() { + return rating; + } +} \ No newline at end of file diff --git a/src/main/java/entity/SavedPlaceFactory.java b/src/main/java/entity/SavedPlaceFactory.java new file mode 100644 index 000000000..722093cf8 --- /dev/null +++ b/src/main/java/entity/SavedPlaceFactory.java @@ -0,0 +1,12 @@ +package entity; + +/** + * Factory for creating SuggestedPlace objects. + */ +public class SavedPlaceFactory implements PlaceFactory { + + @Override + public Place create(String name, String address) { + return new SavedPlace(name, address, "", true); + } +} diff --git a/src/main/java/entity/SuggestedPlace.java b/src/main/java/entity/SuggestedPlace.java new file mode 100644 index 000000000..e3b092b53 --- /dev/null +++ b/src/main/java/entity/SuggestedPlace.java @@ -0,0 +1,26 @@ + +package entity; + +/** + * An implementation of the Place interface. + */ +public class SuggestedPlace implements Place { + + private final String name; + private final String address; + + public SuggestedPlace(String name, String address) { + this.name = name; + this.address = address; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getAddress() { + return address; + } +} \ No newline at end of file diff --git a/src/main/java/entity/SuggestedPlaceFactory.java b/src/main/java/entity/SuggestedPlaceFactory.java new file mode 100644 index 000000000..83b81bb6c --- /dev/null +++ b/src/main/java/entity/SuggestedPlaceFactory.java @@ -0,0 +1,12 @@ +package entity; + +/** + * Factory for creating SuggestedPlace objects. + */ +public class SuggestedPlaceFactory implements PlaceFactory { + + @Override + public Place create(String name, String address) { + return new SuggestedPlace(name, address); + } +} diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java index e0c57e9a6..9d5833990 100644 --- a/src/main/java/entity/User.java +++ b/src/main/java/entity/User.java @@ -1,5 +1,9 @@ package entity; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * The representation of a password-protected user for our program. */ @@ -7,10 +11,12 @@ public class User { private final String name; private final String password; + private final Map> savedPlaces; - public User(String name, String password) { + public User(String name, String password, HashMap> savedPlaces) { this.name = name; this.password = password; + this.savedPlaces = savedPlaces; } public String getName() { @@ -21,4 +27,7 @@ public String getPassword() { return password; } + public Map> getSavedPlaces() { + return savedPlaces; + } } diff --git a/src/main/java/entity/UserFactory.java b/src/main/java/entity/UserFactory.java new file mode 100644 index 000000000..500e11e8f --- /dev/null +++ b/src/main/java/entity/UserFactory.java @@ -0,0 +1,20 @@ +package entity; + +import java.util.HashMap; +import java.util.List; + +/** + * Factory for creating User entities. + */ +public class UserFactory { + + /** + * Create a new user with the given username and password. + * @param username the user's username + * @param password the user's password + * @return a new User instance + */ + public User create(String username, String password) { + return new User(username, password, new HashMap>()); + } +} diff --git a/src/main/java/interface_adapter/ViewManagerModel.java b/src/main/java/interface_adapter/ViewManagerModel.java new file mode 100644 index 000000000..99dc0ffe5 --- /dev/null +++ b/src/main/java/interface_adapter/ViewManagerModel.java @@ -0,0 +1,14 @@ +package interface_adapter; + +/** + * 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 { + + public ViewManagerModel() { + super("view manager"); + this.setState(""); + } + +} diff --git a/src/main/java/interface_adapter/ViewModel.java b/src/main/java/interface_adapter/ViewModel.java index 130d797a1..f4ceddcf5 100644 --- a/src/main/java/interface_adapter/ViewModel.java +++ b/src/main/java/interface_adapter/ViewModel.java @@ -45,6 +45,7 @@ public void firePropertyChanged() { * Fires a property changed event for the state of this ViewModel, which * allows the user to specify a different propertyName. This can be useful * when a class is listening for multiple kinds of property changes. + * *

For example, the LoggedInView listens for two kinds of property changes; * it can use the property name to distinguish which property has changed.

* @param propertyName the label for the property that was changed diff --git a/src/main/java/interface_adapter/add_to_calendar/AddToCalendarController.java b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarController.java new file mode 100644 index 000000000..9841903b3 --- /dev/null +++ b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarController.java @@ -0,0 +1,39 @@ +package interface_adapter.add_to_calendar; + +import java.util.Map; + +import entity.Place; +import use_case.DataAccessException; +import use_case.add_to_calendar.AddToCalendarInputBoundary; +import use_case.add_to_calendar.AddToCalendarInputData; + +/** + * The AddToCalendarController class handles user input related to adding locations to calendar. + * It interacts with the AddToCalendarInputBoundary to process the input and perform actions. + */ +public class AddToCalendarController { + + private final AddToCalendarInputBoundary addToCalendarInteractor; + + public AddToCalendarController(AddToCalendarInputBoundary calendarInteractor) { + this.addToCalendarInteractor = calendarInteractor; + } + + /** + * Executes the show calendar. + * @param addToCalendarData the locations and time map to add. + * @throws DataAccessException if data cannot be accessed + */ + public void execute(Map addToCalendarData) throws DataAccessException { + final AddToCalendarInputData calendarInputData = new AddToCalendarInputData(addToCalendarData); + + addToCalendarInteractor.execute(calendarInputData); + } + + /** + * Executes the switch to location view. + */ + public void switchToLocationView() { + addToCalendarInteractor.switchToLocationView(); + } +} diff --git a/src/main/java/interface_adapter/add_to_calendar/AddToCalendarPresenter.java b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarPresenter.java new file mode 100644 index 000000000..343d4d72c --- /dev/null +++ b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarPresenter.java @@ -0,0 +1,49 @@ +package interface_adapter.add_to_calendar; + +import interface_adapter.ViewManagerModel; +import interface_adapter.location.LocationViewModel; +import use_case.add_to_calendar.AddToCalendarOutputBoundary; +import use_case.add_to_calendar.AddToCalendarOutputData; + +/** + * The Presenter for the Add to Calendar Use Case. + */ +public class AddToCalendarPresenter implements AddToCalendarOutputBoundary { + + private final AddToCalendarViewModel calendarViewModel; + private final ViewManagerModel viewManagerModel; + private final LocationViewModel locationViewModel; + + public AddToCalendarPresenter(AddToCalendarViewModel calendarViewModel, ViewManagerModel viewManagerModel, + LocationViewModel locationViewModel) { + this.calendarViewModel = calendarViewModel; + this.viewManagerModel = viewManagerModel; + this.locationViewModel = locationViewModel; + } + + + @Override + public void prepareSuccessView(AddToCalendarOutputData outputData) { + // On success, switch to the Calendar view. + final AddToCalendarState calendarState = calendarViewModel.getState(); + calendarState.setCalendarItems(outputData.getcalendarItems()); + this.calendarViewModel.setState(calendarState); + this.calendarViewModel.firePropertyChanged(); + + this.viewManagerModel.setState(calendarViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + + @Override + public void prepareFailView(String errorMessage) { + final AddToCalendarState calendarState = calendarViewModel.getState(); + calendarState.setAddError(errorMessage); + calendarViewModel.firePropertyChanged(); + } + + @Override + public void switchToLocationView() { + viewManagerModel.setState(locationViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/add_to_calendar/AddToCalendarState.java b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarState.java new file mode 100644 index 000000000..5f4aeb659 --- /dev/null +++ b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarState.java @@ -0,0 +1,29 @@ +package interface_adapter.add_to_calendar; + +import java.util.Map; + +import entity.Place; + +/** + * The state representing calendar-related data, including locations, times, and any error messages. + */ +public class AddToCalendarState { + private Map calendarItems; + private String addError; + + public Map getCalendarItems() { + return calendarItems; + } + + public String getAddError() { + return addError; + } + + public void setCalendarItems(Map calendarItems) { + this.calendarItems = calendarItems; + } + + public void setAddError(String addError) { + this.addError = addError; + } +} diff --git a/src/main/java/interface_adapter/add_to_calendar/AddToCalendarViewModel.java b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarViewModel.java new file mode 100644 index 000000000..f4b9ee9f9 --- /dev/null +++ b/src/main/java/interface_adapter/add_to_calendar/AddToCalendarViewModel.java @@ -0,0 +1,14 @@ +package interface_adapter.add_to_calendar; + +import interface_adapter.ViewModel; + +/** + * The View Model for the Add to Calendar View. + */ +public class AddToCalendarViewModel extends ViewModel { + + public AddToCalendarViewModel() { + super("Calendar"); + setState(new AddToCalendarState()); + } +} diff --git a/src/main/java/interface_adapter/location/LocationController.java b/src/main/java/interface_adapter/location/LocationController.java new file mode 100644 index 000000000..64614657b --- /dev/null +++ b/src/main/java/interface_adapter/location/LocationController.java @@ -0,0 +1,35 @@ +package interface_adapter.location; + +import use_case.DataAccessException; +import use_case.locations.LocationsInputBoundary; +import use_case.locations.LocationsInputData; + +/** + * The LocationController class handles user input related to locations. + * It interacts with the LocationInputBoundary to process the input and + * perform actions such as setting the city, address, and interests, + * as well as executing save or refresh operations. + */ +public class LocationController { + + private final LocationsInputBoundary locationInput; + + public LocationController(LocationsInputBoundary locationInteractor) { + this.locationInput = locationInteractor; + } + + /** + * Executes the show location. + * @param address the location to show. + * @param locationType the type of location to search. + * @param currentFilter the type of filter to search with. + * @param username the username of the current user. + * @throws DataAccessException if data cannot be accessed + */ + public void execute(String address, String locationType, String currentFilter, String username) throws DataAccessException { + final LocationsInputData locationInputData = new LocationsInputData( + address, locationType, username); + + locationInput.execute(locationInputData, currentFilter); + } +} diff --git a/src/main/java/interface_adapter/location/LocationPresenter.java b/src/main/java/interface_adapter/location/LocationPresenter.java new file mode 100644 index 000000000..781ad81f7 --- /dev/null +++ b/src/main/java/interface_adapter/location/LocationPresenter.java @@ -0,0 +1,44 @@ +package interface_adapter.location; + +import interface_adapter.ViewManagerModel; +import interface_adapter.suggestlocation.SuggestedLocationsState; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import use_case.locations.LocationsOutputBoundary; +import use_case.locations.LocationsOutputData; + +/** + * The Presenter for the Location Use Case. + */ +public class LocationPresenter implements LocationsOutputBoundary { + + private final LocationViewModel locationViewModel; + private final SuggestedLocationsViewModel suggestedLocationsViewModel; + private final ViewManagerModel viewManagerModel; + + public LocationPresenter(LocationViewModel locationViewModel, + SuggestedLocationsViewModel suggestedLocationsViewModel, + ViewManagerModel viewManagerModel) { + this.locationViewModel = locationViewModel; + this.suggestedLocationsViewModel = suggestedLocationsViewModel; + this.viewManagerModel = viewManagerModel; + } + + @Override + public void prepareSuccessView(LocationsOutputData response) { + // On success, switch to the suggested locations view. + final SuggestedLocationsState suggestedLocationsState = suggestedLocationsViewModel.getState(); + suggestedLocationsState.setSuggestedLocations(response.getLocations()); + this.suggestedLocationsViewModel.setState(suggestedLocationsState); + this.suggestedLocationsViewModel.firePropertyChanged(); + + this.viewManagerModel.setState(suggestedLocationsViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + + @Override + public void prepareFailView(String error) { + final LocationState locationState = locationViewModel.getState(); + locationState.setError(error); + locationViewModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/location/LocationState.java b/src/main/java/interface_adapter/location/LocationState.java new file mode 100644 index 000000000..d3c061d0c --- /dev/null +++ b/src/main/java/interface_adapter/location/LocationState.java @@ -0,0 +1,45 @@ +package interface_adapter.location; + +/** + * The state representing location-related data, including city, address, keywords, + * suggested locations, and any error messages. + */ +public class LocationState { + private String address = ""; + private String locationType = ""; + private String username = ""; + private String error; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getLocationType() { + return locationType; + } + + public void setLocationType(String locationType) { + this.locationType = locationType; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUsername() { + return username; + } +} + diff --git a/src/main/java/interface_adapter/location/LocationViewModel.java b/src/main/java/interface_adapter/location/LocationViewModel.java new file mode 100644 index 000000000..6af84c69b --- /dev/null +++ b/src/main/java/interface_adapter/location/LocationViewModel.java @@ -0,0 +1,13 @@ +package interface_adapter.location; + +import interface_adapter.ViewModel; + +/** + * The ViewModel for the LocationView. + */ +public class LocationViewModel extends ViewModel { + public LocationViewModel() { + super("Search Locations"); + setState(new LocationState()); + } +} diff --git a/src/main/java/interface_adapter/login/LoginController.java b/src/main/java/interface_adapter/login/LoginController.java new file mode 100644 index 000000000..6f58ab10b --- /dev/null +++ b/src/main/java/interface_adapter/login/LoginController.java @@ -0,0 +1,32 @@ +package interface_adapter.login; + +import use_case.login.LoginInputBoundary; +import use_case.login.LoginInputData; + +/** + * The controller for the Login Use Case. + */ +public class LoginController { + + private final LoginInputBoundary loginUseCaseInteractor; + + public LoginController(LoginInputBoundary loginUseCaseInteractor) { + this.loginUseCaseInteractor = loginUseCaseInteractor; + } + + /** + * Executes the Login Use Case. + * @param username the username of the user logging in + * @param password the password of the user logging in + */ + public void execute(String username, String password) { + final LoginInputData loginInputData = new LoginInputData( + username, password); + + loginUseCaseInteractor.execute(loginInputData); + } + + 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 new file mode 100644 index 000000000..6f04e738b --- /dev/null +++ b/src/main/java/interface_adapter/login/LoginPresenter.java @@ -0,0 +1,63 @@ +package interface_adapter.login; + +import interface_adapter.ViewManagerModel; +import interface_adapter.location.LocationState; +import interface_adapter.location.LocationViewModel; +import interface_adapter.signup.SignupViewModel; +import interface_adapter.user_profile.UserProfileState; +import interface_adapter.user_profile.UserProfileViewModel; +import use_case.login.LoginOutputBoundary; +import use_case.login.LoginOutputData; + +/** + * The Presenter for the Login Use Case. + */ +public class LoginPresenter implements LoginOutputBoundary { + + private final LoginViewModel loginViewModel; + private final UserProfileViewModel userProfileViewModel; + private final LocationViewModel locationViewModel; + private final ViewManagerModel viewManagerModel; + private final SignupViewModel signUpViewModel; + + public LoginPresenter(ViewManagerModel viewManagerModel, + UserProfileViewModel userProfileViewModel, + LoginViewModel loginViewModel, + LocationViewModel locationViewModel, + SignupViewModel signUpViewModel) { + this.viewManagerModel = viewManagerModel; + this.userProfileViewModel = userProfileViewModel; + this.loginViewModel = loginViewModel; + this.locationViewModel = locationViewModel; + this.signUpViewModel = signUpViewModel; + } + + @Override + public void prepareSuccessView(LoginOutputData response) { + // On success, switch to the logged in view. + + final UserProfileState userProfileState = userProfileViewModel.getState(); + userProfileState.setUsername(response.getUsername()); + this.userProfileViewModel.setState(userProfileState); + this.userProfileViewModel.firePropertyChanged(); + final LocationState locationState = locationViewModel.getState(); + locationState.setUsername(response.getUsername()); + this.locationViewModel.setState(locationState); + + this.viewManagerModel.setState(locationViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + + @Override + public void prepareFailView(String error) { + final LoginState loginState = loginViewModel.getState(); + loginState.setLoginError(error); + loginViewModel.firePropertyChanged(); + } + + @Override + public void switchToSignUpView() { + viewManagerModel.setState(signUpViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/login/LoginState.java b/src/main/java/interface_adapter/login/LoginState.java new file mode 100644 index 000000000..e42632034 --- /dev/null +++ b/src/main/java/interface_adapter/login/LoginState.java @@ -0,0 +1,35 @@ +package interface_adapter.login; + +/** + * The state for the Login View Model. + */ +public class LoginState { + private String username = ""; + private String loginError; + private String password = ""; + + public String getUsername() { + return username; + } + + public String getLoginError() { + return loginError; + } + + public String getPassword() { + return password; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setLoginError(String usernameError) { + this.loginError = usernameError; + } + + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/src/main/java/interface_adapter/login/LoginViewModel.java b/src/main/java/interface_adapter/login/LoginViewModel.java new file mode 100644 index 000000000..41bea7a2e --- /dev/null +++ b/src/main/java/interface_adapter/login/LoginViewModel.java @@ -0,0 +1,15 @@ +package interface_adapter.login; + +import interface_adapter.ViewModel; + +/** + * The View Model for the Login View. + */ +public class LoginViewModel extends ViewModel { + + public LoginViewModel() { + super("log in"); + setState(new LoginState()); + } + +} diff --git a/src/main/java/interface_adapter/note/NoteController.java b/src/main/java/interface_adapter/note/NoteController.java deleted file mode 100644 index e3e5dfb32..000000000 --- a/src/main/java/interface_adapter/note/NoteController.java +++ /dev/null @@ -1,28 +0,0 @@ -package interface_adapter.note; - -import use_case.note.NoteInputBoundary; - -/** - * Controller for our Note related Use Cases. - */ -public class NoteController { - - private final NoteInputBoundary noteInteractor; - - public NoteController(NoteInputBoundary noteInteractor) { - this.noteInteractor = noteInteractor; - } - - /** - * Executes the Note related Use Cases. - * @param note the note to be recorded - */ - public void execute(String note) { - if (note != null) { - noteInteractor.executeSave(note); - } - else { - noteInteractor.executeRefresh(); - } - } -} diff --git a/src/main/java/interface_adapter/note/NotePresenter.java b/src/main/java/interface_adapter/note/NotePresenter.java deleted file mode 100644 index d4e416165..000000000 --- a/src/main/java/interface_adapter/note/NotePresenter.java +++ /dev/null @@ -1,38 +0,0 @@ -package interface_adapter.note; - -import use_case.note.NoteOutputBoundary; - -/** - * The presenter for our Note viewing and editing program. - */ -public class NotePresenter implements NoteOutputBoundary { - - private final NoteViewModel noteViewModel; - - public NotePresenter(NoteViewModel noteViewModel) { - this.noteViewModel = noteViewModel; - } - - /** - * Prepares the success view for the Note related Use Cases. - * - * @param note the output data - */ - @Override - public void prepareSuccessView(String note) { - noteViewModel.getState().setNote(note); - noteViewModel.getState().setError(null); - noteViewModel.firePropertyChanged(); - } - - /** - * Prepares the failure view for the Note related Use Cases. - * - * @param errorMessage the explanation of the failure - */ - @Override - public void prepareFailView(String errorMessage) { - noteViewModel.getState().setError(errorMessage); - noteViewModel.firePropertyChanged(); - } -} diff --git a/src/main/java/interface_adapter/note/NoteState.java b/src/main/java/interface_adapter/note/NoteState.java deleted file mode 100644 index c5b2234d6..000000000 --- a/src/main/java/interface_adapter/note/NoteState.java +++ /dev/null @@ -1,26 +0,0 @@ -package interface_adapter.note; - -/** - * The State for a note. - *

For this example, a note is simplay a string.

- */ -public class NoteState { - private String note = ""; - private String error; - - public String getNote() { - return note; - } - - public void setNote(String note) { - this.note = note; - } - - public void setError(String errorMessage) { - this.error = errorMessage; - } - - public String getError() { - return error; - } -} diff --git a/src/main/java/interface_adapter/note/NoteViewModel.java b/src/main/java/interface_adapter/note/NoteViewModel.java deleted file mode 100644 index 6e185d0fa..000000000 --- a/src/main/java/interface_adapter/note/NoteViewModel.java +++ /dev/null @@ -1,13 +0,0 @@ -package interface_adapter.note; - -import interface_adapter.ViewModel; - -/** - * The ViewModel for the NoteView. - */ -public class NoteViewModel extends ViewModel { - public NoteViewModel() { - super("note"); - setState(new NoteState()); - } -} diff --git a/src/main/java/interface_adapter/reviewlocation/ReviewLocationController.java b/src/main/java/interface_adapter/reviewlocation/ReviewLocationController.java new file mode 100644 index 000000000..9467f0ca5 --- /dev/null +++ b/src/main/java/interface_adapter/reviewlocation/ReviewLocationController.java @@ -0,0 +1,6 @@ +package interface_adapter.reviewlocation; + +public class ReviewLocationController { + + +} diff --git a/src/main/java/interface_adapter/reviewlocation/ReviewLocationPresenter.java b/src/main/java/interface_adapter/reviewlocation/ReviewLocationPresenter.java new file mode 100644 index 000000000..9f9f8673f --- /dev/null +++ b/src/main/java/interface_adapter/reviewlocation/ReviewLocationPresenter.java @@ -0,0 +1,4 @@ +package interface_adapter.reviewlocation; + +public class ReviewLocationPresenter { +} diff --git a/src/main/java/interface_adapter/reviewlocation/ReviewLocationState.java b/src/main/java/interface_adapter/reviewlocation/ReviewLocationState.java new file mode 100644 index 000000000..82b4be804 --- /dev/null +++ b/src/main/java/interface_adapter/reviewlocation/ReviewLocationState.java @@ -0,0 +1,4 @@ +package interface_adapter.reviewlocation; + +public class ReviewLocationState { +} diff --git a/src/main/java/interface_adapter/reviewlocation/ReviewLocationViewModel.java b/src/main/java/interface_adapter/reviewlocation/ReviewLocationViewModel.java new file mode 100644 index 000000000..ba8f46319 --- /dev/null +++ b/src/main/java/interface_adapter/reviewlocation/ReviewLocationViewModel.java @@ -0,0 +1,4 @@ +package interface_adapter.reviewlocation; + +public class ReviewLocationViewModel { +} diff --git a/src/main/java/interface_adapter/selectedlocation/SelectedLocationsController.java b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsController.java new file mode 100644 index 000000000..918ca9dc7 --- /dev/null +++ b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsController.java @@ -0,0 +1,32 @@ +package interface_adapter.selectedlocation; + + +import entity.Place; +import use_case.DataAccessException; +import use_case.selected_locations.SelectedLocationsInputBoundary; +import use_case.selected_locations.SelectedLocationsInputData; + +import java.util.List; + +public class SelectedLocationsController { + + private final SelectedLocationsInputBoundary selectedLocationsInteractor; + + public SelectedLocationsController(SelectedLocationsInputBoundary selectedLocationsInteractor) { + this.selectedLocationsInteractor = selectedLocationsInteractor; + } + + public void execute(List selectedLocations) throws DataAccessException { + final SelectedLocationsInputData selectedLocationsInputData = + new SelectedLocationsInputData(selectedLocations); + selectedLocationsInteractor.execute(selectedLocationsInputData); + } + + /** + * Switches to the location view. + */ + public void switchToLocationView() { + selectedLocationsInteractor.switchToLocationView(); + } + +} diff --git a/src/main/java/interface_adapter/selectedlocation/SelectedLocationsPresenter.java b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsPresenter.java new file mode 100644 index 000000000..f7f7cf8af --- /dev/null +++ b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsPresenter.java @@ -0,0 +1,43 @@ +package interface_adapter.selectedlocation; + +import interface_adapter.ViewManagerModel; +import interface_adapter.location.LocationViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsState; +import use_case.selected_locations.SelectedLocationsOutputBoundary; +import use_case.selected_locations.SelectedLocationsOutputData; + +public class SelectedLocationsPresenter implements SelectedLocationsOutputBoundary { + private final SelectedLocationsViewModel selectedLocationsViewModel; + private final ViewManagerModel viewManagerModel; + private final LocationViewModel locationViewModel; + + public SelectedLocationsPresenter(SelectedLocationsViewModel selectedLocationsViewModel, + ViewManagerModel viewManagerModel, + LocationViewModel locationViewModel) { + this.selectedLocationsViewModel = selectedLocationsViewModel; + this.viewManagerModel = viewManagerModel; + this.locationViewModel = locationViewModel; + } + + @Override + public void prepareSuccessView(SelectedLocationsOutputData outputData) { + final SelectedLocationsState selectedLocationsState = + selectedLocationsViewModel.getState(); + selectedLocationsState.setPlaceToCoordinates(outputData.getLocationCoordinatesMap()); + this.selectedLocationsViewModel.setState(selectedLocationsState); + this.selectedLocationsViewModel.firePropertyChanged(); + this.viewManagerModel.setState(selectedLocationsViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + + @Override + public void prepareFailView(String errorMessage) { + + } + + @Override + public void switchToLocationView() { + viewManagerModel.setState(locationViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/selectedlocation/SelectedLocationsState.java b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsState.java new file mode 100644 index 000000000..60a7b6a21 --- /dev/null +++ b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsState.java @@ -0,0 +1,42 @@ +package interface_adapter.selectedlocation; + +import entity.Place; + +import java.util.List; +import java.util.Map; + +/** + * The state representing selected location-related data, including a list of + * selected locations. + */ +public class SelectedLocationsState { + + private List selectedLocations; + private Map placeToCoordinates; + private String error; + + public List getSelectedLocations() { + return selectedLocations; + } + + public void setSelectedLocations(List selectedLocations) { + this.selectedLocations = selectedLocations; + } + + public void setPlaceToCoordinates(Map placeToCoordinates) { + this.placeToCoordinates = placeToCoordinates; + } + + public Map getPlaceToCoordinates() { + return placeToCoordinates; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + +} diff --git a/src/main/java/interface_adapter/selectedlocation/SelectedLocationsViewModel.java b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsViewModel.java new file mode 100644 index 000000000..6cd0c904e --- /dev/null +++ b/src/main/java/interface_adapter/selectedlocation/SelectedLocationsViewModel.java @@ -0,0 +1,17 @@ +package interface_adapter.selectedlocation; + +import interface_adapter.ViewModel; +import entity.Place; + +import java.util.List; + +/** + * The ViewModel for the SelectedLocationsView. + */ +public class SelectedLocationsViewModel extends ViewModel { + public SelectedLocationsViewModel() { + super("Selected Locations"); + setState(new SelectedLocationsState()); + } + +} diff --git a/src/main/java/interface_adapter/signup/SignupController.java b/src/main/java/interface_adapter/signup/SignupController.java new file mode 100644 index 000000000..d6ed9c4a8 --- /dev/null +++ b/src/main/java/interface_adapter/signup/SignupController.java @@ -0,0 +1,34 @@ +package interface_adapter.signup; + +import use_case.signup.SignupInputBoundary; +import use_case.signup.SignupInputData; + +/** + * Controller for the Signup use case. + */ +public class SignupController { + private final SignupInputBoundary signupInteractor; + + public SignupController(SignupInputBoundary signupInteractor) { + this.signupInteractor = signupInteractor; + } + + /** + * Executes the Signup use case with the provided information. + * + * @param username the username + * @param password the password + * @param repeatPassword the repeated password + */ + public void execute(String username, String password, String repeatPassword) { + final SignupInputData inputData = new SignupInputData(username, password, repeatPassword); + signupInteractor.execute(inputData); + } + + /** + * Switches to the login view. + */ + public void switchToLoginView() { + signupInteractor.switchToLoginView(); + } +} diff --git a/src/main/java/interface_adapter/signup/SignupPresenter.java b/src/main/java/interface_adapter/signup/SignupPresenter.java new file mode 100644 index 000000000..d8216ec02 --- /dev/null +++ b/src/main/java/interface_adapter/signup/SignupPresenter.java @@ -0,0 +1,50 @@ +package interface_adapter.signup; + +import interface_adapter.ViewManagerModel; +import interface_adapter.login.LoginState; +import interface_adapter.login.LoginViewModel; +import use_case.signup.SignupOutputBoundary; +import use_case.signup.SignupOutputData; + +/** + * Presenter for the signup use case. + */ +public class SignupPresenter implements SignupOutputBoundary { + + private final SignupViewModel signupViewModel; + private final LoginViewModel loginViewModel; + private final ViewManagerModel viewManagerModel; + + public SignupPresenter(ViewManagerModel viewManagerModel, + SignupViewModel signupViewModel, + LoginViewModel loginViewModel) { + this.viewManagerModel = viewManagerModel; + this.signupViewModel = signupViewModel; + this.loginViewModel = loginViewModel; + } + + @Override + public void prepareSuccessView(SignupOutputData response) { + // On success, switch to the login view. + final LoginState loginState = loginViewModel.getState(); + loginState.setUsername(response.getUsername()); + this.loginViewModel.setState(loginState); + loginViewModel.firePropertyChanged(); + + viewManagerModel.setState(loginViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } + + @Override + public void prepareFailView(String error) { + final SignupState signupState = signupViewModel.getState(); + signupState.setUsernameError(error); + signupViewModel.firePropertyChanged(); + } + + @Override + public void switchToLoginView() { + viewManagerModel.setState(loginViewModel.getViewName()); + viewManagerModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/signup/SignupState.java b/src/main/java/interface_adapter/signup/SignupState.java new file mode 100644 index 000000000..ead93994c --- /dev/null +++ b/src/main/java/interface_adapter/signup/SignupState.java @@ -0,0 +1,67 @@ +package interface_adapter.signup; + +public class SignupState { + private String username = ""; + private String usernameError; + private String password = ""; + private String passwordError; + private String repeatPassword = ""; + private String repeatPasswordError; + + public String getUsername() { + return username; + } + + public String getUsernameError() { + return usernameError; + } + + public String getPassword() { + return password; + } + + public String getPasswordError() { + return passwordError; + } + + public String getRepeatPassword() { + return repeatPassword; + } + + public String getRepeatPasswordError() { + return repeatPasswordError; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setUsernameError(String usernameError) { + this.usernameError = usernameError; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setPasswordError(String passwordError) { + this.passwordError = passwordError; + } + + public void setRepeatPassword(String repeatPassword) { + this.repeatPassword = repeatPassword; + } + + public void setRepeatPasswordError(String repeatPasswordError) { + this.repeatPasswordError = repeatPasswordError; + } + + @Override + public String toString() { + return "SignupState{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + ", repeatPassword='" + repeatPassword + '\'' + + '}'; + } +} diff --git a/src/main/java/interface_adapter/signup/SignupViewModel.java b/src/main/java/interface_adapter/signup/SignupViewModel.java new file mode 100644 index 000000000..df3903f7e --- /dev/null +++ b/src/main/java/interface_adapter/signup/SignupViewModel.java @@ -0,0 +1,68 @@ +package interface_adapter.signup; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import interface_adapter.ViewModel; + + +/** + * ViewModel for the signup process, holding state for the signup view. + */ +public class SignupViewModel extends ViewModel { +// private String username; +// private String errorMessage; +// private final PropertyChangeSupport support; + + public SignupViewModel() { + super("sign up"); + setState(new SignupState()); + } +// +// public String getUsername() { +// return username; +// } +// +// /** +// * Sets the username and notifies listeners of the change. +// * +// * @param username the username +// */ +// public void setUsername(String username) { +// final String oldUsername = this.username; +// this.username = username; +// support.firePropertyChange("username", oldUsername, username); +// } +// +// /** +// * Sets the error message and notifies listeners of the change. +// * +// * @param errorMessage the error message +// */ +// public void setErrorMessage(String errorMessage) { +// final String oldErrorMessage = this.errorMessage; +// this.errorMessage = errorMessage; +// support.firePropertyChange("errorMessage", oldErrorMessage, errorMessage); +// } +// +// /** +// * Adds a property change listener. +// * +// * @param listener the listener +// */ +// public void addPropertyChangeListener(PropertyChangeListener listener) { +// support.addPropertyChangeListener(listener); +// } +// +// /** +// * Removes a property change listener. +// * +// * @param listener the listener +// */ +// public void removePropertyChangeListener(PropertyChangeListener listener) { +// support.removePropertyChangeListener(listener); +// } +// +// public SignupState getState() { +// return +// } +} diff --git a/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsController.java b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsController.java new file mode 100644 index 000000000..4e601078b --- /dev/null +++ b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsController.java @@ -0,0 +1,33 @@ +package interface_adapter.suggestlocation; + +import java.util.List; +import java.util.Map; + +import entity.Place; +import use_case.DataAccessException; +import use_case.suggested_locations.SuggestedLocationsInputBoundary; +import use_case.suggested_locations.SuggestedLocationsInputData; + +/** + * The SuggestedLocationsController class handles user input related to suggested locations. + */ +public class SuggestedLocationsController { + + private final SuggestedLocationsInputBoundary suggestedLocationsInteractor; + + public SuggestedLocationsController(SuggestedLocationsInputBoundary suggestedLocationsInteractor) { + this.suggestedLocationsInteractor = suggestedLocationsInteractor; + } + + /** + * Executes the suggested locations' operation. + * @param calendarItems the calendarItems produced. + * @throws DataAccessException if data cannot be accessed. + */ + public void execute(Map calendarItems) throws DataAccessException { + final SuggestedLocationsInputData suggestedLocationInputData = new SuggestedLocationsInputData( + calendarItems); + + suggestedLocationsInteractor.execute(suggestedLocationInputData); + } +} diff --git a/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsPresenter.java b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsPresenter.java new file mode 100644 index 000000000..61df96697 --- /dev/null +++ b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsPresenter.java @@ -0,0 +1,65 @@ +package interface_adapter.suggestlocation; + +import interface_adapter.ViewManagerModel; +import interface_adapter.selectedlocation.SelectedLocationsViewModel; +import interface_adapter.add_to_calendar.AddToCalendarState; +import interface_adapter.add_to_calendar.AddToCalendarViewModel; +import use_case.locations.LocationsOutputData; +import use_case.suggested_locations.SuggestedLocationsOutputBoundary; +import use_case.suggested_locations.SuggestedLocationsOutputData; +import interface_adapter.selectedlocation.SelectedLocationsState; + +/** + * The presenter for the suggested locations use case. + */ +public class SuggestedLocationsPresenter implements SuggestedLocationsOutputBoundary { + + private final ViewManagerModel viewManagerModel; + private final SuggestedLocationsViewModel suggestedLocationsViewModel; + private final SelectedLocationsViewModel selectedLocationViewModel; + private final AddToCalendarViewModel addToCalendarViewModel; + + public SuggestedLocationsPresenter(ViewManagerModel viewManagerModel, + SuggestedLocationsViewModel suggestedLocationsViewModel, + AddToCalendarViewModel addToCalendarViewModel, + SelectedLocationsViewModel selectedLocationViewModel) { + this.viewManagerModel = viewManagerModel; + this.suggestedLocationsViewModel = suggestedLocationsViewModel; + this.addToCalendarViewModel = addToCalendarViewModel; + this.selectedLocationViewModel = selectedLocationViewModel; + } + + @Override + public void prepareSuccessView(SuggestedLocationsOutputData response) { + // On success, switch to the Calendar view. + + final AddToCalendarState calendarState = addToCalendarViewModel.getState(); +// calendarState.setCalendarItems(response.getCalendarItems()); + this.addToCalendarViewModel.setState(calendarState); + this.addToCalendarViewModel.firePropertyChanged(); + + this.viewManagerModel.setState(addToCalendarViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + +// @Override + public void placeholderForSeansCode(SuggestedLocationsOutputData response) { + // On success, switch to the selected locations view. + + final SelectedLocationsState selectedLocationState = + selectedLocationViewModel.getState(); + selectedLocationState.setSelectedLocations(response.getSelectedLocations()); + this.selectedLocationViewModel.setState(selectedLocationState); + this.selectedLocationViewModel.firePropertyChanged(); + + this.viewManagerModel.setState(selectedLocationViewModel.getViewName()); + this.viewManagerModel.firePropertyChanged(); + } + + @Override + public void prepareFailView(String error) { + final SuggestedLocationsState suggestedLocationState = suggestedLocationsViewModel.getState(); + suggestedLocationState.setError(error); + suggestedLocationsViewModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsState.java b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsState.java new file mode 100644 index 000000000..fa2a3e461 --- /dev/null +++ b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsState.java @@ -0,0 +1,41 @@ +package interface_adapter.suggestlocation; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import entity.Place; + +/** + * The state representing suggested location-related data, including a list of suggested locations. + */ +public class SuggestedLocationsState { + private List suggestedLocations = new ArrayList<>(); + private Map calendarItems = new HashMap<>(); + private String error; + + public List getSuggestedLocations() { + return suggestedLocations; + } + + public void setSuggestedLocations(List suggestedLocations) { + this.suggestedLocations = suggestedLocations; + } + + public Map getCalendarItems() { + return calendarItems; + } + + public void setCalendarItems(Map calendarItems) { + this.calendarItems = calendarItems; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsViewModel.java b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsViewModel.java new file mode 100644 index 000000000..a17bb17fd --- /dev/null +++ b/src/main/java/interface_adapter/suggestlocation/SuggestedLocationsViewModel.java @@ -0,0 +1,13 @@ +package interface_adapter.suggestlocation; + +import interface_adapter.ViewModel; + +/** + * The ViewModel for the SuggestedLocationsView. + */ +public class SuggestedLocationsViewModel extends ViewModel { + public SuggestedLocationsViewModel() { + super("Suggested Locations"); + setState(new SuggestedLocationsState()); + } +} diff --git a/src/main/java/interface_adapter/user_profile/UserProfileController.java b/src/main/java/interface_adapter/user_profile/UserProfileController.java new file mode 100644 index 000000000..736d02b61 --- /dev/null +++ b/src/main/java/interface_adapter/user_profile/UserProfileController.java @@ -0,0 +1,36 @@ +package interface_adapter.user_profile; + +import java.util.List; +import java.util.Map; + +import entity.Place; +import use_case.user_profile.UserProfileInputBoundary; + +/** + * Controller for the User Profile use case. + */ +public class UserProfileController { + private final UserProfileInputBoundary userProfileInteractor; + + public UserProfileController(UserProfileInputBoundary userProfileInteractor) { + this.userProfileInteractor = userProfileInteractor; + } + + /** + * Saves the places for the given username. + * + * @param username the username + * @param places the places to be saved, represented as a map with the place name as the key + * and a list of place objects as the value + */ + public void savePlaces(String username, Map> places) { + userProfileInteractor.savePlaces(username, places); + } + + /** + * Logs out the user. + */ + public void logOut() { + userProfileInteractor.logOut(); + } +} diff --git a/src/main/java/interface_adapter/user_profile/UserProfilePresenter.java b/src/main/java/interface_adapter/user_profile/UserProfilePresenter.java new file mode 100644 index 000000000..e73b819bf --- /dev/null +++ b/src/main/java/interface_adapter/user_profile/UserProfilePresenter.java @@ -0,0 +1,29 @@ +package interface_adapter.user_profile; + +import interface_adapter.ViewManagerModel; +import use_case.user_profile.UserProfileOutputBoundary; + +/** + * Presenter for user profile creation. + */ +public class UserProfilePresenter implements UserProfileOutputBoundary { + private final ViewManagerModel viewManagerModel; + private final UserProfileViewModel userViewModel; + + public UserProfilePresenter(ViewManagerModel viewManagerModel, UserProfileViewModel userViewModel) { + this.viewManagerModel = viewManagerModel; + this.userViewModel = userViewModel; + } + + @Override + public void prepareSuccessView(String message) { + //teddy u can implement this for urs + } + + @Override + public void prepareFailView(String errorMessage) { + final UserProfileState userProfileState = userViewModel.getState(); + userProfileState.setError(errorMessage); + userViewModel.firePropertyChanged(); + } +} diff --git a/src/main/java/interface_adapter/user_profile/UserProfileState.java b/src/main/java/interface_adapter/user_profile/UserProfileState.java new file mode 100644 index 000000000..1424d3682 --- /dev/null +++ b/src/main/java/interface_adapter/user_profile/UserProfileState.java @@ -0,0 +1,49 @@ +package interface_adapter.user_profile; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import entity.Place; + +/** + * Represents the state of a user. + */ +public class UserProfileState { + private String username = ""; + private Map> savedPlaces = new HashMap<>(); + private String error; + + public UserProfileState(UserProfileState copy) { + username = copy.username; + savedPlaces = copy.savedPlaces; + } + + public UserProfileState() { + + } + + public String getName() { + return username; + } + + public Map> getSavedPlaces() { + return savedPlaces; + } + + public void setSavedPlaces(Map> savedPlaces) { + this.savedPlaces = savedPlaces; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/src/main/java/interface_adapter/user_profile/UserProfileViewModel.java b/src/main/java/interface_adapter/user_profile/UserProfileViewModel.java new file mode 100644 index 000000000..d050dea1a --- /dev/null +++ b/src/main/java/interface_adapter/user_profile/UserProfileViewModel.java @@ -0,0 +1,36 @@ +package interface_adapter.user_profile; + +import interface_adapter.ViewModel; + +/** + * View model for the user profile. + */ +public class UserProfileViewModel extends ViewModel { + + public UserProfileViewModel() { + super("User Profile"); + setState(new UserProfileState()); + } +// private String successMessage; +// private String errorMessage; +// +// public UserProfileViewModel() { +// super("User Profile"); +// } +// +// public String getSuccessMessage() { +// return successMessage; +// } +// +// public void setSuccessMessage(String successMessage) { +// this.successMessage = successMessage; +// } +// +// public String getErrorMessage() { +// return errorMessage; +// } +// +// public void setErrorMessage(String errorMessage) { +// this.errorMessage = errorMessage; +// } +} diff --git a/src/main/java/use_case/note/DataAccessException.java b/src/main/java/use_case/DataAccessException.java similarity index 89% rename from src/main/java/use_case/note/DataAccessException.java rename to src/main/java/use_case/DataAccessException.java index b8c17920d..59cf779b3 100644 --- a/src/main/java/use_case/note/DataAccessException.java +++ b/src/main/java/use_case/DataAccessException.java @@ -1,4 +1,4 @@ -package use_case.note; +package use_case; /** * Exception thrown when there is an error with accessing data. diff --git a/src/main/java/use_case/add_to_calendar/AddToCalendarDataAccessInterface.java b/src/main/java/use_case/add_to_calendar/AddToCalendarDataAccessInterface.java new file mode 100644 index 000000000..13f07fc05 --- /dev/null +++ b/src/main/java/use_case/add_to_calendar/AddToCalendarDataAccessInterface.java @@ -0,0 +1,32 @@ +package use_case.add_to_calendar; + +import entity.Place; + +/** + * Interface for the AddToCalendar DAO. It consists of methods for adding locations to the calendar. + */ +public interface AddToCalendarDataAccessInterface { + + /** + * Checks if the given Place exists. + * @param location the name of the location to look for. + * @return true if a Place with the given name exists; false otherwise + */ + boolean hasLocation(Place location); + + /** + * Checks if the given time is available. + * @param time the time of the visit to look for + * @return true if the timeslot is already full; false otherwise + */ + boolean hasTime(String time); + + /** + * Saves the location and time to calendar. + * @param location the location to save + * @param time the time to save + */ + void save(Place location, String time); + + +} diff --git a/src/main/java/use_case/add_to_calendar/AddToCalendarInputBoundary.java b/src/main/java/use_case/add_to_calendar/AddToCalendarInputBoundary.java new file mode 100644 index 000000000..aec1930cf --- /dev/null +++ b/src/main/java/use_case/add_to_calendar/AddToCalendarInputBoundary.java @@ -0,0 +1,23 @@ +package use_case.add_to_calendar; + +import use_case.DataAccessException; + +/** +* The Add to Calendar Use Case. +*/ +public interface AddToCalendarInputBoundary { + + /** + * Execute the Add To Calendar Use Case. + * + * @param addToCalendarInputData the input data for this use case + * @throws DataAccessException if data cannot be accessed at any time + */ + void execute(AddToCalendarInputData addToCalendarInputData) throws DataAccessException; + + /** + * Execute the switch to location view. + */ + void switchToLocationView(); + +} diff --git a/src/main/java/use_case/add_to_calendar/AddToCalendarInputData.java b/src/main/java/use_case/add_to_calendar/AddToCalendarInputData.java new file mode 100644 index 000000000..a81445c4e --- /dev/null +++ b/src/main/java/use_case/add_to_calendar/AddToCalendarInputData.java @@ -0,0 +1,21 @@ +package use_case.add_to_calendar; + +import java.util.Map; + +import entity.Place; + +/** + * The input data for the Add to Calendar Use Case. + */ +public class AddToCalendarInputData { + + private final Map addToCalendarPlace; + + public AddToCalendarInputData(Map addToCalendarPlace) { + this.addToCalendarPlace = addToCalendarPlace; + } + + public Map getAddToCalendarPlace() { + return addToCalendarPlace; + } +} diff --git a/src/main/java/use_case/add_to_calendar/AddToCalendarInteractor.java b/src/main/java/use_case/add_to_calendar/AddToCalendarInteractor.java new file mode 100644 index 000000000..a3a94f763 --- /dev/null +++ b/src/main/java/use_case/add_to_calendar/AddToCalendarInteractor.java @@ -0,0 +1,42 @@ +package use_case.add_to_calendar; + +import java.util.Map; + +import entity.Place; +import use_case.DataAccessException; + +/** + * The Add to Calendar Interactor. + */ +public class AddToCalendarInteractor implements AddToCalendarInputBoundary { + private final AddToCalendarDataAccessInterface calendarDataAccessObject; + private final AddToCalendarOutputBoundary calendarPresenter; + + public AddToCalendarInteractor(AddToCalendarDataAccessInterface calendarDataAccessObject, + AddToCalendarOutputBoundary calendarPresenter) { + this.calendarDataAccessObject = calendarDataAccessObject; + this.calendarPresenter = calendarPresenter; + } + + @Override + public void execute(AddToCalendarInputData addToCalendarInputData) throws DataAccessException { + final Map addToCalendarPlace = addToCalendarInputData.getAddToCalendarPlace(); + for (Map.Entry entry : addToCalendarPlace.entrySet()) { + final Place location = entry.getKey(); + final String time = entry.getValue(); + if (calendarDataAccessObject.hasTime(time)) { + calendarPresenter.prepareFailView(time + " is already full."); + } + else { + calendarDataAccessObject.save(location, time); + } + } + final AddToCalendarOutputData addToCalendarOutputData = new AddToCalendarOutputData(addToCalendarPlace); + calendarPresenter.prepareSuccessView(addToCalendarOutputData); + } + + @Override + public void switchToLocationView() { + calendarPresenter.switchToLocationView(); + } +} diff --git a/src/main/java/use_case/add_to_calendar/AddToCalendarOutputBoundary.java b/src/main/java/use_case/add_to_calendar/AddToCalendarOutputBoundary.java new file mode 100644 index 000000000..7abf6266e --- /dev/null +++ b/src/main/java/use_case/add_to_calendar/AddToCalendarOutputBoundary.java @@ -0,0 +1,24 @@ +package use_case.add_to_calendar; + +/** + * The output boundary for the Add to Calendar Use Case. + */ +public interface AddToCalendarOutputBoundary { + + /** + * Prepares the success view for the AddToCalendar Use Case. + * @param outputData the output data + */ + void prepareSuccessView(AddToCalendarOutputData outputData); + + /** + * Prepares the failure view for the AddToCalendar Use Case. + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); + + /** + * Execute the switch to location view. + */ + void switchToLocationView(); +} diff --git a/src/main/java/use_case/add_to_calendar/AddToCalendarOutputData.java b/src/main/java/use_case/add_to_calendar/AddToCalendarOutputData.java new file mode 100644 index 000000000..153eb5aa5 --- /dev/null +++ b/src/main/java/use_case/add_to_calendar/AddToCalendarOutputData.java @@ -0,0 +1,21 @@ +package use_case.add_to_calendar; + +import java.util.Map; + +import entity.Place; + +/** + * The output data for the Add to Calendar Use Case. + */ +public class AddToCalendarOutputData { + + private final Map calendarItems; + + public AddToCalendarOutputData(Map addToCalendarPlace) { + this.calendarItems = addToCalendarPlace; + } + + public Map getcalendarItems() { + return calendarItems; + } +} diff --git a/src/main/java/use_case/locations/LocationDataAccessInterface.java b/src/main/java/use_case/locations/LocationDataAccessInterface.java new file mode 100644 index 000000000..b6a40331c --- /dev/null +++ b/src/main/java/use_case/locations/LocationDataAccessInterface.java @@ -0,0 +1,23 @@ +package use_case.locations; + +import java.util.List; + +import entity.Place; +import use_case.DataAccessException; + +/** + * Interface for the LocationDAO. It consists of methods for + * searching locations + */ +public interface LocationDataAccessInterface { + + /** + * Gets a list of locations based on address given. + * @param address address the user gives + * @param locationType the type of location + * @return a list of locations + * @throws DataAccessException if the location can not be loaded for any reason + */ + List searchLocation(String address, String locationType) throws DataAccessException; + +} diff --git a/src/main/java/use_case/locations/LocationsInputBoundary.java b/src/main/java/use_case/locations/LocationsInputBoundary.java new file mode 100644 index 000000000..838406368 --- /dev/null +++ b/src/main/java/use_case/locations/LocationsInputBoundary.java @@ -0,0 +1,17 @@ +package use_case.locations; + +import use_case.DataAccessException; + +/** + * The Suggest Locations Use Case. + */ +public interface LocationsInputBoundary { + /** + * Execute the Suggest Locations Use Case. + * + * @param locationsInputData the input data for this use case + * @param currentFilter + * @throws DataAccessException if data cannot be accessed at any time + */ + void execute(LocationsInputData locationsInputData, String currentFilter) throws DataAccessException; +} diff --git a/src/main/java/use_case/locations/LocationsInputData.java b/src/main/java/use_case/locations/LocationsInputData.java new file mode 100644 index 000000000..86b44a84f --- /dev/null +++ b/src/main/java/use_case/locations/LocationsInputData.java @@ -0,0 +1,30 @@ +package use_case.locations; + +/** + * The input data for the Suggest Locations Use Case. + */ +public class LocationsInputData { + + private final String address; + private final String locationType; + private final String username; + + public LocationsInputData(String address, String locationType, String username) { + this.address = address; + this.locationType = locationType; + this.username = username; + } + + public String getAddress() { + return address; + } + + public String getLocationType() { + return locationType; + } + + public String getUsername() { + return username; + } + +} diff --git a/src/main/java/use_case/locations/LocationsInteractor.java b/src/main/java/use_case/locations/LocationsInteractor.java new file mode 100644 index 000000000..d8a805436 --- /dev/null +++ b/src/main/java/use_case/locations/LocationsInteractor.java @@ -0,0 +1,52 @@ +package use_case.locations; + +import data_access.InMemoryUserDataAccessObject; +import entity.Place; +import interface_adapter.user_profile.UserProfileState; +import use_case.DataAccessException; +import entity.User; +import use_case.user_profile.UserProfileDataAccessInterface; + +import java.util.List; +import java.util.Map; + +/** + * The Suggest Locations Interactor. + */ +public class LocationsInteractor implements LocationsInputBoundary { + private final LocationDataAccessInterface placeDataAccessObject; + private final LocationsOutputBoundary placePresenter; + private final UserProfileDataAccessInterface userDataAccessObject; + + public LocationsInteractor(LocationDataAccessInterface suggestLocationsPlaceDataAccessInterface, + LocationsOutputBoundary locationsOutputBoundary, + UserProfileDataAccessInterface userDataAccessObject) { + this.placeDataAccessObject = suggestLocationsPlaceDataAccessInterface; + this.placePresenter = locationsOutputBoundary; + this.userDataAccessObject = userDataAccessObject; + } + + @Override + public void execute(LocationsInputData locationsInputData, String currentFilter) throws DataAccessException { + final List suggestedPlaces = placeDataAccessObject + .searchLocation(locationsInputData.getAddress(), locationsInputData.getLocationType()); + + if ("None".equals(currentFilter)) { + final LocationsOutputData locationsOutputData = new LocationsOutputData(placeDataAccessObject + .searchLocation(locationsInputData.getAddress(), locationsInputData.getLocationType()), + false); + placePresenter.prepareSuccessView(locationsOutputData); + } else if ("Remove Saved Locations".equals(currentFilter)) { + for (Place place : suggestedPlaces) { + final User user = userDataAccessObject.getUser(locationsInputData.getUsername()); + for (Map.Entry> entry : user.getSavedPlaces().entrySet()) { + if (entry.getValue().contains(place)) { + suggestedPlaces.remove(place); + } + } + } + final LocationsOutputData locationsOutputData = new LocationsOutputData(suggestedPlaces, false); + placePresenter.prepareSuccessView(locationsOutputData); + } + } +} diff --git a/src/main/java/use_case/locations/LocationsOutputBoundary.java b/src/main/java/use_case/locations/LocationsOutputBoundary.java new file mode 100644 index 000000000..904014a0b --- /dev/null +++ b/src/main/java/use_case/locations/LocationsOutputBoundary.java @@ -0,0 +1,18 @@ +package use_case.locations; + +/** + * The output boundary for the Suggest Locations Use Case. + */ +public interface LocationsOutputBoundary { + /** + * Prepares the success view for the Suggest Locations Use Case. + * @param outputData the output data + */ + void prepareSuccessView(LocationsOutputData outputData); + + /** + * Prepares the failure view for the SuggestLocations Use Case. + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); +} diff --git a/src/main/java/use_case/locations/LocationsOutputData.java b/src/main/java/use_case/locations/LocationsOutputData.java new file mode 100644 index 000000000..01d04795f --- /dev/null +++ b/src/main/java/use_case/locations/LocationsOutputData.java @@ -0,0 +1,23 @@ +package use_case.locations; + +import java.util.List; + +import entity.Place; + +/** + * Output Data for the Suggest Locations Use Case. + */ +public class LocationsOutputData { + + private final List locations; + private final boolean useCaseFailed; + + public LocationsOutputData(List locations, boolean useCaseFailed) { + this.locations = locations; + this.useCaseFailed = useCaseFailed; + } + + public List getLocations() { + return locations; + } +} diff --git a/src/main/java/use_case/login/LoginInputBoundary.java b/src/main/java/use_case/login/LoginInputBoundary.java new file mode 100644 index 000000000..cb696b1e8 --- /dev/null +++ b/src/main/java/use_case/login/LoginInputBoundary.java @@ -0,0 +1,18 @@ +package use_case.login; + +/** + * Input Boundary for actions which are related to logging in. + */ +public interface LoginInputBoundary { + + /** + * Executes the login use case. + * @param loginInputData the input data + */ + void execute(LoginInputData loginInputData); + + /** + * Executes the switch back to sign up view + */ + void switchToSignUpView(); +} diff --git a/src/main/java/use_case/login/LoginInputData.java b/src/main/java/use_case/login/LoginInputData.java new file mode 100644 index 000000000..363316832 --- /dev/null +++ b/src/main/java/use_case/login/LoginInputData.java @@ -0,0 +1,24 @@ +package use_case.login; + +/** + * The Input Data for the Login Use Case. + */ +public class LoginInputData { + + private final String username; + private final String password; + + public LoginInputData(String username, String password) { + this.username = username; + this.password = password; + } + + String getUsername() { + return username; + } + + String getPassword() { + return password; + } + +} diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java new file mode 100644 index 000000000..a856b9f56 --- /dev/null +++ b/src/main/java/use_case/login/LoginInteractor.java @@ -0,0 +1,44 @@ +package use_case.login; + +import entity.User; +import use_case.user_profile.UserProfileDataAccessInterface; + +/** + * The Login Interactor. + */ +public class LoginInteractor implements LoginInputBoundary { + private final LoginUserDataAccessInterface userDataAccessObject; + private final LoginOutputBoundary loginPresenter; + + public LoginInteractor(LoginUserDataAccessInterface userDataAccessInterface, + LoginOutputBoundary loginOutputBoundary) { + this.userDataAccessObject = userDataAccessInterface; + this.loginPresenter = loginOutputBoundary; + } + + @Override + public void execute(LoginInputData loginInputData) { + final String username = loginInputData.getUsername(); + final String password = loginInputData.getPassword(); + if (!userDataAccessObject.existsByName(username)) { + loginPresenter.prepareFailView(username + ": Account does not exist."); + } + else { + final String pwd = userDataAccessObject.get(username).getPassword(); + if (!password.equals(pwd)) { + loginPresenter.prepareFailView("Incorrect password for \"" + username + "\"."); + } + else { + + final User user = userDataAccessObject.get(loginInputData.getUsername()); + final LoginOutputData loginOutputData = new LoginOutputData(user.getName(), false); + loginPresenter.prepareSuccessView(loginOutputData); + } + } + } + + @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 new file mode 100644 index 000000000..81c6934f3 --- /dev/null +++ b/src/main/java/use_case/login/LoginOutputBoundary.java @@ -0,0 +1,23 @@ +package use_case.login; + +/** + * The output boundary for the Login Use Case. + */ +public interface LoginOutputBoundary { + /** + * Prepares the success view for the Login Use Case. + * @param outputData the output data + */ + void prepareSuccessView(LoginOutputData outputData); + + /** + * Prepares the failure view for the Login Use Case. + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); + + /** + * Switches to the SignUp View. + */ + void switchToSignUpView(); +} diff --git a/src/main/java/use_case/login/LoginOutputData.java b/src/main/java/use_case/login/LoginOutputData.java new file mode 100644 index 000000000..3ea119a8f --- /dev/null +++ b/src/main/java/use_case/login/LoginOutputData.java @@ -0,0 +1,20 @@ +package use_case.login; + +/** + * Output Data for the Login Use Case. + */ +public class LoginOutputData { + + private final String username; + private final boolean useCaseFailed; + + public LoginOutputData(String username, boolean useCaseFailed) { + this.username = username; + this.useCaseFailed = useCaseFailed; + } + + public String getUsername() { + return username; + } + +} diff --git a/src/main/java/use_case/login/LoginUserDataAccessInterface.java b/src/main/java/use_case/login/LoginUserDataAccessInterface.java new file mode 100644 index 000000000..60e744d78 --- /dev/null +++ b/src/main/java/use_case/login/LoginUserDataAccessInterface.java @@ -0,0 +1,29 @@ +package use_case.login; + +import entity.User; + +/** + * DAO for the Login Use Case. + */ +public interface LoginUserDataAccessInterface { + + /** + * Checks if the given username exists. + * @param username the username to look for + * @return true if a user with the given username exists; false otherwise + */ + boolean existsByName(String username); + + /** + * Saves the user. + * @param user the user to save + */ + void save(User user); + + /** + * Returns the user with the given username. + * @param username the username to look up + * @return the user with the given username + */ + User get(String username); +} diff --git a/src/main/java/use_case/note/NoteDataAccessInterface.java b/src/main/java/use_case/note/NoteDataAccessInterface.java deleted file mode 100644 index b71597828..000000000 --- a/src/main/java/use_case/note/NoteDataAccessInterface.java +++ /dev/null @@ -1,30 +0,0 @@ -package use_case.note; - -import entity.User; - -/** - * Interface for the NoteDAO. It consists of methods for - * both loading and saving a note. - */ -public interface NoteDataAccessInterface { - - /** - * Saves a note for a given user. This will replace any existing note. - *

The password of the user must match that of the user saved in the system.

- * @param user the user information associated with the note - * @param note the note to be saved - * @return the contents of the note - * @throws DataAccessException if the user's note can not be saved for any reason - */ - String saveNote(User user, String note) throws DataAccessException; - - /** - * Returns the note associated with the user. The password - * is not checked, so anyone can read the information. - * @param user the user information associated with the note - * @return the contents of the note - * @throws DataAccessException if the user's note can not be loaded for any reason - */ - String loadNote(User user) throws DataAccessException; - -} diff --git a/src/main/java/use_case/note/NoteInputBoundary.java b/src/main/java/use_case/note/NoteInputBoundary.java deleted file mode 100644 index b41da9bf5..000000000 --- a/src/main/java/use_case/note/NoteInputBoundary.java +++ /dev/null @@ -1,19 +0,0 @@ -package use_case.note; - -/** - * The Input Boundary for our note-related use cases. Since they are closely related, - * we have included them both in the same interface for simplicity. - */ -public interface NoteInputBoundary { - - /** - * Executes the refresh note use case. - */ - void executeRefresh(); - - /** - * Executes the save note use case. - * @param message the input data - */ - void executeSave(String message); -} diff --git a/src/main/java/use_case/note/NoteInteractor.java b/src/main/java/use_case/note/NoteInteractor.java deleted file mode 100644 index 369e9309a..000000000 --- a/src/main/java/use_case/note/NoteInteractor.java +++ /dev/null @@ -1,59 +0,0 @@ -package use_case.note; - -import entity.User; - -/** - * The "Use Case Interactor" for our two note-related use cases of refreshing - * the contents of the note and saving the contents of the note. Since they - * are closely related, we have combined them here for simplicity. - */ -public class NoteInteractor implements NoteInputBoundary { - - private final NoteDataAccessInterface noteDataAccessInterface; - private final NoteOutputBoundary noteOutputBoundary; - // Note: this program has it hardcoded which user object it is getting data for; - // you could change this if you wanted to generalize the code. For example, - // you might allow a user of the program to create a new note, which you - // could store as a "user" through the API OR you might maintain all notes - // in a JSON object stored in one common "user" stored through the API. - private final User user = new User("jonathan_calver2", "abc123"); - - public NoteInteractor(NoteDataAccessInterface noteDataAccessInterface, - NoteOutputBoundary noteOutputBoundary) { - this.noteDataAccessInterface = noteDataAccessInterface; - this.noteOutputBoundary = noteOutputBoundary; - } - - /** - * Executes the refresh note use case. - * - */ - @Override - public void executeRefresh() { - try { - - final String note = noteDataAccessInterface.loadNote(user); - noteOutputBoundary.prepareSuccessView(note); - } - catch (DataAccessException ex) { - noteOutputBoundary.prepareFailView(ex.getMessage()); - } - } - - /** - * Executes the save note use case. - * - * @param note the input data - */ - @Override - public void executeSave(String note) { - try { - - final String updatedNote = noteDataAccessInterface.saveNote(user, note); - noteOutputBoundary.prepareSuccessView(updatedNote); - } - catch (DataAccessException ex) { - noteOutputBoundary.prepareFailView(ex.getMessage()); - } - } -} diff --git a/src/main/java/use_case/note/NoteOutputBoundary.java b/src/main/java/use_case/note/NoteOutputBoundary.java deleted file mode 100644 index c0c2bb1d0..000000000 --- a/src/main/java/use_case/note/NoteOutputBoundary.java +++ /dev/null @@ -1,18 +0,0 @@ -package use_case.note; - -/** - * The output boundary for the Login Use Case. - */ -public interface NoteOutputBoundary { - /** - * Prepares the success view for the Note related Use Cases. - * @param message the output data - */ - void prepareSuccessView(String message); - - /** - * Prepares the failure view for the Note related Use Cases. - * @param errorMessage the explanation of the failure - */ - void prepareFailView(String errorMessage); -} diff --git a/src/main/java/use_case/review_locations/ReviewLocationsInputBoundary.java b/src/main/java/use_case/review_locations/ReviewLocationsInputBoundary.java new file mode 100644 index 000000000..bbe04c4a6 --- /dev/null +++ b/src/main/java/use_case/review_locations/ReviewLocationsInputBoundary.java @@ -0,0 +1,4 @@ +package use_case.review_locations; + +public class ReviewLocationsInputBoundary { +} diff --git a/src/main/java/use_case/review_locations/ReviewLocationsInputData.java b/src/main/java/use_case/review_locations/ReviewLocationsInputData.java new file mode 100644 index 000000000..c487e8120 --- /dev/null +++ b/src/main/java/use_case/review_locations/ReviewLocationsInputData.java @@ -0,0 +1,4 @@ +package use_case.review_locations; + +public class ReviewLocationsInputData { +} diff --git a/src/main/java/use_case/review_locations/ReviewLocationsInteractor.java b/src/main/java/use_case/review_locations/ReviewLocationsInteractor.java new file mode 100644 index 000000000..cb8c08685 --- /dev/null +++ b/src/main/java/use_case/review_locations/ReviewLocationsInteractor.java @@ -0,0 +1,4 @@ +package use_case.review_locations; + +public class ReviewLocationsInteractor { +} diff --git a/src/main/java/use_case/review_locations/ReviewLocationsOutputBoundary.java b/src/main/java/use_case/review_locations/ReviewLocationsOutputBoundary.java new file mode 100644 index 000000000..92fd978be --- /dev/null +++ b/src/main/java/use_case/review_locations/ReviewLocationsOutputBoundary.java @@ -0,0 +1,4 @@ +package use_case.review_locations; + +public class ReviewLocationsOutputBoundary { +} diff --git a/src/main/java/use_case/review_locations/ReviewLocationsOutputData.java b/src/main/java/use_case/review_locations/ReviewLocationsOutputData.java new file mode 100644 index 000000000..b906eb33d --- /dev/null +++ b/src/main/java/use_case/review_locations/ReviewLocationsOutputData.java @@ -0,0 +1,4 @@ +package use_case.review_locations; + +public class ReviewLocationsOutputData { +} diff --git a/src/main/java/use_case/selected_locations/CoordinateDataAccessInterface.java b/src/main/java/use_case/selected_locations/CoordinateDataAccessInterface.java new file mode 100644 index 000000000..40f09bbca --- /dev/null +++ b/src/main/java/use_case/selected_locations/CoordinateDataAccessInterface.java @@ -0,0 +1,20 @@ +package use_case.selected_locations; + +import entity.Place; +import use_case.DataAccessException; + +/** + * Interface for the LocationDAO. It consists of methods for + * searching locations + */ +public interface CoordinateDataAccessInterface { + + /** + * Gets a list of locations based on address given. + * @param location the place the user gives + * @return a HashMap of locations and their coordinates + * @throws DataAccessException if the location can not be loaded for any reason + */ + String searchCoordinates(Place location) throws DataAccessException; + +} diff --git a/src/main/java/use_case/selected_locations/SelectedLocationsInputBoundary.java b/src/main/java/use_case/selected_locations/SelectedLocationsInputBoundary.java new file mode 100644 index 000000000..c07647b15 --- /dev/null +++ b/src/main/java/use_case/selected_locations/SelectedLocationsInputBoundary.java @@ -0,0 +1,21 @@ +package use_case.selected_locations; + +import use_case.DataAccessException; + +/** + * The input boundary for the Selected Locations Use Case. + */ +public interface SelectedLocationsInputBoundary { + /** + * Execute the Selected Locations Use Case. + * + * @param selectedLocationsInputData the input data for this use case + * @throws DataAccessException if data cannot be accessed at any time + */ + void execute(SelectedLocationsInputData selectedLocationsInputData) throws DataAccessException; + + /** + * Switches to the location view. + */ + public void switchToLocationView(); +} diff --git a/src/main/java/use_case/selected_locations/SelectedLocationsInputData.java b/src/main/java/use_case/selected_locations/SelectedLocationsInputData.java new file mode 100644 index 000000000..4d498bde1 --- /dev/null +++ b/src/main/java/use_case/selected_locations/SelectedLocationsInputData.java @@ -0,0 +1,18 @@ +package use_case.selected_locations; + +import entity.Place; + +import java.util.List; + +public class SelectedLocationsInputData { + + private final List selectedLocations; + + public SelectedLocationsInputData(List selectedLocations) { + this.selectedLocations = selectedLocations; + } + + public List getSelectedLocations() { + return selectedLocations; + } +} diff --git a/src/main/java/use_case/selected_locations/SelectedLocationsInteractor.java b/src/main/java/use_case/selected_locations/SelectedLocationsInteractor.java new file mode 100644 index 000000000..baa065152 --- /dev/null +++ b/src/main/java/use_case/selected_locations/SelectedLocationsInteractor.java @@ -0,0 +1,44 @@ +package use_case.selected_locations; + +import entity.Place; +import use_case.DataAccessException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SelectedLocationsInteractor implements SelectedLocationsInputBoundary { + private final CoordinateDataAccessInterface coordinateDataAccessInterface; + private final SelectedLocationsOutputBoundary selectedLocationsPresenter; + + public SelectedLocationsInteractor(SelectedLocationsOutputBoundary selectedLocationsOutputBoundary, + CoordinateDataAccessInterface coordinateDataAccessInterface) { + this.selectedLocationsPresenter = selectedLocationsOutputBoundary; + this.coordinateDataAccessInterface = coordinateDataAccessInterface; + } + + @Override + public void execute(SelectedLocationsInputData selectedLocationsInputData) throws DataAccessException { + final List selectedLocations = selectedLocationsInputData.getSelectedLocations(); + final Map locationCoordinatesMap = new HashMap<>(); + + for (Place location : selectedLocations) { + try { + final String coordinates = coordinateDataAccessInterface.searchCoordinates(location); + locationCoordinatesMap.put(location, coordinates); + } + catch (DataAccessException e) { + selectedLocationsPresenter.prepareFailView("Failed to fetch coordinates for: " + location.getName()); + } + } + + final SelectedLocationsOutputData selectedLocationsOutputData = + new SelectedLocationsOutputData(locationCoordinatesMap); + selectedLocationsPresenter.prepareSuccessView(selectedLocationsOutputData); + } + + @Override + public void switchToLocationView() { + selectedLocationsPresenter.switchToLocationView(); + } +} diff --git a/src/main/java/use_case/selected_locations/SelectedLocationsOutputBoundary.java b/src/main/java/use_case/selected_locations/SelectedLocationsOutputBoundary.java new file mode 100644 index 000000000..715e0a774 --- /dev/null +++ b/src/main/java/use_case/selected_locations/SelectedLocationsOutputBoundary.java @@ -0,0 +1,24 @@ +package use_case.selected_locations; + +/** + * The output boundary for the Selected Locations Use Case + */ +public interface SelectedLocationsOutputBoundary { + + /** + * Prepare the success view + * @param outputData The output data + */ + void prepareSuccessView(SelectedLocationsOutputData outputData); + + /** + * Prepare the failure view. + * @param errorMessage The explanation of the failure + */ + void prepareFailView(String errorMessage); + + /** + * Switches to the location view. + */ + void switchToLocationView(); +} diff --git a/src/main/java/use_case/selected_locations/SelectedLocationsOutputData.java b/src/main/java/use_case/selected_locations/SelectedLocationsOutputData.java new file mode 100644 index 000000000..cc26545ef --- /dev/null +++ b/src/main/java/use_case/selected_locations/SelectedLocationsOutputData.java @@ -0,0 +1,20 @@ +package use_case.selected_locations; + +import entity.Place; + +import java.util.HashMap; +import java.util.Map; + +public class SelectedLocationsOutputData { + + private final Map locationCoordinatesMap; + + public SelectedLocationsOutputData(Map locationCoordinatesMap) { + this.locationCoordinatesMap = locationCoordinatesMap; + } + + public Map getLocationCoordinatesMap() { + return locationCoordinatesMap; + } + +} diff --git a/src/main/java/use_case/signup/SignupInputBoundary.java b/src/main/java/use_case/signup/SignupInputBoundary.java new file mode 100644 index 000000000..00e88daed --- /dev/null +++ b/src/main/java/use_case/signup/SignupInputBoundary.java @@ -0,0 +1,20 @@ +package use_case.signup; + +/** + * Input boundary for the Signup use case. + */ +public interface SignupInputBoundary { + /** + * Executes the Signup use case with the provided information. + * + * @param inputData the input data + */ + void execute(SignupInputData inputData); + + /** + * Switches to the login view. + */ + + void switchToLoginView(); +} + diff --git a/src/main/java/use_case/signup/SignupInputData.java b/src/main/java/use_case/signup/SignupInputData.java new file mode 100644 index 000000000..982423e70 --- /dev/null +++ b/src/main/java/use_case/signup/SignupInputData.java @@ -0,0 +1,28 @@ +package use_case.signup; + +/** + * Data structure for holding signup input information. + */ +public class SignupInputData { + private final String username; + private final String password; + private final String repeatPassword; + + public SignupInputData(String username, String password, String repeatPassword) { + this.username = username; + this.password = password; + this.repeatPassword = repeatPassword; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getRepeatPassword() { + return repeatPassword; + } +} diff --git a/src/main/java/use_case/signup/SignupInteractor.java b/src/main/java/use_case/signup/SignupInteractor.java new file mode 100644 index 000000000..ece1512ec --- /dev/null +++ b/src/main/java/use_case/signup/SignupInteractor.java @@ -0,0 +1,53 @@ +package use_case.signup; + +import entity.User; +import entity.UserFactory; +import use_case.DataAccessException; + +/** + * The interactor for signing up a new user. + */ +public class SignupInteractor implements SignupInputBoundary { + private final SignupUserDataAccessInterface userDataAccess; + private final SignupOutputBoundary userPresenter; + private final UserFactory userFactory; + + public SignupInteractor(SignupUserDataAccessInterface userDataAccess, + SignupOutputBoundary signupOutputBoundary, + UserFactory userFactory) { + this.userDataAccess = userDataAccess; + this.userPresenter = signupOutputBoundary; + this.userFactory = userFactory; + } + + @Override + public void execute(SignupInputData signupInputData) { + final String username = signupInputData.getUsername(); + final String password = signupInputData.getPassword(); + final String repeatPassword = signupInputData.getRepeatPassword(); + + try { + if (userDataAccess.existsByName(username)) { + userPresenter.prepareFailView("User already exists."); + } + else if (!password.equals(repeatPassword)) { + userPresenter.prepareFailView("Passwords don't match."); + } + else { + final User newUser = userFactory.create(username, password); + userDataAccess.save(newUser); + + final SignupOutputData signupOutputData = new SignupOutputData(newUser.getName(), false); + userPresenter.prepareSuccessView(signupOutputData); + } + } + catch (DataAccessException error) { + userPresenter.prepareFailView("An error occurred while signing up."); + } + } + + @Override + public void switchToLoginView() { + userPresenter.switchToLoginView(); + } +} diff --git a/src/main/java/use_case/signup/SignupOutputBoundary.java b/src/main/java/use_case/signup/SignupOutputBoundary.java new file mode 100644 index 000000000..316de4aca --- /dev/null +++ b/src/main/java/use_case/signup/SignupOutputBoundary.java @@ -0,0 +1,25 @@ +package use_case.signup; + +/** + * Output boundary for the Signup use case. + */ +public interface SignupOutputBoundary { + /** + * Prepares the view with the output data. + * + * @param outputData the output data + */ + void prepareSuccessView(SignupOutputData outputData); + + /** + * Prepares the view with an error message. + * + * @param errorMessage the error message + */ + void prepareFailView(String errorMessage); + + /** + * Prepares the view to switch to the login view. + */ + void switchToLoginView(); +} diff --git a/src/main/java/use_case/signup/SignupOutputData.java b/src/main/java/use_case/signup/SignupOutputData.java new file mode 100644 index 000000000..37f1c2a89 --- /dev/null +++ b/src/main/java/use_case/signup/SignupOutputData.java @@ -0,0 +1,22 @@ +package use_case.signup; + +/** + * Data structure for holding signup output information. + */ +public class SignupOutputData { + private final String username; + private final boolean newUser; + + public SignupOutputData(String username, boolean newUser) { + this.username = username; + this.newUser = newUser; + } + + public String getUsername() { + return username; + } + + public boolean isNewUser() { + return newUser; + } +} diff --git a/src/main/java/use_case/signup/SignupUserDataAccessInterface.java b/src/main/java/use_case/signup/SignupUserDataAccessInterface.java new file mode 100644 index 000000000..59168c864 --- /dev/null +++ b/src/main/java/use_case/signup/SignupUserDataAccessInterface.java @@ -0,0 +1,31 @@ +package use_case.signup; + +import entity.User; +import use_case.DataAccessException; + +/** + * Interface for accessing user data for sign-up. + */ +public interface SignupUserDataAccessInterface { + /** + * Get a user by username. + * @param username the username to get + * @return the user with the given username + * @throws DataAccessException if the user does not exist + */ + User getSignupUser(String username) throws DataAccessException; + + /** + * Save a user to the data store. + * @param user the user to save + * @throws DataAccessException if the user already exists + */ + void save(User user) throws DataAccessException; + + /** + * Check if a user with the given username exists. + * @param username the username to check + * @return true if a user with the given username exists, false otherwise + */ + boolean existsByName(String username); +} diff --git a/src/main/java/use_case/suggested_locations/SuggestedLocationsInputBoundary.java b/src/main/java/use_case/suggested_locations/SuggestedLocationsInputBoundary.java new file mode 100644 index 000000000..6f21b7d7d --- /dev/null +++ b/src/main/java/use_case/suggested_locations/SuggestedLocationsInputBoundary.java @@ -0,0 +1,16 @@ +package use_case.suggested_locations; + +import use_case.DataAccessException; + +/** + * The Suggested Locations Use Case. + */ +public interface SuggestedLocationsInputBoundary { + /** + * Execute the Suggested Locations Use Case. + * + * @param suggestedLocationsInputData the input data for this use case + * @throws DataAccessException if data cannot be accessed at any time + */ + void execute(SuggestedLocationsInputData suggestedLocationsInputData) throws DataAccessException; +} diff --git a/src/main/java/use_case/suggested_locations/SuggestedLocationsInputData.java b/src/main/java/use_case/suggested_locations/SuggestedLocationsInputData.java new file mode 100644 index 000000000..585d7e52f --- /dev/null +++ b/src/main/java/use_case/suggested_locations/SuggestedLocationsInputData.java @@ -0,0 +1,23 @@ +package use_case.suggested_locations; + +import java.util.List; +import java.util.Map; + +import entity.Place; + +/** + * The input data for the Suggested Locations Use Case. + */ +public class SuggestedLocationsInputData { + + private final Map calendarItems; + + public SuggestedLocationsInputData(Map calendarItems) { + this.calendarItems = calendarItems; + } + + public Map getSuggestedLocations() { + return calendarItems; + } + +} diff --git a/src/main/java/use_case/suggested_locations/SuggestedLocationsInteractor.java b/src/main/java/use_case/suggested_locations/SuggestedLocationsInteractor.java new file mode 100644 index 000000000..fa0fb02c3 --- /dev/null +++ b/src/main/java/use_case/suggested_locations/SuggestedLocationsInteractor.java @@ -0,0 +1,23 @@ +package use_case.suggested_locations; + +import use_case.DataAccessException; +import use_case.selected_locations.SelectedLocationsInputData; + +/** + * The Suggested Locations Interactor. + */ +public class SuggestedLocationsInteractor implements SuggestedLocationsInputBoundary { + private final SuggestedLocationsOutputBoundary placePresenter; + + public SuggestedLocationsInteractor(SuggestedLocationsOutputBoundary suggestedLocationsOutputBoundary) { + this.placePresenter = suggestedLocationsOutputBoundary; + } + + @Override + public void execute(SuggestedLocationsInputData suggestedLocationsInputData) { +// final SuggestedLocationsOutputData suggestedLocationsOutputData = new SuggestedLocationsOutputData( +// suggestedLocationsInputData.getSuggestedLocations(), false); +// placePresenter.prepareSuccessView(suggestedLocationsOutputData) + System.out.println("got here somehow"); + } +} diff --git a/src/main/java/use_case/suggested_locations/SuggestedLocationsOutputBoundary.java b/src/main/java/use_case/suggested_locations/SuggestedLocationsOutputBoundary.java new file mode 100644 index 000000000..e66cb9d0f --- /dev/null +++ b/src/main/java/use_case/suggested_locations/SuggestedLocationsOutputBoundary.java @@ -0,0 +1,20 @@ +package use_case.suggested_locations; + +/** + * The output boundary for the Suggested Locations Use Case. + */ +public interface SuggestedLocationsOutputBoundary { + /** + * Prepares the success view for the Suggest Locations Use Case. + * + * @param outputData the output data + */ + void prepareSuccessView(SuggestedLocationsOutputData outputData); + + /** + * Prepares the failure view for the SuggestLocations Use Case. + * + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); +} diff --git a/src/main/java/use_case/suggested_locations/SuggestedLocationsOutputData.java b/src/main/java/use_case/suggested_locations/SuggestedLocationsOutputData.java new file mode 100644 index 000000000..56aeb653b --- /dev/null +++ b/src/main/java/use_case/suggested_locations/SuggestedLocationsOutputData.java @@ -0,0 +1,37 @@ +package use_case.suggested_locations; + +import java.util.List; +import java.util.Map; + +import entity.Place; + +/** + * Output Data for the Suggested Locations Use Case. + */ +public class SuggestedLocationsOutputData { + +// private final Map calendarItems; + private final List selectedLocations; + private final boolean useCaseFailed; + + public SuggestedLocationsOutputData(List selectedLocations, + boolean useCaseFailed) { +// this.calendarItems = calendarItems; + this.selectedLocations = selectedLocations; + this.useCaseFailed = useCaseFailed; + } + +// public pleaceholderforSeancode(List selectedLocations, Map calendarItems, boolean useCaseFailed) { +// this.selectedLocations = selectedLocations; +// this.calendarItems = calendarItems; +// this.useCaseFailed = useCaseFailed; +// } + + public List getSelectedLocations() { + return selectedLocations; + } + +// public Map getCalendarItems() { +// return calendarItems; +// } +} diff --git a/src/main/java/use_case/user_profile/UserProfileDataAccessInterface.java b/src/main/java/use_case/user_profile/UserProfileDataAccessInterface.java new file mode 100644 index 000000000..e1c965c6b --- /dev/null +++ b/src/main/java/use_case/user_profile/UserProfileDataAccessInterface.java @@ -0,0 +1,44 @@ +package use_case.user_profile; + +import java.util.List; +import java.util.Map; + +import entity.Place; +import entity.User; +import use_case.DataAccessException; + +/** + * Interface for accessing user profile data. + */ +public interface UserProfileDataAccessInterface { + /** + * Get the user with the given username. + * + * @param username the username + * @return the user + * @throws DataAccessException if the user does not exist + */ + User getUser(String username) throws DataAccessException; + + /** + * Save the places for the given username. + * + * @param username the username + * @param places the places to be saved, represented as a map with the place name as the key + * and a list of place objects as the value + * @throws DataAccessException if the user does not exist + */ + + void savePlaces(String username, Map> places) throws DataAccessException; + + /** + * Get the saved places for the given username. + * + * @param username the username + * @return the saved places, represented as a map with the place name as the key + * and a list of place objects as the value + * @throws DataAccessException if the user does not exist + */ + + Map> getSavedPlaces(String username) throws DataAccessException; +} diff --git a/src/main/java/use_case/user_profile/UserProfileInputBoundary.java b/src/main/java/use_case/user_profile/UserProfileInputBoundary.java new file mode 100644 index 000000000..a2faa4844 --- /dev/null +++ b/src/main/java/use_case/user_profile/UserProfileInputBoundary.java @@ -0,0 +1,26 @@ +package use_case.user_profile; + +import java.util.List; +import java.util.Map; + +import entity.Place; + +/** + * Input boundary for managing user profiles. + */ +public interface UserProfileInputBoundary { + /** + * Save the places for the given username. + * + * @param username the username + * @param places the places to be saved, represented as a map with the place name as the key + * and a list of place objects as the value + */ + void savePlaces(String username, Map> places); + + /** + * Logs out the user. + */ + + void logOut(); +} diff --git a/src/main/java/use_case/user_profile/UserProfileInputData.java b/src/main/java/use_case/user_profile/UserProfileInputData.java new file mode 100644 index 000000000..8caa8a5dc --- /dev/null +++ b/src/main/java/use_case/user_profile/UserProfileInputData.java @@ -0,0 +1,30 @@ +package use_case.user_profile; + +import java.util.List; + +/** + * The input data for creating a user profile. + */ +public class UserProfileInputData { + private final String username; + private final String password; + private final List interests; + + public UserProfileInputData(String username, String password, List interests) { + this.username = username; + this.password = password; + this.interests = interests; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public List getInterests() { + return interests; + } +} diff --git a/src/main/java/use_case/user_profile/UserProfileInteractor.java b/src/main/java/use_case/user_profile/UserProfileInteractor.java new file mode 100644 index 000000000..e35fa9c27 --- /dev/null +++ b/src/main/java/use_case/user_profile/UserProfileInteractor.java @@ -0,0 +1,37 @@ +package use_case.user_profile; + +import java.util.List; +import java.util.Map; + +import entity.Place; +import use_case.DataAccessException; + +/** + * Interactor for managing user profiles. + */ +public class UserProfileInteractor implements UserProfileInputBoundary { + private final UserProfileDataAccessInterface userProfileDataAccess; + private final UserProfileOutputBoundary userProfileOutputBoundary; + + public UserProfileInteractor(UserProfileDataAccessInterface userProfileDataAccessObject, + UserProfileOutputBoundary userProfileOutputBoundary) { + this.userProfileDataAccess = userProfileDataAccessObject; + this.userProfileOutputBoundary = userProfileOutputBoundary; + } + + @Override + public void savePlaces(String username, Map> places) { + try { + userProfileDataAccess.savePlaces(username, places); + userProfileOutputBoundary.prepareSuccessView("Places saved successfully!"); + } + catch (DataAccessException error) { + userProfileOutputBoundary.prepareFailView("Failed to save places."); + } + } + + @Override + public void logOut() { + userProfileOutputBoundary.prepareFailView("Logging out is currently not implemented."); + } +} diff --git a/src/main/java/use_case/user_profile/UserProfileOutputBoundary.java b/src/main/java/use_case/user_profile/UserProfileOutputBoundary.java new file mode 100644 index 000000000..c0fc4e470 --- /dev/null +++ b/src/main/java/use_case/user_profile/UserProfileOutputBoundary.java @@ -0,0 +1,21 @@ +package use_case.user_profile; + +/** + * Output boundary for the User Profile use case. + */ +public interface UserProfileOutputBoundary { + /** + * Prepares the view with a success message. + * + * @param message the message + */ + void prepareSuccessView(String message); + + /** + * Prepares the view with an error message. + * + * @param errorMessage the error message + */ + + void prepareFailView(String errorMessage); +} diff --git a/src/main/java/view/CalendarView.java b/src/main/java/view/CalendarView.java new file mode 100644 index 000000000..1e957b554 --- /dev/null +++ b/src/main/java/view/CalendarView.java @@ -0,0 +1,104 @@ +package view; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Map; + +import entity.Place; +import interface_adapter.add_to_calendar.AddToCalendarController; +import interface_adapter.add_to_calendar.AddToCalendarState; +import interface_adapter.add_to_calendar.AddToCalendarViewModel; +import use_case.DataAccessException; + +/** + * The View for when the user is viewing their calendar. + */ +public class CalendarView extends JPanel implements ActionListener, PropertyChangeListener { + + private final String viewName = "Calendar"; + private final AddToCalendarViewModel calendarViewModel; + private final AddToCalendarController addToCalendarController; + + private final JPanel calendarPanel; + private final JButton newSearchButton; + + private String[] calendarTimes = new String[]{"7:00AM", "8:00AM", "9:00AM", "10:00AM", "11:00AM", "12:00PM", + "1:00PM", "2:00PM", "3:00PM", "4:00PM", "5:00PM", "6:00PM", "7:00PM", "8:00PM", "9:00PM", "10:00PM"}; + + private final int viewWidth = 800; + private final int viewHeight = 1200; + + public CalendarView(AddToCalendarViewModel calendarViewModel, AddToCalendarController addToCalendarController) { + this.calendarViewModel = calendarViewModel; + this.calendarViewModel.addPropertyChangeListener(this); + this.addToCalendarController = addToCalendarController; + + this.setLayout(new BorderLayout()); + + final JLabel title = new JLabel("Calendar"); + title.setFont(new Font("Arial", Font.BOLD, 24)); + final JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + titlePanel.add(title); + this.add(titlePanel, BorderLayout.NORTH); + + this.calendarPanel = new JPanel(); + this.calendarPanel.setLayout(new BoxLayout(calendarPanel, BoxLayout.Y_AXIS)); + this.add(calendarPanel, BorderLayout.WEST); + + this.newSearchButton = new JButton("New Search"); + newSearchButton.addActionListener(this); + final JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + bottomPanel.add(newSearchButton); + this.add(bottomPanel, BorderLayout.SOUTH); + + System.out.println(calendarViewModel.getState().getCalendarItems()); + updateCalendarView(calendarViewModel.getState()); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(newSearchButton)) { + addToCalendarController.switchToLocationView(); + } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("state".equals(evt.getPropertyName())) { + System.out.println("update (calendar view)"); + updateCalendarView((AddToCalendarState) evt.getNewValue()); + } + } + + private void updateCalendarView(AddToCalendarState state) { + final Map calendarItems = state.getCalendarItems(); + System.out.println("line 75"); + for (int i = 0; i < calendarTimes.length; i++) { + final JPanel timePanel = new JPanel(); + final JTextField timeField = new JTextField(calendarTimes[i]); + timePanel.add(timeField); + for (Map.Entry entry : calendarItems.entrySet()) { + if (entry.getValue().equals(calendarTimes[i])) { + final JTextField locationField = new JTextField(entry.getKey().getName()); + final JTextField addressField = new JTextField(entry.getKey().getAddress()); + timePanel.add(locationField); + timePanel.add(addressField); + } + } +// timePanel.add(Box.createVerticalStrut(calendarTimes.length)); +// timePanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + calendarPanel.add(timePanel); + } + calendarPanel.revalidate(); + calendarPanel.repaint(); + this.setPreferredSize(new Dimension(viewWidth, viewHeight)); + } + + public String getViewName() { + return viewName; + } +} diff --git a/src/main/java/view/LabelTextPanel.java b/src/main/java/view/LabelTextPanel.java new file mode 100644 index 000000000..e7138555f --- /dev/null +++ b/src/main/java/view/LabelTextPanel.java @@ -0,0 +1,15 @@ +package view; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * A panel containing a label and a text field. + */ +class LabelTextPanel extends JPanel { + LabelTextPanel(JLabel label, JTextField textField) { + this.add(label); + this.add(textField); + } +} diff --git a/src/main/java/view/LocationView.java b/src/main/java/view/LocationView.java new file mode 100644 index 000000000..47c908770 --- /dev/null +++ b/src/main/java/view/LocationView.java @@ -0,0 +1,185 @@ +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.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import interface_adapter.location.LocationController; +import interface_adapter.location.LocationState; +import interface_adapter.location.LocationViewModel; +import use_case.DataAccessException; + +/** + * The View for when the user is viewing a location in the program. + */ +public class LocationView extends JPanel implements ActionListener, PropertyChangeListener { + + private final String locationViewName = "Search Locations"; + private final LocationViewModel locationViewModel; + private final LocationController locationController; + + private final JTextField addressField = new JTextField(20); + + private final JTextField[] locationTypeFields = new JTextField[5]; + + private final JButton suggestLocationsButton; + + private final JComboBox filtersDropDown; + private String currentFilter = ""; + + public LocationView(LocationViewModel locationViewModel, LocationController locationController) { + this.locationViewModel = locationViewModel; + this.locationViewModel.addPropertyChangeListener(this); + this.locationController = locationController; + final JLabel title = new JLabel("Suggest Locations"); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + + final LabelTextPanel addressInfo = new LabelTextPanel( + new JLabel("Address"), addressField); + + final JPanel locationTypePanel = new JPanel(); + locationTypePanel.setLayout(new BoxLayout(locationTypePanel, BoxLayout.Y_AXIS)); + for (int i = 0; i < locationTypeFields.length; i++) { + locationTypeFields[i] = new JTextField(20); + locationTypePanel.add(new LabelTextPanel(new JLabel("Location Type " + (i + 1)), locationTypeFields[i])); + } + + final JPanel buttons = new JPanel(); + suggestLocationsButton = new JButton("Suggest Locations"); + buttons.add(suggestLocationsButton); + final JLabel filterLabel = new JLabel("Filters:"); + final String[] filters = {"None", "Remove Disliked Locations", "Remove Saved Locations"}; + filtersDropDown = new JComboBox<>(filters); + buttons.add(filterLabel); + buttons.add(filtersDropDown); + suggestLocationsButton.addActionListener(this); + filtersDropDown.addActionListener(this); + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.add(title); + this.add(addressInfo); + this.add(locationTypePanel); + this.add(buttons); + + addressField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final LocationState currentState = locationViewModel.getState(); + currentState.setAddress(addressField.getText()); + locationViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + + for (JTextField locationTypeField : locationTypeFields) { + locationTypeField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final LocationState currentState = locationViewModel.getState(); + // make into one string + final StringBuilder locationTypes = new StringBuilder(); + for (JTextField field : locationTypeFields) { + if (!field.getText().isEmpty()) { + if (locationTypes.length() > 0) { + locationTypes.append(", "); + } + locationTypes.append(field.getText()); + } + } + currentState.setLocationType(locationTypes.toString()); + locationViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + } + + suggestLocationsButton.addActionListener(evt -> { + if (evt.getSource().equals(suggestLocationsButton)) { + currentFilter = filtersDropDown.getSelectedItem().toString(); + final LocationState currentState = locationViewModel.getState(); + try { + locationController.execute(currentState.getAddress(), currentState.getLocationType(), + currentFilter, currentState.getUsername()); + } + catch (DataAccessException e) { + throw new RuntimeException(e); + } + } + }); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(suggestLocationsButton)) { + final LocationState currentState = locationViewModel.getState(); + try { + locationController.execute(currentState.getAddress(), currentState.getLocationType(), currentFilter, + currentState.getUsername()); + } + catch (DataAccessException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + final LocationState state = (LocationState) evt.getNewValue(); + setFields(state); + if (state.getError() != null) { + JOptionPane.showMessageDialog(this, state.getError(), + "Error", JOptionPane.ERROR_MESSAGE); + } + } + + private void setFields(LocationState state) { + addressField.setText(state.getAddress()); + final String[] locationTypes = state.getLocationType().split(", "); + for (int i = 0; i < locationTypeFields.length; i++) { + if (i < locationTypes.length) { + locationTypeFields[i].setText(locationTypes[i]); + } + else { + locationTypeFields[i].setText(""); + } + } + } + + public String getViewName() { + return locationViewName; + } +} + diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java new file mode 100644 index 000000000..8fc91c043 --- /dev/null +++ b/src/main/java/view/LoginView.java @@ -0,0 +1,158 @@ +package view; + +import interface_adapter.login.LoginController; +import interface_adapter.login.LoginState; +import interface_adapter.login.LoginViewModel; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * The View for when the user is logging into the program. + */ +public class LoginView extends JPanel implements ActionListener, PropertyChangeListener { + + private final String viewName = "log in"; + private final LoginViewModel loginViewModel; + + private final JTextField usernameInputField = new JTextField(15); + private final JLabel usernameErrorField = new JLabel(); + + private final JPasswordField passwordInputField = new JPasswordField(15); + private final JLabel passwordErrorField = new JLabel(); + + private final JButton logIn; + private final JButton cancel; + private final LoginController loginController; + + public LoginView(LoginViewModel loginViewModel, LoginController controller) { + + this.loginController = controller; + this.loginViewModel = loginViewModel; + this.loginViewModel.addPropertyChangeListener(this); + + final JLabel title = new JLabel("Login Screen"); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + + final LabelTextPanel usernameInfo = new LabelTextPanel( + new JLabel("Username"), usernameInputField); + final LabelTextPanel passwordInfo = new LabelTextPanel( + new JLabel("Password"), passwordInputField); + + final JPanel buttons = new JPanel(); + logIn = new JButton("log in"); + buttons.add(logIn); + cancel = new JButton("cancel"); + buttons.add(cancel); + + logIn.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(logIn)) { + final LoginState currentState = loginViewModel.getState(); + + loginController.execute( + currentState.getUsername(), + currentState.getPassword() + ); + } + } + } + ); + + cancel.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(cancel)) { + loginController.switchToSignUpView(); + } + } + } + ); + + usernameInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final LoginState currentState = loginViewModel.getState(); + currentState.setUsername(usernameInputField.getText()); + loginViewModel.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)); + + passwordInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final LoginState currentState = loginViewModel.getState(); + currentState.setPassword(new String(passwordInputField.getPassword())); + loginViewModel.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(usernameInfo); + this.add(usernameErrorField); + this.add(passwordInfo); + 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 LoginState state = (LoginState) evt.getNewValue(); + setFields(state); + usernameErrorField.setText(state.getLoginError()); + } + + private void setFields(LoginState state) { + usernameInputField.setText(state.getUsername()); + } + + public String getViewName() { + return viewName; + } +} diff --git a/src/main/java/view/NoteView.java b/src/main/java/view/NoteView.java deleted file mode 100644 index 331d76493..000000000 --- a/src/main/java/view/NoteView.java +++ /dev/null @@ -1,95 +0,0 @@ -package view; - -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.BoxLayout; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextArea; - -import interface_adapter.note.NoteController; -import interface_adapter.note.NoteState; -import interface_adapter.note.NoteViewModel; - -/** - * The View for when the user is viewing a note in the program. - */ -public class NoteView extends JPanel implements ActionListener, PropertyChangeListener { - - private final NoteViewModel noteViewModel; - - private final JLabel noteName = new JLabel("note for jonathan_calver2"); - private final JTextArea noteInputField = new JTextArea(); - - private final JButton saveButton = new JButton("Save"); - private final JButton refreshButton = new JButton("Refresh"); - private NoteController noteController; - - public NoteView(NoteViewModel noteViewModel) { - - noteName.setAlignmentX(Component.CENTER_ALIGNMENT); - this.noteViewModel = noteViewModel; - this.noteViewModel.addPropertyChangeListener(this); - - final JPanel buttons = new JPanel(); - buttons.add(saveButton); - buttons.add(refreshButton); - - saveButton.addActionListener( - evt -> { - if (evt.getSource().equals(saveButton)) { - noteController.execute(noteInputField.getText()); - - } - } - ); - - refreshButton.addActionListener( - evt -> { - if (evt.getSource().equals(refreshButton)) { - noteController.execute(null); - - } - } - ); - - this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - - this.add(noteName); - this.add(noteInputField); - 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 NoteState state = (NoteState) evt.getNewValue(); - setFields(state); - if (state.getError() != null) { - JOptionPane.showMessageDialog(this, state.getError(), - "Error", JOptionPane.ERROR_MESSAGE); - } - } - - private void setFields(NoteState state) { - noteInputField.setText(state.getNote()); - } - - public void setNoteController(NoteController controller) { - this.noteController = controller; - } -} - diff --git a/src/main/java/view/ReviewLocationsView.java b/src/main/java/view/ReviewLocationsView.java new file mode 100644 index 000000000..655672fdb --- /dev/null +++ b/src/main/java/view/ReviewLocationsView.java @@ -0,0 +1,4 @@ +package view; + +public class ReviewLocationsView { +} diff --git a/src/main/java/view/SelectedLocationView.java b/src/main/java/view/SelectedLocationView.java new file mode 100644 index 000000000..aba6177d7 --- /dev/null +++ b/src/main/java/view/SelectedLocationView.java @@ -0,0 +1,122 @@ +package view; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; + +import entity.Place; +import interface_adapter.selectedlocation.SelectedLocationsController; +import interface_adapter.selectedlocation.SelectedLocationsState; +import interface_adapter.selectedlocation.SelectedLocationsViewModel; +import use_case.DataAccessException; + +/** + * The View for when the user has selected a location in the program. + */ +public class SelectedLocationView extends JPanel implements ActionListener, PropertyChangeListener { + + private final String viewName = "Selected Locations"; + private final SelectedLocationsViewModel selectedLocationsViewModel; + private final SelectedLocationsController selectedLocationsController; + + private final JButton newSearchButton; + private final JPanel selectedLocationPanel; + + public SelectedLocationView(SelectedLocationsViewModel selectedLocationsViewModel, + SelectedLocationsController selectedLocationsController) { + this.selectedLocationsViewModel = selectedLocationsViewModel; + this.selectedLocationsViewModel.addPropertyChangeListener(this); + this.selectedLocationsController = selectedLocationsController; + + final JLabel title = new JLabel("Selected Locations:"); + title.setAlignmentX(JLabel.CENTER_ALIGNMENT); + + this.selectedLocationPanel = new JPanel(); + this.selectedLocationPanel.setLayout(new BoxLayout(selectedLocationPanel, BoxLayout.Y_AXIS)); + + this.add(title); + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.add(selectedLocationPanel); + + this.newSearchButton = new JButton("New Search"); + newSearchButton.addActionListener(this); + newSearchButton.setAlignmentX(Component.CENTER_ALIGNMENT); + this.add(newSearchButton); + + updateSelectedLocations(selectedLocationsViewModel.getState()); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource().equals(newSearchButton)) { + selectedLocationsController.switchToLocationView(); + } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("state".equals(evt.getPropertyName())) { + updateSelectedLocations((SelectedLocationsState) evt.getNewValue()); + } + } + + private void updateSelectedLocations(SelectedLocationsState state) { + selectedLocationPanel.removeAll(); + final List selectedLocations = state.getSelectedLocations(); + for (Place location : selectedLocations) { + final JLabel nameLabel = new JLabel(location.getName()); + final JLabel addressLabel = new JLabel(location.getAddress()); + final JButton actionButton = new JButton("Get Directions to " + location.getName()); + final Map map = + state.getPlaceToCoordinates(); + actionButton.addActionListener(evt -> { + if (evt.getSource() == actionButton) { + try { + // Define the URL to open + final String coordinates = map.get(location); + final URI url = new URI("https://maps.google.com/?q=" + coordinates ); + + // Check if Desktop is supported + if (Desktop.isDesktopSupported()) { + final Desktop desktop = Desktop.getDesktop(); + + // Check if browsing is supported + if (desktop.isSupported(Desktop.Action.BROWSE)) { + desktop.browse(url); + } + else { + JOptionPane.showMessageDialog(this, "Browsing is not supported on your system."); + } + } + else { + JOptionPane.showMessageDialog(this, "Desktop is not supported on your system."); + } + } + catch (IOException | URISyntaxException ex) { + JOptionPane.showMessageDialog(this, "Failed to open the URL: " + ex.getMessage()); + } + } + } + ); + selectedLocationPanel.add(nameLabel); + selectedLocationPanel.add(addressLabel); + selectedLocationPanel.add(actionButton); + selectedLocationPanel.add(Box.createVerticalStrut(10)); + + selectedLocationPanel.revalidate(); + selectedLocationPanel.repaint(); + } + } + + public String getViewName() { + return viewName; + } +} \ No newline at end of file diff --git a/src/main/java/view/SignupView.java b/src/main/java/view/SignupView.java new file mode 100644 index 000000000..2514ca8d0 --- /dev/null +++ b/src/main/java/view/SignupView.java @@ -0,0 +1,180 @@ +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.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import interface_adapter.signup.SignupController; +import interface_adapter.signup.SignupState; +import interface_adapter.signup.SignupViewModel; + +/** + * View for the signup process. + */ +public class SignupView extends JPanel implements ActionListener, PropertyChangeListener { + private final String viewName = "sign up"; + + private final JTextField usernameInputField = new JTextField(15); + private final JPasswordField passwordInputField = new JPasswordField(15); + private final JPasswordField repeatPasswordInputField = new JPasswordField(15); + private final SignupController signupController; + private final SignupViewModel signupViewModel; + + public SignupView(SignupController signupController, SignupViewModel signupViewModel) { + this.signupController = signupController; + this.signupViewModel = signupViewModel; + signupViewModel.addPropertyChangeListener(this); + final JLabel title = new JLabel("Sign Up"); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + + final LabelTextPanel usernameInfo = new LabelTextPanel( + new JLabel("Pick a username:"), usernameInputField); + final LabelTextPanel passwordInfo = new LabelTextPanel( + new JLabel("Pick a password:"), passwordInputField); + final LabelTextPanel repeatPasswordInfo = new LabelTextPanel( + new JLabel("Re-enter password:"), repeatPasswordInputField); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + final JPanel buttons = new JPanel(); + final JButton signUpButton = new JButton("Sign Up"); + buttons.add(signUpButton); + final JButton gotoLoginButton = new JButton("Go to Login"); + buttons.add(gotoLoginButton); + + signUpButton.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (e.getSource() == signUpButton) { + final SignupState currentState = signupViewModel.getState(); + signupController.execute( + currentState.getUsername(), currentState.getPassword(), + currentState.getRepeatPassword() + ); + + } + } + } + ); + gotoLoginButton.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + signupController.switchToLoginView(); + } + } + ); + + addUsernameListener(); + addPasswordListener(); + addRepeatPasswordListener(); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + this.add(title); + this.add(usernameInfo); + this.add(passwordInfo); + this.add(repeatPasswordInfo); + this.add(buttons); + } + + private void addRepeatPasswordListener() { + repeatPasswordInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final SignupState currentState = signupViewModel.getState(); + currentState.setRepeatPassword(new String(repeatPasswordInputField.getPassword())); + signupViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + } + + private void addPasswordListener() { + passwordInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final SignupState currentState = signupViewModel.getState(); + currentState.setPassword(new String(passwordInputField.getPassword())); + signupViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + } + + private void addUsernameListener() { + usernameInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final SignupState currentState = signupViewModel.getState(); + currentState.setUsername(usernameInputField.getText()); + signupViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + } + + @Override + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(this, "Cancel not implemented yet."); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + final SignupState state = (SignupState) evt.getNewValue(); + if (state.getUsernameError() != null) { + JOptionPane.showMessageDialog(this, state.getUsernameError()); + } + } + + public String getViewName() { + return viewName; + } +} diff --git a/src/main/java/view/SuggestedLocationsView.java b/src/main/java/view/SuggestedLocationsView.java new file mode 100644 index 000000000..949436eea --- /dev/null +++ b/src/main/java/view/SuggestedLocationsView.java @@ -0,0 +1,201 @@ +package view; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.*; + +import entity.Place; +import interface_adapter.selectedlocation.SelectedLocationsController; +import interface_adapter.selectedlocation.SelectedLocationsState; +import interface_adapter.selectedlocation.SelectedLocationsViewModel; +import interface_adapter.add_to_calendar.AddToCalendarController; +import interface_adapter.add_to_calendar.AddToCalendarState; +import interface_adapter.add_to_calendar.AddToCalendarViewModel; +import interface_adapter.location.LocationController; +import interface_adapter.location.LocationState; +import interface_adapter.location.LocationViewModel; +import interface_adapter.suggestlocation.SuggestedLocationsController; +import interface_adapter.suggestlocation.SuggestedLocationsState; +import interface_adapter.suggestlocation.SuggestedLocationsViewModel; +import use_case.DataAccessException; + +/** + * The View for when the user receives a list of suggested locations in the program. + */ +public class SuggestedLocationsView extends JPanel implements ActionListener, PropertyChangeListener { + + private final String viewName = "Suggested Locations"; +// private final LocationViewModel locationViewModel; +// private final LocationController locationController; + private final SuggestedLocationsViewModel suggestedLocationsViewModel; + private final SuggestedLocationsController suggestedLocationsController; + private final SelectedLocationsController selectedLocationsController; + private final SelectedLocationsViewModel selectedLocationsViewModel; + private final AddToCalendarViewModel calendarViewModel; + private final AddToCalendarController calendarController; + + private final JPanel suggestedLocationsPanel; + private final JButton saveSelectionButton; + private final JButton newSearchButton; + private final JButton saveToCalendarButton; + + private final List selectedLocations; + private final Map calendarLocations; + + private final int numLocationsDisplayed = 5; + private final int viewWidth = 800; + private final int viewHeight = 1200; + + public SuggestedLocationsView(SuggestedLocationsViewModel suggestedLocationsViewModel, + SuggestedLocationsController suggestedLocationsController, + AddToCalendarViewModel calendarViewModel, + AddToCalendarController calendarController, + SelectedLocationsViewModel selectedLocationsViewModel, + SelectedLocationsController selectedLocationsController) { + + this.suggestedLocationsViewModel = suggestedLocationsViewModel; + this.suggestedLocationsViewModel.addPropertyChangeListener(this); + this.suggestedLocationsController = suggestedLocationsController; + this.selectedLocationsController = selectedLocationsController; + this.selectedLocationsViewModel = selectedLocationsViewModel; + this.calendarViewModel = calendarViewModel; + this.calendarController = calendarController; + + this.selectedLocations = new ArrayList<>(); + this.calendarLocations = new HashMap<>(); + + final JLabel title = new JLabel("List of Suggested Locations:"); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + + this.suggestedLocationsPanel = new JPanel(); + this.suggestedLocationsPanel.setLayout(new BoxLayout(suggestedLocationsPanel, BoxLayout.Y_AXIS)); + + this.saveSelectionButton = new JButton("Save Selection"); + saveSelectionButton.addActionListener(this); + saveSelectionButton.setAlignmentX(Component.CENTER_ALIGNMENT); + + this.newSearchButton = new JButton("New Search"); + newSearchButton.addActionListener(this); + newSearchButton.setAlignmentX(Component.CENTER_ALIGNMENT); + + this.saveToCalendarButton = new JButton("Save to Calendar"); + saveToCalendarButton.addActionListener(this); + saveToCalendarButton.setAlignmentX(Component.CENTER_ALIGNMENT); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.add(title); + this.add(suggestedLocationsPanel); + this.add(saveSelectionButton); + this.add(newSearchButton); + this.add(saveToCalendarButton); + + updateSuggestedLocations(suggestedLocationsViewModel.getState()); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("state".equals(evt.getPropertyName())) { + updateSuggestedLocations((SuggestedLocationsState) evt.getNewValue()); + } + } + + private void updateSuggestedLocations(SuggestedLocationsState state) { + suggestedLocationsPanel.removeAll(); + final List suggestedLocations = state.getSuggestedLocations(); + if (suggestedLocations != null) { + for (int i = 0; i < Math.min(numLocationsDisplayed, suggestedLocations.size()); i++) { + final Place location = suggestedLocations.get(i); + final JPanel locationPanel = new JPanel(); + locationPanel.setLayout(new FlowLayout()); + final JCheckBox checkBox = new JCheckBox(); + checkBox.addActionListener(e -> { + if (checkBox.isSelected()) { + selectedLocations.add(location); + } + else { + selectedLocations.remove(location); + } + }); + // array of string containing cities + final String[] times = new String[]{"None", "7:00AM", "8:00AM", "9:00AM", "10:00AM", "11:00AM", + "12:00PM", "1:00PM", "2:00PM", "3:00PM", "4:00PM", "5:00PM", "6:00PM", "7:00PM", "8:00PM", + "9:00PM", "10:00PM"}; + // create checkbox + final JComboBox timeSelection = new JComboBox<>(times); + // add ItemListener + timeSelection.addItemListener(e -> { + // if the state combobox is changed + if (e.getSource() == timeSelection) { + if (timeSelection.getSelectedItem().equals("None")) { + calendarLocations.remove(location); + } + else { + calendarLocations.put(location, timeSelection.getSelectedItem().toString()); + } + } + }); + locationPanel.add(checkBox); + locationPanel.add(new JLabel(location.getName())); + locationPanel.add(new JLabel(location.getAddress())); + locationPanel.add(Box.createVerticalStrut(numLocationsDisplayed)); + locationPanel.add(timeSelection); + locationPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + suggestedLocationsPanel.add(locationPanel); + } + final SelectedLocationsState selectedLocationsState = selectedLocationsViewModel.getState(); + selectedLocationsState.setSelectedLocations(selectedLocations); + selectedLocationsViewModel.setState(selectedLocationsState); + + final AddToCalendarState currentState = calendarViewModel.getState(); + currentState.setCalendarItems(calendarLocations); + calendarViewModel.setState(currentState); + } + else { + JOptionPane.showMessageDialog(this, "No suggested locations available.", "Info", JOptionPane.INFORMATION_MESSAGE); + } + suggestedLocationsPanel.revalidate(); + suggestedLocationsPanel.repaint(); + this.setPreferredSize(new Dimension(viewWidth, viewHeight)); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == saveSelectionButton) { + final SelectedLocationsState selectedLocationsState = selectedLocationsViewModel.getState(); + try { + selectedLocationsController.execute(selectedLocationsState.getSelectedLocations()); + } + catch (DataAccessException e) { + throw new RuntimeException(); + } + } + if (evt.getSource().equals(newSearchButton)) { +// final LocationState currentState = locationViewModel.getState(); +// try { +// locationController.execute(currentState.getAddress(), currentState.getLocationType()); + } + if (evt.getSource().equals(saveToCalendarButton)) { + final AddToCalendarState currentState = calendarViewModel.getState(); + try { + calendarController.execute(currentState.getCalendarItems()); + } + catch (DataAccessException e) { + throw new RuntimeException(e); + } + } + + } + + public String getViewName() { + return viewName; + } +} diff --git a/src/main/java/view/UserProfileView.java b/src/main/java/view/UserProfileView.java new file mode 100644 index 000000000..52add2369 --- /dev/null +++ b/src/main/java/view/UserProfileView.java @@ -0,0 +1,128 @@ +package view; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import entity.Place; +import entity.SavedPlace; +import interface_adapter.user_profile.UserProfileController; +import interface_adapter.user_profile.UserProfileViewModel; + +/** + * View for the user profile. + */ +public class UserProfileView extends JPanel implements PropertyChangeListener, ActionListener { + + private final String viewName = "User Profile"; + private final UserProfileViewModel userProfileViewModel; + private final UserProfileController userProfileController; + + private final JTextField placeNameField = new JTextField(20); + private final JTextField placeAddressField = new JTextField(20); + private final JTextField placeReviewField = new JTextField(20); + private final JCheckBox placeRatingCheckBox = new JCheckBox("Liked"); + private final JButton addPlaceButton = new JButton("Add Place"); + private final JTextArea placesListArea = new JTextArea(10, 30); + private final JButton logOutButton = new JButton("Log Out"); + + public UserProfileView(UserProfileController userProfileController, UserProfileViewModel userProfileViewModel) { + this.userProfileController = userProfileController; + this.userProfileViewModel = userProfileViewModel; + this.userProfileViewModel.addPropertyChangeListener(this); + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + // Add UI elements for adding a place + add(new JLabel("Place Name:")); + add(placeNameField); + add(new JLabel("Place Address:")); + add(placeAddressField); + add(new JLabel("Place Review:")); + add(placeReviewField); + add(placeRatingCheckBox); + add(addPlaceButton); + + addPlaceButton.addActionListener(evt -> { + final String placeName = placeNameField.getText(); + final String placeAddress = placeAddressField.getText(); + final String placeReview = placeReviewField.getText(); + final boolean rating = placeRatingCheckBox.isSelected(); + + final SavedPlace newPlace = new SavedPlace(placeName, placeAddress, placeReview, rating); + final List placeList = new ArrayList<>(); + placeList.add(newPlace); + + final Map> newPlaceMap = new HashMap<>(); + newPlaceMap.put(placeName, placeList); + + userProfileController.savePlaces(userProfileViewModel.getState().getName(), newPlaceMap); + }); + + // Display list of saved places + add(new JLabel("Saved Places:")); + add(new JScrollPane(placesListArea)); + + final Map> savedPlaces = userProfileViewModel.getState().getSavedPlaces(); + for (final Map.Entry> entry : savedPlaces.entrySet()) { + final JPanel listNamePanel = new JPanel(); + final JLabel listName = new JLabel(entry.getKey()); + final JButton viewListButton = new JButton("View List"); + listNamePanel.add(listName); + listNamePanel.add(viewListButton); + //teddy u can add the action listener for the button here + } + +// userProfileViewModel.addPropertyChangeListener(evt -> { +// // if this doesn't work, try changing the "save places" to state, and in the presenter get rid of "save places" +// if ("save places".equals(evt.getPropertyName())) { +// final Map> savedPlaces = (Map>) evt.getNewValue(); +// final StringBuilder placesString = new StringBuilder(); +// for (Map.Entry> entry : savedPlaces.entrySet()) { +// placesString.append(entry.getKey()).append(":\n"); +// for (Place place : entry.getValue()) { +// if (place instanceof SavedPlace) { +// final SavedPlace savedPlace = (SavedPlace) place; +// placesString.append(" - Address: ").append(savedPlace.getAddress()).append("\n"); +// placesString.append(" - Review: ").append(savedPlace.getReview()).append("\n"); +// placesString.append(" - Liked: ").append(savedPlace.getRating() ? "Yes" : "No").append("\n"); +// } +// } +// } +// placesListArea.setText(placesString.toString()); +// } +// }); + + // Log out button + add(logOutButton); + logOutButton.addActionListener(evt -> userProfileController.logOut()); + } + + @Override + public void actionPerformed(ActionEvent e) { + + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + + } + + public String getViewName() { + return viewName; + } + +} diff --git a/src/main/java/view/ViewManager.java b/src/main/java/view/ViewManager.java new file mode 100644 index 000000000..d0f471d71 --- /dev/null +++ b/src/main/java/view/ViewManager.java @@ -0,0 +1,27 @@ +package view; + +import interface_adapter.ViewManagerModel; +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class ViewManager implements PropertyChangeListener { + private final CardLayout cardLayout; + private final JPanel views; + private final ViewManagerModel viewManagerModel; + + public ViewManager(JPanel views, CardLayout cardLayout, ViewManagerModel viewManagerModel) { + this.cardLayout = cardLayout; + this.views = views; + this.viewManagerModel = viewManagerModel; + this.viewManagerModel.addPropertyChangeListener(this); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("state")) { + cardLayout.show(views, (String) evt.getNewValue()); + } + } +} diff --git a/src/test/java/app/MainNoteApplicationTest.java b/src/test/java/app/MainLocationApplicationTest.java similarity index 96% rename from src/test/java/app/MainNoteApplicationTest.java rename to src/test/java/app/MainLocationApplicationTest.java index 025d970e2..a0ea6d0f8 100644 --- a/src/test/java/app/MainNoteApplicationTest.java +++ b/src/test/java/app/MainLocationApplicationTest.java @@ -1,6 +1,5 @@ package app; -import entity.User; import org.junit.Before; import org.junit.Test; import use_case.note.NoteDataAccessInterface; @@ -11,7 +10,7 @@ import static java.lang.Thread.sleep; import static org.junit.Assert.*; -public class MainNoteApplicationTest { +public class MainLocationApplicationTest { private JFrame app; @@ -35,7 +34,7 @@ public String loadNote(User user) { } }; - final NoteAppBuilder builder = new NoteAppBuilder(); + final LocationAppBuilder builder = new LocationAppBuilder(); app = builder.addNoteDAO(noteDataAccess) .addNoteView() .addNoteUseCase().build(); diff --git a/src/test/java/use_case/note/NoteInteractorTest.java b/src/test/java/use_case/NoteInteractorTest.java similarity index 87% rename from src/test/java/use_case/note/NoteInteractorTest.java rename to src/test/java/use_case/NoteInteractorTest.java index a3ed466b6..51cde7287 100644 --- a/src/test/java/use_case/note/NoteInteractorTest.java +++ b/src/test/java/use_case/NoteInteractorTest.java @@ -1,7 +1,9 @@ -package use_case.note; +package use_case; -import entity.User; import org.junit.Test; +import use_case.note.NoteDataAccessInterface; +import use_case.note.NoteInteractor; +import use_case.note.NoteOutputBoundary; import static org.junit.Assert.*; diff --git a/src/test/java/use_case/add_to_calendar/AddToCalendarInteractorTest.java b/src/test/java/use_case/add_to_calendar/AddToCalendarInteractorTest.java new file mode 100644 index 000000000..87afabc3c --- /dev/null +++ b/src/test/java/use_case/add_to_calendar/AddToCalendarInteractorTest.java @@ -0,0 +1,97 @@ +package use_case.add_to_calendar; + +import data_access.InMemoryCalendarDataAccessObject; +import entity.Place; +import entity.SuggestedPlace; +import entity.SuggestedPlaceFactory; +import org.junit.Test; +import use_case.DataAccessException; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class AddToCalendarInteractorTest { + + @Test + public void successTest() { + SuggestedPlaceFactory factory = new SuggestedPlaceFactory(); + Place inputPlace = factory.create("University of Toronto","27 King's College Cir, Toronto"); + Map inputData = new HashMap<>(); + String inputTime = "12:30"; + inputData.put(inputPlace, inputTime); + AddToCalendarInputData calendarInputData = new AddToCalendarInputData(inputData); + AddToCalendarDataAccessInterface calendarRepository = new InMemoryCalendarDataAccessObject(); + + // Add University of Toronto to the data access repository. + calendarRepository.save(inputPlace, inputTime); + + // Create a successPresenter to test the test case + AddToCalendarOutputBoundary successPresenter = new AddToCalendarOutputBoundary() { + @Override + public void prepareSuccessView(AddToCalendarOutputData outputData) { + assertEquals("University of Toronto", inputPlace.getName()); + } + + @Override + public void prepareFailView(String errorMessage) { + fail("Use case failure is unexpected."); + } + + @Override + public void switchToLocationView() { + System.out.println("Switch view success"); + } + }; + + AddToCalendarInputBoundary interactor = new AddToCalendarInteractor(calendarRepository, successPresenter); + try{ + interactor.execute(calendarInputData); + } catch (DataAccessException e) { + throw new RuntimeException(e); + } + } + + @Test + public void failureTimeslotBookedTest() { + SuggestedPlaceFactory factory = new SuggestedPlaceFactory(); + Place inputPlace = factory.create("University of Toronto","27 King's College Cir, Toronto"); + Map inputData = new HashMap<>(); + String inputTime = "12:30"; + inputData.put(inputPlace, inputTime); + String inputTime2 = "12:30"; + inputData.put(inputPlace, inputTime2); + AddToCalendarInputData calendarInputData = new AddToCalendarInputData(inputData); + AddToCalendarDataAccessInterface calendarRepository = new InMemoryCalendarDataAccessObject(); + + // Add University of Toronto to the data access repository. + calendarRepository.save(inputPlace, inputTime); + + // Create a successPresenter to test the test case + AddToCalendarOutputBoundary successPresenter = new AddToCalendarOutputBoundary() { + @Override + public void prepareSuccessView(AddToCalendarOutputData outputData) { + fail("Use case success is unexpected."); + } + + @Override + public void prepareFailView(String errorMessage) { + assertEquals("12:30 is already full.", errorMessage); + } + + @Override + public void switchToLocationView() { + System.out.println("Switch view success"); + } + }; + + AddToCalendarInputBoundary interactor = new AddToCalendarInteractor(calendarRepository, successPresenter); + try{ + interactor.execute(calendarInputData); + } catch (DataAccessException e) { + throw new RuntimeException(e); + } + } +}