diff --git a/README.md b/README.md index 13f5c77403f..db7735bc27e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/AY2223S2-CS2103T-T13-4/tp/actions/workflows/gradle.yml/badge.svg)](https://github.com/AY2223S2-CS2103T-T13-4/tp/actions/) +[![codecov](https://codecov.io/gh/AY2223S2-CS2103T-T13-4/tp/branch/master/graph/badge.svg?token=BR0O1FQK5K)](https://codecov.io/gh/AY2223S2-CS2103T-T13-4/tp) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
+* This is **a desktop application for managing bookmarks**.
Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. + * To keep track of a book your are currently reading. + * To keep track of books you have finished reading. + * To keep track of books you wish to start reading. +* For the detailed documentation of this project, see the **[MyLib Product Website](https://ay2223s2-cs2103t-t13-4.github.io/tp/)**. +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/build.gradle b/build.gradle index 108397716bd..c44a144a944 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.library.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -20,6 +20,9 @@ checkstyle { toolVersion = '10.2' } +run { + enableAssertions = true; +} test { useJUnitPlatform() finalizedBy jacocoTestReport @@ -66,7 +69,7 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'MyLib.jar' } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..a867c0dc68e 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,51 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Teo Rayon - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/NoyaRoeT)] +[[portfolio](team/noyaroet.md)] -* Role: Project Advisor +* Role: Developer +* Responsibilities: Backend & Features -### Jane Doe +### Zenith Yap - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/zenithyap)] +[[portfolio](team/zenithyap.md)] -* Role: Team Lead +* Role: Developer * Responsibilities: UI -### Johnny Doe +### Wong Jia Jun - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/Wong-Jia-Jun)] +[[portfolio](team/wong-jia-jun.md)] * Role: Developer -* Responsibilities: Data - -### Jean Doe +* Responsibilities: UI +### Wong Yong Xiang - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/wongyx)] +[[portfolio](team/wongyx.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: TBC -### James Doe +### Zhou Yuxin - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/zhoyx)] +[[portfolio](team/zhoyx.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Backend work diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..f650f75b10b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -6,39 +6,81 @@ title: Developer Guide {:toc} -------------------------------------------------------------------------------------------------------------------- +
-## **Acknowledgements** + + +## **Software Overview** + +* MyLib is a desktop application originally built to serve as a single platform for organising and tracking all the online webnovels and comics that you may be reading. However, it is more than capable of doing so for any other reading material you might be interested in, such as blogs, articles, research papers and basically anything you can read. MyLib is optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). This means that most of MyLib’s features are meant to be accessed through typed commands rather than mouse clicks. + +-------------------------------------------------------------------------------------------------------------------- + +## **Intended Audience** + +This guide is intended primarily for developers who want to work on the MyLib code base at https://github.com/AY2223S2-CS2103T-T13-4/tp. + +-------------------------------------------------------------------------------------------------------------------- +
+ +## **Using the Guide** + +This guide contains certain visuals to aid in conveying information more effectively -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +| Symbol | GMFD syntax | Meaning | +|:---------------------:|:----------------------:|:-------------------------------------------:| +| :bulb: | `:bulb:` | Useful advice that devleopers can use | +| :information_source: | `:information_source:` | Supplementary information for the developer | -------------------------------------------------------------------------------------------------------------------- -## **Setting up, getting started** -Refer to the guide [_Setting up and getting started_](SettingUp.md). +## **About Us** +Please refer to the [About Us](AboutUs.md) page for information about the developers. -------------------------------------------------------------------------------------------------------------------- +## **Acknowledgements** + +* This project is based on the [AddressBook Level 3 (AB3)](https://se-education.org/addressbook-level3/) project created by the [SE-EDU initiative](https://se-education.org/). +* Libraries used: [JavaFX](https://openjfx.io/), [JUnit5](https://github.com/junit-team/junit5) + +-------------------------------------------------------------------------------------------------------------------- +## **About MyLib** +This Developer Guide provides in-depth documentation on how MyLib is designed and implemented. It covers the architecture of MyLib, detailed specifications on smaller pieces of the design, and an outline of all parts of the software and how they will work. + +You can use this guide to maintain and evolve MyLib. + +
+ ## **Design** +This section gives you a high-level overview of how the application is structured and what the key components of MyLib are. + +
:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
+ ### Architecture - + + The ***Architecture Diagram*** given above explains the high-level design of the App. Given below is a quick overview of main components and how they interact with each other. +
**Main components of the architecture** -**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, -* At app launch: Initializes the components in the correct sequence, and connects them up with each other. -* At shut down: Shuts down the components and invokes cleanup methods where necessary. +**`Main`** has two classes called [`Main`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/Main.java) and [`MainApp`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/MainApp.java). It is responsible for: +* Initializing the components in the correct sequence +* Connecting components with each other during app launch +* Shutting down the components +* Invoking cleanup methods where necessary when app shuts down [**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. @@ -60,135 +102,401 @@ Each of the four main components (also shown in the diagram above), * defines its *API* in an `interface` with the same name as the Component. * implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +
For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. The sections below give more details of each component. +
### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The UI component handles the user-interface portion of the application. +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `BookmarkListPanel`, `StatusBarFooter`, `ZoomView` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Bookmark` object residing in the `Model`. +
### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +The Logic Component handles the execution of commands +**API** : [`Logic.java`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. +1. When `Logic` is called upon to execute a command, it uses the `LibraryParser` class to parse the user command. 1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). +1. The command can communicate with the `Model` when it is executed (e.g. to add a bookmark). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +
The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png#center)
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. +* When called upon to parse a user command, the `LibraryParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `LibraryParser` returns back as a `Command` object. * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +
### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) + +The Model component holds the data of the app in memory. +**API** : [`Model.java`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/model/Model.java) The `Model` component, - -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the Library data i.e., all `Bookmark` objects (which are contained in a `UniqueBookmarkList` object). +* stores the currently 'selected' `Bookmark` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) +
-
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `Library`, which `Bookmark` references. This allows `Library` to only require one `Tag` object per unique tag, instead of each `Bookmark` needing their own `Tag` objects.
- +
### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +The Storage Component reads data from, and writes data to, the hard disk. +**API** : [`Storage.java`](https://github.com/AY2223S2-CS2103T-T13-4/tp/blob/master/src/main/java/seedu/library/storage/Storage.java) - + The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). +* can save both Library data and user preference data in json format, and read them back into corresponding objects. +* inherits from both `LibraryStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.library.commons` package. -------------------------------------------------------------------------------------------------------------------- +
## **Implementation** This section describes some noteworthy details on how certain features are implemented. +### Add Feature + +#### Implementation + +The `add` command creates a new bookmark in the library. This allows users to keep easily keep track of their readings. + +A bookmark in MyLib contains: +* Title - the title of the content that is being tracked by this bookmark +* Author - the author of the content that is being tracked by this bookmark +* Progress - consists of volume, chapter and page, and is used to track your progress with the content being tracked by this bookmark. +* Genre - the genre your content falls under. +* Rating - to indicate how much you enjoyed the content being tracked by this bookmark. +* Tags - a custom labels attached to a bookmark for custom categorization purposes. +* URL - to hold the URL of the website that the content being tracked by this bookmark is hosted at. + +For a bookmark in MyLib, everything except for title and genre is optional. + +Steps involved and interactions between components when a valid add bookmark command is received +1. Upon receiving a valid `add` command from the UI component, the `execute()` command for the `LogicManager` is called with the command inputs. +2. `LibraryParser` parses the input to determine the command type, in this case it is a `add` command, thus an `AddCommandParser` is created. +3. The `AddCommandParser` then parses the input and constructs a new `Bookmark` with the input fields based on their tags. +4. The newBookmark is then used to create an `AddCommand` to be returned to the `LogicManager`. +5. `execute()` method of `AddCommand` is then called by the `LogicManager` + 1. This adds the newBookmark to the library + 2. And returns a `CommandResult` object to the `LogidManager`. +
+ +The following sequence diagram shows the interaction between the objects when a user executes the Add command. + +![AddSequenceDiagram](images/AddSequenceDiagram.png) + +
+ +### GoTo Feature + +#### Implementation + +The `Bookmark` class will have an attribute `url` of type `URL` which represents site which the bookmark is hyperlinked to. +The `GoToCommand` will then open the site in the default browser of the user. Before the `GoToCommand` object is created. + +* The `GoToCommandParser` will checks if the command is of the correct format and argument index is valid +* Then the `GoToCommand` will execute these steps in order + 1. Get the current bookmarklist displayed + 2. Get bookmark of specified index from the bookmarklist and get its `url` + 3. Open the url in the default browser. + +The following Activity diagram depicts what happens when the `GoToCommand` is executed. + +![GoToActivity](images/GoToActivityDiagram.png) +
+ +The following sequence diagram shows the interaction between the objects when a user executes the GoTo command. + +![GoToSequence](images/GoToSequenceDiagram.png) + +#### Design Considerations: + +**Aspect: What data type to use?:** + +Currently, the url value is a `String` object that is parsed into Url object in `Bookmark` class and +is then created into a URI object when `GoToCommand` is executed + +The benefits of using `String` is that it is easy to saved and retrieve from Json Storage File. + +* **Alternative 1 (current choice):** url stored as string and URI object to open site only created in `GoToCommand`. + * Pros: Easy to implement, easier storage + * Cons: May have security due to parsing or encoding errors + +* **Alternative 2:** url stored as a URI object in Bookmark + * Pros: Will use be safer as errors are caught before object is created + * Cons: Difficulty in implementing as harder to parse for user input due to format of URI +
+ +### View Feature + +#### Implementation + +The `ViewCommand` allows user to view the bookmark's labelled details in the right panel/ Zoomview panel on the bottom right of GUI. + +The user can use the `view` command followed by a postive index/integer. The value of this index corresponds to the +index beside the bookmark the user wishes to view in the bookmarkList panel of the GUI. Therefore its value has to lie +between the 1 and the length of current displayed bookmarklist inclusive. + +
:information_source: **Note:** This `view` command can only be executed +if a bookmark exists in the bookmarkList Panel +
+ +#### Design considerations: + +**Aspect: How to pick bookmark to view?:** + +* **Alternative 1(current choice):** Pick by index given to bookmark when `bookmarkCard` object is created. + * Pros: Simple to implement and intuitive for user to pick desired bookmark based on the order in bookmarklist + * Cons: An extra field to be stored which might be unnecessary. +* **Alternative 2:** Pick based on the title of the bookmark. + * Pros: No need extra overhead or space required to store an extra field + * Cons: Some titles may have similar titles so user may have to type out entire title which may be quite long or + have some sort of combination of Title and author to identify the unique bookmark. + +
+ +### Find Feature + +#### Implementation + +The `FindCommand` allows user to find an existing bookmark by searching for a specific `Title`, `Genre`, `Tag`, and/or +`Author`. + +User can use the `find` command followed by optional prefixes of the field they want to search by and the word they +want to search for. The prefixes are `n/` for `Title`, `g/` for `Genre`, `t/` for `Tag`, and `a/` for `Author`. +An example of a user input would be: `find g/ Fantasy`. User inputs are parsed in `FindCommandParser` which will +split the input based on the prefixes. + +Some limitations of the user input includes: +1. There must be at least 1 prefix provided + * User cannot use `find` without specifying a field that they want to search for +2. The `genre` and `tag` provided must be valid + * User can only search for `genre` and `tags` that already exist in the list of `Genre` and `Tag` + * Searching for invalid `genre`/`tag` would give an error message informing the user that the `genre`/`tag` does not + exist + +#### Design considerations: + +**Aspect: Which fields of bookmark can `find` searched for?:** + +Currently, `find` allows user to search by `title`, `genre`, `tag` and `author`. + +The main reason is that it is intuitive to use these fields to identify one bookmark from another. + +A considered field to be included in `find` is the `progress` field. However, it seems counter-intuitive to search a +bookmark by the `progress` field as it is not common for people to remember how far they have read a book, so users are +unlikely to search for a bookmark using `progress`. +
+ +### Tags Feature + +#### Implementation +The `ListTagsCommand` lists all tags in the tag list. The `AddTagCommand` allows you to add tags to the tag list. +The `DeleteTagCommand` allows you to delete tags from the tag list. + +For listing the tags, you can use the `tags` command. For adding a tag to the tag list, +you can use the `addtag` command followed by the tag prefix, `t/`. An example of a user input +would be `addtag t/MaleProtagonist`. For deleting a tag from the tag list, you can use the +`dtag` command followed by the name of the tag name. An example of user input would be +`dtag MaleProtagnoist`. + +Some limitations of the user inputs include: +1. For deleting tags, only one tag name can be specified + * You cannot delete more than one tag name in a single command + +#### Design considerations: + +**Aspect: How to store the tag list?:** +The tag list is stored as a JSON file which will be saved to the user's local device. + +The main reason for this is due to the need for the user's tag list to be saved even after the user exist +the application. When the user starts the application in the future, the tag list must be how the user left it. +
+ +### Rating Field +#### Implementation +The `Rating` field of a bookmark allows user to rate books in the Library. This is stored as the attribute `Rating` +in the `Bookmark` class. + +`Rating` is an optional field to be added to a bookmark. It was represented as an integer as that allows it to be +easily displayed as stars on the user interface and thus easy to understand for the user. When no `Rating` is provided, +no star logos would be displayed. The sort command also sorts the bookmarks based on the rating provided. + +#### Design considerations: + +**Aspect: How should `Rating` be stored?** +The `Rating` field is represented by integer values from 0 to 5. This allows for a simple and intuitive way to rate +books in the `Library`. + +- **Alternative 1(current choice):** Representing it as an integer from 0 to 5 inclusive. + - Pros: Simple to keep track of and store, easily map rating to star icons in UI. + - Cons: Might not be granular enough to represent all possible values. +- **Alternative 2:** Representing it a decimal value from 0 to 5. + - Pros: Higher level of granularity. + - Cons: Difficult to represent with visual symbols in the UI to accurately reflect difference in ratings. + +**Aspect: How should `Rating` be displayed to users?** +- Alternative 1: Represented as a number for example "Rating: 4". + - Pros: Easy to implement in the UI. + - Cons: Lots of textual information already present in the UI, adding more text makes it less visually appealing. +- Alternative 2(current choice): Representing it with 5-star icons in the BookmarkList Panel. + - Pros: User can view ratings of books without having to click on the Bookmark Card. + - Cons: Harder to implement in the UI. +
+ +### Progress Field + +#### Implementation + +The `Progress` field of a `Bookmark` is used to track the latest read portion of the associated book. This could be many +things including the latest read volume of a series, or the chapter or page of a single book. It is mainly for a user to +remember where he last left off when they revisit the book. + +Currently, `Progress` contains 3 public attributes: `volume`, `chapter` and `page`, all of which are implemented as +separate `String` objects. While these attributes can be empty, at least one of them must not be empty. +
:information_source: **Note:** For a case where all 3 attributes are empty +, this should be reflected by a `Bookmark` with `Progress = null`. +
+ +The valid range of values for the 3 attributes are as follows: +1. Each of the 3 attributes can only be `~` or an unsigned integer in the form of a `String` + * `~` represents an empty value. + * If it is an unsigned integer, it may not start with `0`. +2. At least one of the 3 attributes must be an unsigned integer + * They cannot all be `~`. i.e. Cannot have an empty `Progress` + +The format for user input is: `p/VOLUME CHAPTER PAGE`. + +The valid range of values for `VOLUME`, `CHAPTER` and `PAGE` are identical to that of the `volume`, `chapter` and `page` +attributes. Similarly, the value of the 3 attributes is identical to the value stored in JSON when the `Bookmark` is +saved. For example, if `page` has the value `~`, that exact value is saved into the JSON file. +
+ +#### Design considerations: + +**Aspect: What data should `Progress` contain?:** + +Currently, Progress stores information about the volume, chapter and page of the book being tracked. + +This is believed to be sufficient for tracking basically any book since most, if not all books (online or physical) +organise themselves with the 3 attributes. + +Other possible attributes that were considered include: line number and word number. However, if we consider a user revisiting a book and wanting to continue where they last left off, +it is very unlikely that they will continue from the word or line that they stopped at. + +**Aspect: What data type to use?:** + +Currently, `volume`, `chapter` and `page` are all stored as separate `String` objects. + +This makes it easy to parse user input (which is a `String`) into a `Progress` object, and easy for a `Progress` +object to be converted into a set of `String` objects to be saved into a JSON file for storage. + +A considered alternative is to use 3 `Integer` objects instead. The benefit of this would be allowing integer +arithmetic while requiring slightly less memory. However, there are no plans for allowing a user to update `Progress` +in a way that would require integer arithmetic and the difference in memory cost is negligible. Furthermore, like other +`Bookmark` fields, `Progress` is designed to be immutable. + +**Aspect: How to represent a value that does not exist?:** + +Currently, `~` is used to represent absence of a value. + +The main reason is to simplify the logic for parsing user input. + +A considered alternative is to simply leave empty fields as an empty string `""`. However, a possible user input would +then look like `"1 50"` and it becomes impossible to differentiate between the 3 attributes. It is possible to use a +prefixes to differentiate them, but parsing becomes more complex. +
+ ### \[Proposed\] Undo/redo feature #### Proposed Implementation -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +The proposed undo/redo mechanism is facilitated by `VersionedLibrary`. It extends `Library` with an undo/redo history, stored internally as an `libraryStateList` and `currentStatePointer`. Additionally, it implements the following operations: -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +* `VersionedLibrary#commit()` — Saves the current Library state in its history. +* `VersionedLibrary#undo()` — Restores the previous Library state from its history. +* `VersionedLibrary#redo()` — Restores a previously undone Library state from its history. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +These operations are exposed in the `Model` interface as `Model#commitLibrary()`, `Model#undoLibrary()` and `Model#redoLibrary()` respectively. Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +Step 1. The user launches the application for the first time. The `VersionedLibrary` will be initialized with the initial library state, and the `currentStatePointer` pointing to that single Library state. ![UndoRedoState0](images/UndoRedoState0.png) +
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +Step 2. The user executes `delete 5` command to delete the 5th bookmark in the library. The `delete` command calls `Model#commitLibrary()`, causing the modified state of the Library after the `delete 5` command executes to be saved in the `libraryStateList`, and the `currentStatePointer` is shifted to the newly inserted Library state. ![UndoRedoState1](images/UndoRedoState1.png) -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Step 3. The user executes `add n/Hobbit …​` to add a new bookmark. The `add` command also calls `Model#commitLibrary()`, causing another modified library state to be saved into the `libaryStateList`. ![UndoRedoState2](images/UndoRedoState2.png) -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitLibrary()`, so the library state will not be saved into the `libraryStateList`.
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +Step 4. The user now decides that adding the bookmark was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoLibrary()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous library state, and restores the library to that state. ![UndoRedoState3](images/UndoRedoState3.png) -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather +
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial Library state, then there are no previous Library states to restore. The `undo` command uses `Model#canUndoLibrary()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.
@@ -201,19 +509,21 @@ The following sequence diagram shows how the undo operation works:
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +The `redo` command does the opposite — it calls `Model#redoLibrary()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the Library to that state. -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +
:information_source: **Note:** If the `currentStatePointer` is at index `libraryStateList.size() - 1`, pointing to the latest Library state, then there are no undone Library states to restore. The `redo` command uses `Model#canRedoLibrary()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Step 5. The user then decides to execute the command `list`. Commands that do not modify the Library, such as `list`, will usually not call `Model#commitLibrary()`, `Model#undoLibrary()` or `Model#redoLibrary()`. Thus, the `libraryStateList` remains unchanged. ![UndoRedoState4](images/UndoRedoState4.png) -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +Step 6. The user executes `clear`, which calls `Model#commitLibrary()`. Since the `currentStatePointer` is not pointing at the end of the `libraryStateList`, all Library states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. ![UndoRedoState5](images/UndoRedoState5.png) +
The following activity diagram summarizes what happens when a user executes a new command: @@ -223,23 +533,17 @@ The following activity diagram summarizes what happens when a user executes a ne **Aspect: How undo & redo executes:** -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +* **Alternative 1 (current choice):** Saves the entire Library. + * Pros: Easy to implement. + * Cons: May have performance issues in terms of memory usage. * **Alternative 2:** Individual command knows how to undo/redo by itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. - -_{more aspects and alternatives to be added}_ - -### \[Proposed\] Data archiving - -_{Explain here how the data archiving feature will be implemented}_ - + * Pros: Will use less memory (e.g. for `delete`, just save the bookmark being deleted). + * Cons: We must ensure that the implementation of each individual command are correct. -------------------------------------------------------------------------------------------------------------------- +
## **Documentation, logging, testing, configuration, dev-ops** @@ -250,49 +554,72 @@ _{Explain here how the data archiving feature will be implemented}_ * [DevOps guide](DevOps.md) -------------------------------------------------------------------------------------------------------------------- - ## **Appendix: Requirements** ### Product scope **Target user profile**: -* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps - -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +* Consumer of comics and web novels +* Has a need to manage the large number of books he consumes +* Prefer desktop apps over other types +* Can type fast +* Prefers typing to mouse interactions +* Is reasonably comfortable using CLI apps +**Value proposition**: +* Manage books for the user. +* Keeps track of books they have not started, is currently reading, has finished reading. +* Manage books faster than a typical mouse/GUI driven app +
### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | - -*{More to be added}* +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|----------------------------------------|------------------------------------------------|--------------------------------------------------------------------| +| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | +| `* * *` | user | add a new bookmark | start tracking a book | +| `* * *` | user | delete a bookmark | remove entries that I no longer need | +| `* * *` | user | edit a bookmark | update the information in my bookmark | +| `* *` | user | view the details of a single bookmark | see information about a particular book I am tracking | +| `* *` | user with mostly unnecessary bookmarks | clear all bookmarks | not delete each bookmark one by one | +| `* *` | user with many bookmarks | find bookmarks by book type | view bookmarks of only a certain type | +| `* *` | user with many bookmarks | find bookmarks by book genre | view bookmarks of only a certain genre | +| `* *` | user | rate a book through its bookmark | remember how much I enjoyed the book | +| `* *` | user with many bookmarks | sort bookmarks by book title | locate a bookmark easily | +| `* *` | user with many bookmarks | sort bookmarks by rating | locate bookmarks of books I enjoyed easily | +| `* *` | user who likes detail | add tags to bookmark | give additional labels to a bookmark | +| `* *` | user with many bookmarks | find bookmarks by their tags | view only bookmarks who have certain tags | +| `* *` | user with many bookmarks | find bookmarks by book author | view bookmarks of books written by a specific author | +| `* *` | user | add hyperlinks to bookmarks | link the website where I am reading the book's chapters from | +| `* *` | user | goto url of bookmarks | easily go to site of bookmark | +| `*` | user | add book characters to a bookmark | store noteworthy characters which I remember the book by | +| `*` | user | find bookmarks using name of characters | locate books with certain characters easily | +| `* *` | user with many bookmarks | sort bookmarks by their rating | view bookmarks in order of their rating | +| `* *` | user with many bookmarks | find bookmarks by last modified date | view bookmarks in order of most recently updated | +| `* *` | user | add the current reading progress to a bookmark | know where I last left off with a certain book | +| `* *` | user | find bookmarks based on their progress | view only bookmarks of a certain progress easily | +| `* *` | user | add tags to an approved tag list | standardize the tags used and make finding bookmark by tags easily | +| `* *` | user | delete a tag from the approved tag list | remove tags that I no longer need | +| `* *` | user | view tags in the approved tag list | see that tags have been added and available for use | +| `* *` | user | view the list of valid genres | see what are genres that can be added to a bookmark | + +
### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `MyLib` and the **Actor** is the `user`, unless specified otherwise) -**Use case: Delete a person** +**Use case: Delete a bookmark** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to list bookmarks +2. MyLib shows a list of bookmarks +3. User requests to delete a specific bookmark in the list +4. MyLib deletes the bookmark Use case ends. @@ -304,74 +631,498 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 3a. The given index is invalid. - * 3a1. AddressBook shows an error message. + * 3a1. MyLib shows an error message. Use case resumes at step 2. -*{More to be added}* +**Use case: Edit a bookmark** -### Non-Functional Requirements +**MSS** + +1. User requests to list bookmarks +2. MyLib shows a list of bookmarks +3. User requests to edit a specific bookmark in the list +4. MyLib edits the bookmark + + Use case ends. +
+ +**Extensions** + +* 2a. The list is empty. + + Use case ends. + +* 3a. The given index is invalid. + + * 3a1. MyLib shows an error message. + + Use case resumes at step 2. + +* 3b. Less than one field to edit provided. + + * 3b1. MyLib shows an error message. + + Use case resumes at step 2 + +* 3c. New value is invalid for any field. + + * 3c1. MyLib shows an error message. + + Use case resumes at step 2. + +**Use case: Add a bookmark** + +**MSS** + +1. User requests to add a bookmark +2. MyLib adds a bookmark + + Use case ends. + +**Extensions** + +* 1a. The given index is invalid. + + * 1a1. MyLib shows an error message. + + Use case resumes at step 2. + +* 1b. Not all compulsory fields provided. + + * 1b1. MyLib shows an error message. + + Use case resumes at step 2 + +* 1c. Value is invalid for any field. + + * 1c1. MyLib shows an error message. + + Use case resumes at step 2. + +**Use case: Finding a bookmark** + +**MSS** + +1. User requests to find a bookmark by title of some keyword +2. MyLib finds and displays all bookmarks which contains keyword in its title + + Use case ends. + +**Extensions** + +* 1a. Not all compulsory fields provided. + + * 1a1. MyLib shows an error message. + + Use case resumes at step 2 + +* 1b. Value is invalid for any field. + + * 1b1. MyLib shows an error message. + + Use case resumes at step 2. + +**Use case: Sorting all bookmark** -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +**MSS** + +1. User requests to list bookmarks +2. MyLib shows a list of bookmarks +3. User requests to sort all bookmark +4. MyLib sorts all bookmarks by rating. + + Use case ends. + +**Extensions** + +* 1a. Not all compulsory fields provided. + + * 1a1. MyLib shows an error message. + + Use case resumes at step 2 + +* 1b. Value is invalid for any field. + + * 1b1. MyLib shows an error message. + + Use case resumes at step 2. +
+ +**Use case: View a bookmark** + +**MSS** + +1. User requests to list bookmarks +2. MyLib shows a list of bookmarks +3. User requests to view a specific bookmark in the list +4. MyLib views the bookmark in right panel of UI + + Use case ends. + +**Extensions** -*{More to be added}* +* 2a. The list is empty. + + Use case ends. + +* 3a. The given index is invalid. + + * 3a1. MyLib shows an error message. + + Use case resumes at step 2. + +**Use case: Goto bookmark's Url** + +**MSS** + +1. User requests to list bookmarks +2. MyLib shows a list of bookmarks +3. User requests to goto a specific bookmark's Url in the list +4. MyLib opens url in user's default browser + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + + Use case ends. + +* 3a. The given index is invalid. + + * 3a1. MyLib shows an error message. + + Use case resumes at step 2. + +
+ +### Non-Functional Requirements + +1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Should be able to hold up to 1000 bookmark without a noticeable sluggishness in performance for typical usage. +3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +4. Saved data should be stored locally and in a human editable text file to allow advanced users to manipulate the data by editing the file. +5. The software should work without requiring an installer. +6. The Product JAR/ZIP file should not exceed 100MB. +7. A user should be able to effectively use the application after reading the help page. ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **Bookmark**: Tracker for a book that contains information about the book and a user's reading progress +* **Library**: A collection of stored Bookmarks +* **GUI**: Graphical User Interface. The visual display that the user interacts with for MyLib. -------------------------------------------------------------------------------------------------------------------- +
-## **Appendix: Instructions for manual testing** +## **Appendix A: Instructions for Manual Testing** -Given below are instructions to test the app manually. +Given below are instructions and test cases to test MyLib manually. -
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. +
:information_source: **Note:** These instructions only provide a starting point for testers to work on. +Testers are expected to do more *exploratory* testing. Also, each test case is independent of the other test cases.
-### Launch and shutdown +### Launch and Shutdown + +1. **Initial launch** + + 1. Download the [MyLib jar file](https://github.com/AY2223S2-CS2103T-T13-4/tp/releases) and copy into an empty folder. + + 2. Double-click the jar file. + + **Expected**: Shows the GUI with a set of sample bookmarks. The window size may not be optimal. + + +2. **Saving window preferences** + + 1. Resize the window to an optimum size. Move the window to a different location. Close the window. + + 2. Re-launch the app by double-clicking the jar file.
+ + **Expected**: The most recent window size and location is retained. +
+ +### Listing all Bookmarks + +1. `list` + + **Expected**: All bookmark entries are listed out and displayed in the List Panel. + + +2. `list hello` + + **Expected**: All bookmark entries are listed out and displayed in the List Panel. + +### Adding a Bookmark + +1. `add n/The Odyssey a/Homer p/1 1 1 g/Fantasy r/4 u/http://classics.mit.edu/Homer/odyssey.html t/Literature` + + **Expected**: A new bookmark entry is successfully added. The bookmark will have Title `The Odyssey`, author `Homer`, progress `Vol 1 Chapter 1 Page 1`, genre `Fantasy`, rating `4`, URL `http://classics.mit.edu/Homer/odyssey.html` and tag `Literature`. The View Panel displays all these information except URL, and a success message is displayed in the Result Display. + +2. `add n/Hobbit a/J. R. R. Tolkien p/1 ~ 256 r/4 g/Fantasy` + + **Expected**: A new bookmark entry is successfully added. The new bookmark entry will have Title `Hobbit`, author `J. R. R. Tolkien`, progress `Vol 1 Chapter ~ Page 256`, Rating `4` and genre `Fantasy`. The View Panel displays the information for this new bookmark entry, where tag is will be empty. A success message is displayed in the Result Display. + +3. `add n/Chainsaw Man r/5` + + **Expected**: No new bookmark is added. An error message is displayed in the Result Display. This is because the compulsory field Genre is missing. + +
+ +### Editing a Bookmark +Assumptions: The sample data provided by MyLib is used, where there is a total of 4 bookmark entries. + +1. `edit 2 n/Kimetsu No Yaiba` + + **Expected**: The Title of the second bookmark is updated to `Kimetsu No Yaiba`. The View Panel displays the updated details of the second bookmark entry. + +2. `edit 2 n/Darwin's Game r/1` + + **Expected**: The Title and Rating of the second bookmark entry are updated to`Darwin's Game` and `1` respectively. The View Panel displays the updated details of the second bookmark entry. + +3. `edit 2 t/` + + **Expected**: All previous tags for the second bookmark entry are removed. The View Panel displays the updated details fo the second bookmark + entry. + +4. Unsuccessful editing after finding a bookmark + 1. `find n/Solo Leveling t/Cheats` + 2. `edit 3 n/No Game No Life` + + **Expected**: An error message is displayed in the Result Display. This is because in the filtered bookmark list, there is only 1 bookmark entries, so `3` is not a valid value for the + `INDEX` field. + +5. `edit` -1. Initial launch + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 optional field must be specified. - 1. Download the jar file and copy into an empty folder +
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +### Viewing a Bookmark +Assumptions: The sample data provided by MyLib is used, where there is a total of 4 bookmark entries. +1. `view 1` -1. Saving window preferences + **Expected**: The View Panel displays the details for the first Bookmark. - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. +2. Successful viewing after finding - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. + 1. `find n/Solo n/Cheats` + 2. `view 1` -1. _{ more test cases …​ }_ + **Expected**: The View Panel displays the details for the first Bookmark entry in the filtered bookmark list. In this case, it displays the details for the entry whose Title name is `Solo Leveling`. -### Deleting a person +3. Unsuccessful viewing after finding + 1. `find n/Solo n/Cheats` + 2. `view 2` -1. Deleting a person while all persons are being shown + **Expected**: An error message is displayed in the Result Display. This is because in the filtered bookmark list, there is only 1 bookmark entry, implying that `2` is not a valid value for the `INDEX` field. + +4. `view -1` + + **Expected**: An error message is displayed in the Result Display. This is because the `INDEX` field must be a positive integer greater than or equal to 1. + +
+ +### Finding Bookmarks +Assumptions: The sample data provided by MyLib is used, where there is a total of 4 bookmark entries. +1. `find n/Solo` + + **Expected**: The List Panel shows the bookmark entry whose Title matches with`Solo`. A success message is displayed in the Result Display. + +2. `find n/Solo n/Defense` + + **Expected**: The List Panel shows the bookmark entries whose Title matches with `Defense`. This is because if only one field is required, find ignores all fields of the same prefix except the last. A success message is displayed in the Result Display. + +3. `find` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 optional field must be specified. + +5. `find t/Weird` + + **Expected**: An error message is displayed in the Result Display. This is because `Weird` is not in the valid tag list. + +
+ + +### Deleting bookmarks +Prerequisites: List all bookmarks using the `list` command. Multiple bookmarks are present in the list. +Assumptions: The sample data provided by MyLib is used, where there is a total of 4 bookmark entries. + +1. `delete 1` + + **Expected**: The first bookmark entry in the List Panel is deleted. A success message is displayed in the Result Display. + +2. `delete 1 2` + + **Expected**: An error message is displayed in the Result Display. This is because delete only takes in 1 positive index. + +3. `delete` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 index must be specified. + +3. Deleting bookmark after finding + 1. `find n/Solo` + 2. `delete 1` + + **Expected**: The first bookmark of title `Solo Leveling` is deleted. A success message is displayed in the Result Display. +
+ +### Going to bookmarks' url +Prerequisites: List all bookmarks using the `list` command. Multiple bookmarks are present in the list. +Assumptions: The sample data provided by MyLib is used, where there is a total of 4 bookmark entries. + +1. `goto 1` + + **Expected**: The first bookmark's url is opened in users default browser. A success message is displayed in the Result Display. + +2. `goto 1 2` + + **Expected**: An error message is displayed in the Result Display. This is because goto only takes in 1 positive index. + +3. `goto` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 index must be specified. + +3. Going to bookmark's url after finding + 1. `find n/Solo` + 2. `goto 1` + + **Expected**: The first bookmark's url of title `Solo Leveling` is opened in browser. A success message is displayed in the Result Display. + + +### Clear all bookmark + +1. `clear` + + **Expected**: All bookmark entries are deleted. + + +2. `clear hello` + + **Expected**: All bookmark entries are deleted. + +
+ +### Listing valid genres +1. `genre` + + **Expected**: A list of valid genres are displayed in Result Display. + +### Listing valid tags +1. `tags` + + **Expected**: A list of valid tags are displayed in Result Display. + + +2. `tags hello` + +**Expected**: A list of valid tags are displayed in Result Display. + +### Adding tags to the tag list +Assumptions: The default tags provided by MyLib is used. +1. `addtag t/Hero` + + **Expected**: A new tag "Hero" is successfully added to the tag list. A success message is displayed in the Result Display. + + +2. `addtag t/HighSchool t/Security` + + **Expected**: Two new tags "HighSchool" and "Security" are successfully added to the tag list. A success message is displayed in the Result Display. + + +3. `addtag t/Novel` + + **Expected**: No new tag added to the tag list. An error message is displayed in the Result Display. This is because the tag "Novel" already exists in the tag list. +
+ +### Deleting tags from the tag list +Assumptions: The sample data provided by MyLib is used, where there is a total of 4 bookmark entries. The default tags provided by MyLib is used. + +1. `dtag Novel` + + **Expected**: The tag "Novel" is successfully deleted from the tag list. A success message is displayed in the Result Display. + + + +2. `dtag Korean` + + **Expected**: No tag is deleted from the tag list. An error message is displayed in the Result Display. This is because there is no "Korean" tag in the tag list. + + +3. `dtag MaleProtagonist FemaleProtagonist` + + **Expected**: No tag is deleted from the tag list. An error message is displayed in the Result Display. This is because tags can only be deleted one at a time and each tag can only be one word long with no spaces in between. + + +### Get Help + +1. `help` + + **Expected**: The help window opens. + + +2. `help hello` + + **Expected**: The help window opens. + +### Exit MyLib + +1. `exit` + + **Expected**: MyLib closes. + + +2. `exit hello` + + **Expected**: MyLib closes. +
+ +### Save Data + +1. Missing Data File + + Prerequisite: There is no file called `library.json` in the `data` subfolder of the folder + where MyLib is located. + + 1. If you have an existing `library.json` file, delete the file. + 2. Double-click MyLib's jar file. + + **Expected**: MyLib launches with the sample bookmark data shown in the List Panel. There + is a total of 4 bookmark entries. +-------------------------------------------------------------------------------------------------------------------- +
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +## **Appendix B: Planned Enhancements** +Currently, there a few feature flaws with the application. These are some proposed fixes to address them and improve functionality for the user: - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. +* `edit 0` as show in Figure 1 and `edit` with any other postitive index in Figure 2 below have differing response/result in the result display box.
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +![edit-0](images/edit-0.png) +
+ Figure 1 + + : After executing edit 0 +
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +![edit-8](images/edit-8.png) +
+ Figure 2 + + : After executing edit 8 +
-1. _{ more test cases …​ }_ +Currently, there is a discrepancy where `edit 0` with missing prefix causes an error message of invalid command format +while `edit` with any positive index and missing prefix causes an error message of at least 1 field must be present. -### Saving data +**Potential Enhancement and Suggested Implementation:** +It would be great to streamline this error message by perhaps having a separate error message of "index cannot be less +than 1 " since the bookmarklist index starts from 1. And keep the current error message for indexes more than 0 -1. Dealing with missing/corrupted data files +In the `parse` method of `EditCommandParser` class , change the message thrown to "index cannot be less than 1" when +ParseException is thrown. - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ -1. _{ more test cases …​ }_ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index e7df68b01ea..fff31079210 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,152 +3,449 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. - +## **Table of Contents** * Table of Contents {:toc} +-------------------------------------------------------------------------------------------------------------------- +
+ +## **Introduction** + + + +MyLib is a desktop bookmarking application originally built to serve as a single platform for organising and tracking all the online webnovels and comics that you may be reading. However, its general bookmarking features is more than capable of handling any other bookmarking tasks you might have. This not only includes other types of novels, but also blogs, articles, reddit posts, Youtube videos and anything else that you want to track! + +With MyLib, you have the ability to throw all your bookmarks onto a **single platform**, and organise them in a **highly personalized** way via a custom set of tags or labels that you can define on your own. + +MyLib is optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). This means that most of MyLib's features are meant to be accessed through typed commands rather than mouse clicks. However, the commands are simple enough such that typing them out will not be much slower than using a GUI, if at all! + +-------------------------------------------------------------------------------------------------------------------- +[Back to Table of Contents](#table-of-contents) +
+ +## **About this User Guide** + +### Target Audience +This User Guide is mainly targeted towards new users who want to get started using MyLib to bookmark their content. It can also serve as a comprehensive reference and guide for current users of the application. + +### Objectives of this User Guide + +This User Guide provides an easy-to-understand and comprehensive documentation, so you can easily start using MyLib. It covers how to download the application, launch the application and the various features in MyLib that will make it easy for you to bookmark all your content. + +[Back to Table of Contents](#table-of-contents) +
+ +### How to use the User Guide + +This User Guide is split into multiple important sections: +* The [Getting Started](#getting-started) section provides step-by-step instructions to download and get started with MyLib. +* The [Key Definitions](#key-definitions) section provides a concise explanation of the various key terms used in this User Guide. It will explain what a bookmark is in the context of MyLib. +* The [Graphical User Interface](#graphical-user-interface) section provides a quick rundown of the different components of MyLib's user interface. +* The [Commands](#commands) section provides detailed explanations of all the various features in MyLib to help you bookmark your content. + +If you are a new user, we recommend going through the sections in the order provided above. If you are an experienced user and are just looking for a quick reference, we recommend either going through the [Commands](#commands) section or the [Command Summary](#command-summary) table. The [Commands](#commands) section will provide a detailed explanation of each command. If you're only looking for a brief overview of the commands, we recommend the [Command Summary](#command-summary) table. + +The user guide will contain certain visuals to aid in conveying information more effectively. These include: + +:information_source: **Info** - Useful supplementary information + +:bulb: **Tip** - Suggestions on how to enhance your experience + +:exclamation: **Warning** - Warning of a potentially dangerous action that you should be aware of -------------------------------------------------------------------------------------------------------------------- +[Back to Table of Contents](#table-of-contents) +
-## Quick start +## **Getting Started** 1. Ensure you have Java `11` or above installed in your Computer. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +2. Download the latest `MyLib.jar` from [here](https://github.com/AY2223S2-CS2103T-T13-4/tp/releases). + +3. Copy the file to the folder you want to use as the _home folder_ for your Library. + +4. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar MyLib.jar` command to run the application. + +5. A GUI similar to the one below should appear in a few seconds. You can get an overview of the various components that make up MyLib's GUI [here](#graphical-user-interface). + + + +6. If this is your first time running the application, it should contain some sample bookmarks. + +7. MyLib's functionality is accessed through typed commands. These commands are entered into the [command box](#graphical-user-interface) at the very top of the GUI. After entering the command, press enter to execute it and the result of that command will be displayed below the command box. +
+ +### Let's try out some of the features +1. Let's start by trying to add a bookmark. + + * Enter `add n/My Book a/My Book's Author g/Fantasy r/4` into the command box and press the Enter key. + * Adds a bookmark for the book with the title `My Book`, the author `My Book's Author` and the genre `Fantasy` to your bookmark library. + +2. Now, let's try editing the title in the bookmark you just added + + * Enter `edit 5 n/Edited Title` into the command box and press the Enter key. + + * This edits the title of your bookmark, changing it from `My Book` to `Edited Title` + +3. Finally, let's delete that bookmark and exit the application + + * Enter `delete 5` into the command box and press the Enter key. + + * This will delete your bookmark + + * Next, use `exit` to exit the application. + +4. You will be able to find a detailed guide on the list of available commands in the [Commands](#commands) section. -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +-------------------------------------------------------------------------------------------------------------------- +[Back to Table of Contents](#table-of-contents) +
+ +## **Key Definitions** +This section provides explanations of important terms that will be used in this User Guide. + +### Bookmark +As a bookmarking application, MyLib naturally uses bookmarks to bookmark your content. While MyLib was originally intended to be used for tracking web novels and comics, it's bookmarking features are general enough that your content can basically be anything. + +A bookmark in MyLib contains: +* Title - the title of the content that is being tracked by this bookmark +* Author - the author of the content that is being tracked by this bookmark +* Progress - consists of volume, chapter and page, and is used to track your progress with the content being tracked by this bookmark. +* Genre - the genre your content falls under. More details can be found [here](#genre). +* Rating - to indicate how much you enjoyed the content being tracked by this bookmark. +* Tags - a custom labels attached to a bookmark for custom categorization purposes. More details can be found [here](#tags). +* URL - to hold the URL of the website that the content being tracked by this bookmark is hosted at. + +For a bookmark in MyLib, everything except for title and genre is optional. + + +### Genre +As mentioned in [Bookmark](#bookmark), a genre in a bookmark indicates the genre of the content the bookmark is tracking. + +In MyLib, a bookmark's genre may only be selected from a fixed list of genres provided by MyLib. + +To find out how to view this fixed list of genres, go [here](#viewing-genres-genre). + +[Back to Table of Contents](#table-of-contents) +
+ +### Tags +As mentioned in [Bookmark](#bookmark), tags are custom labels that you can attach to a bookmark to categorize them. + +MyLib has a tag list, which is a list of all available tags that can be attached to a bookmark. MyLib provides a default set of tags in this tag list that you can use to tag your bookmarks. In addition, you may also view, add and delete tags from the tag list. Your tag list will be saved along with your bookmarks when you close the application. + +The commands to modify your tag list can be found at [Tag Commands](#tag-commands). + +-------------------------------------------------------------------------------------------------------------------- +[Back to Table of Contents](#table-of-contents) +
+ + +## **Graphical User Interface** +This section provides a quick rundown of the various components of MyLib's user interface. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) + -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try: +### Purpose of each GUI component - * `list` : Lists all contacts. +| Component | Purpose | +|:------------------:|:---------------------------------------------------------:| +| Command Box | To accept user commands. | +| Result Box | To display the result of the commands that user executed. | +| BookmarkList Panel | To display the current list of bookmarks | +| View Panel | To display the details of a specified bookmark | - * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. +[Back to Table of Contents](#table-of-contents) +
- * `delete 3` : Deletes the 3rd contact shown in the current list. +### Bookmark UI details - * `clear` : Deletes all contacts. + - * `exit` : Exits the app. +The bookmark in the BookmarkList panel will only contain the: +* Title +* Author +* Progress +* Genre +* Rating +* Tag -1. Refer to the [Features](#features) below for details of each command. -------------------------------------------------------------------------------------------------------------------- +[Back to Table of Contents](#table-of-contents) +
-## Features +## **Commands** +This section provides an in-depth explanation of MyLib's commands. For a quick reference of the available commands, use the [Command Summary](#command-summary) table.
**:information_source: Notes about the command format:**
* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. + e.g. in `add n/TITLE`, `TITLE` or name of `TITLE` is a parameter which can be used as `add n/The Odessey`. * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. + e.g `n/TITLE [t/TAG]` can be used as `n/The Odessey t/School` or as `n/The Odessey`. * Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. + e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/School`, `t/School t/Literature` etc. * Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. + e.g. if the command specifies `n/TITLE a/AUTHOR`, `a/AUTHOR n/TITLE` is also acceptable. -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +* If a parameter is expected only once in the command, but you specified it multiple times, only the last occurrence of the parameter will be taken.
+ e.g. if you specify `a/John Butcher a/Jim Butcher`, only `a/Jim Butcher` will be taken. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extra parameters for commands that do not take in parameters (such as `help`, `list`, `exit`, `genre`, `tags` and `clear`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`. -
-### Viewing help : `help` +[Back to Table of Contents](#table-of-contents) +
-Shows a message explaning how to access the help page. +### Tag Commands +This section goes through the commands for viewing and modifying your tag list. -![help message](images/helpMessage.png) +#### Viewing tags: `tags` +To view your list of tags, use the `tags` command. -Format: `help` +The format for the command is simply: `tags`. +Since MyLib comes with a default set of tags, you can expect a non-empty list of tags even as a new user. -### Adding a person: `add` +#### Adding tags: `addtag` +To add new tags to your tag list, use the `addtag` command. -Adds a person to the address book. +The format for the `addtag` command is as follows: -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +`addtag [t/TAG]…` -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+* `TAG` must be alphanumeric (must only contain english letters and numbers). +* Each `TAG` must be a single word with no spaces in between. Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `addtag t/Novel t/MaleProtagonist` +* `addtag t/FemaleProtagonist` -### Listing all persons : `list` +#### Deleting a tag: `dtag` +To delete a tag from your tag list, use the `dtag` command. -Shows a list of all persons in the address book. +Format: `dtag TAG` -Format: `list` +Example: +* `dtag MaleProtagonist` + +[Back to Table of Contents](#table-of-contents) +
+ +### Viewing genres: `genre` +You can view MyLib's fixed list of genres using the `genre` command. + +The format for the command is simply: `genre` + +The command should display the list of available genres in the `Result Box`. + + + +[Back to Table of Contents](#table-of-contents) +
+ +### Adding a bookmark: `add` + +To create a new bookmark and add it to your bookmark library you will need to use the `add` command. + +The format for the `add` command is as follows: -### Editing a person : `edit` +`add n/TITLE g/GENRE [a/AUTHOR] [p/PROGRESS] [r/RATING] [u/URL] [t/TAG]…​` -Edits an existing person in the address book. +The `add` command accepts the following items as user input: -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +| Prefix | Parameter | Description | +|:------:|:---------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `n/` | TITLE | This is the title that you want to give to the bookmark. Usually, this is the name of the content the bookmark is tracking.

**Restrictions:**
The title can contain any characters be it alphabets, numbers or symbols.

❗ **Caution**: certain non-English alphabets or symbols may not display correctly in the application. | +| `a/` | AUTHOR | This is the author of the content the bookmark is tracking.

**Restrictions:**
The title can contain any characters be it alphabets, numbers or symbols.

❗ **Caution**: certain non-English alphabets or symbols may not display correctly in the application. | +| `p/` | PROGRESS | This is used to remember your progress with the content being tracked by this bookmark. For example, if the bookmark is tracking a novel, the progress can be used to denote the latest read chapter.

**Restrictions:**
PROGRESS should have the format: `VOLUME CHAPTER PAGE`.

`VOLUME`, `CHAPTER` and `PAGE` should either be a positive number (without +) or `~`.

`~` is used to denote an empty `VOLUME`, `CHAPTER` and `PAGE`. For example, if you only want to use `CHAPTER` to track your progress you would do: `~ CHAPTER ~`. At least one of `VOLUME`, `CHAPTER` and `PAGE` must not be `~`. | +| `g/` | GENRE | The genre of the content the bookmark is tracking.

**Restrictions:**
MyLib provides a fixed list of genres.
Only genres in that list can be used as the genre of a bookmark.
To find out more about this list, go [here](#viewing-genres-genre). | +| `u/` | URL | This is the url to the website containing the bookmarked content.

**Restrictions:**
Must contain [Protocol][Domain name] for example: [http://]www.[example.com] | +| `r/` | RATING | This is the rating to give the bookmark.

**Restrictions:**
Must be a number from 0 to 5 (without any additional symbols like +/-) | +| `t/` | TAG | This is a tag that you want to attach to the bookmark.

**Restrictions:**
Only tags that are in your tag list can be added to a bookmark.
For more information on how to modify your tag list, go to the [Tag Commands](#tag-commands) section. | -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ +Examples: +* `add n/Solo Leveling a/Chugong p/~ 110 ~ r/4 g/Fantasy t/Novel` + * Adds a bookmark for a fantasy novel called Solo Leveling, authored by Chugong, giving it a rating of 4 and with the last read chapter being the 110th chapter. +* `add n/The Odyssey g/Action` + * Adds a bookmark for a book called The Odyssey with the Action genre. All other items are optional and are left empty. + +
+ +### Deleting a bookmark : `delete` + +Deletes the specified bookmark from the library. + +Format: `delete INDEX` + +* Deletes the bookmark at the specified `INDEX`. +* The index refers to the index number shown in the displayed bookmark list. +* The index **must be a positive integer** 1, 2, 3, …​ + +Examples: +* `list` followed by `delete 2` deletes the 2nd bookmark in the library. +* `find n/Chainsaw Man` followed by `delete 1` deletes the 1st bookmark in the results of the `find` command. + +### Editing a bookmark : `edit` + +Edits an existing bookmark in the library. + +Format: `edit INDEX [n/TITLE] [a/AUTHOR] [p/PROGRESS] [g/GENRE] [r/RATING] [u/URL] [t/TAG]…​` + +* Edits the bookmark at the specified `INDEX`. The index refers to the index number shown in the displayed bookmark list. The index **must be a positive integer** 1, 2, 3, …​ * At least one of the optional fields must be provided. +* The valid values for each item is the same as the [`add` command](#adding-a-bookmark-add). * Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +* When editing tags, the existing tags of the bookmark will be removed i.e adding of tags is not cumulative. +* You can remove all the bookmark’s tags by typing `t/` without specifying any tags after it. Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +* `edit 1 n/Hobbit a/J. R. R. Tolkien` Edits the title and author of the 1st bookmark to be `Hobbit` and `J. R. R. Tolkien` respectively. +* `edit 2 n/The Odyssey t/` Edits the name of the 2nd bookmark to be `The Odyssey` and clears all existing tags. -### Locating persons by name: `find` +[Back to Table of Contents](#table-of-contents) +
-Finds persons whose names contain any of the given keywords. +### Sorting bookmarks : `sort` -Format: `find KEYWORD [MORE_KEYWORDS]` +Sorts the list of bookmarks by ratings in either ascending or descending order. -* The search is case-insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +Format : sort [ORDER] +* ORDER can only be either `asc` or `desc` +* ORDER is case-sensitive. e.g. `sort ASC` does not work. Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* sort asc +* sort desc -### Deleting a person : `delete` +### Locating bookmarks by specific fields: `find` -Deletes the specified person from the address book. +Find bookmarks whose specified fields contain the given keywords. -Format: `delete INDEX` +`find` helps you to find bookmarks whose specified fields contain the given keywords. You can use this when you +want to filter out certain bookmarks from your large list of bookmarks. `find` allows you to search for bookmarks +using the title, author, genre and/or tags of a bookmark. + +
:bulb: **Tip:** +You can use the `list` command to get back the bookmarks that you have filtered out after `find`. +
+ +Format: `find [n/TITLE] [a/AUTHOR] [g/GENRE] [t/TAG]…​` + +* At least one of the optional fields must be provided. +* The search for name and author is case-insensitive. e.g. `rankers` will match `Rankers` +* The search for genre and tag is case-sensitive. e.g. `fantasy` will not match `Fantasy` +* The valid values for each item is the same as the [`add` command](#adding-a-bookmark-add). +* The order of the keywords matter. e.g. `Guide Rankers` will not match `Rankers Guide` +* Only the fields of the specified prefixes are searched. +* Only full words will be matched e.g. `Ranker` will not match `Ranker's`. +* The search for tags will return any bookmark that has a tag that matches the given tag. +* If multiple tags are provided, any bookmarks containing at least one of the tags would be returned. + +[Back to Table of Contents](#table-of-contents) +
+ +Examples: +* `find n/ranker's g/Fantasy` returns `Ranker's Guide to an Ordinary Life` that has the genre `Fantasy` +* `find n/Chainsaw Man` after `list` returns `chainsaw man` and `Chainsaw Man` as shown in figure 1 below. + +![Find](images/find-chainsaw%20man.png) +
+ Figure 1 + + : Executing find after start up +
+ + +### Listing all bookmarks/ Resetting filters : `list` + +Shows a list of all bookmarks in the library. + +
:bulb: **Tip:** +Use this command to get back the original list of bookmarks after using `find` command to filter out bookmarks. +
+ +Format: `list` + +[Back to Table of Contents](#table-of-contents) +
+ +### Viewing a bookmark's details : `view` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. +Display details of Bookmark on the right side panel. + +Format: `view INDEX` + +* gets bookmark at the specified `INDEX` and displays its details. +* The index refers to the index number shown in the displayed bookmark list. * The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +* `list` followed by `view 2` displays the 2nd bookmark in the library as shown in figure 2 below. +* `find n/Chainsaw Man` followed by `goto 1` displays 1st bookmark in the results of the `find` command. + +![View](images/View-command.png) + + +
+ Figure 2 + + : Executing view after list +
+ +
:bulb: **Tip:** +Our Application also supports this feature in GUI format, simply click onto the bookmark you want and watch the magic happen. +But do note that there will not be a response in result box since this is not a CLI command. +
+ +[Back to Table of Contents](#table-of-contents) +
+ +### Going to a url : `goto` + +Opens up specified bookmark's url in default browser + +Format: `goto INDEX` + +* Opens the url of bookmark at the specified `INDEX`. +* The index refers to the index number shown in the displayed bookmark list. +* The index **must be a positive integer** 1, 2, 3, …​ + +Examples: +* `list` followed by `goto 2` opens up the url of 2nd bookmark in the library. +* `find n/Chainsaw Man` followed by `goto 1` opens url of the 1st bookmark in the results of the `find` command. + +
:bulb: **Tip:** +Our Application also supports this feature in GUI format, clicking on the url in the right panel will open it on your default browser . +But do note that there will not be a response in result box since this is not a CLI command. +
### Clearing all entries : `clear` -Clears all entries from the address book. +Clears all bookmark entries from the MyLib. You will still keep your list of genres and tags after using the `clear` command. Format: `clear` +[Back to Table of Contents](#table-of-contents) +
+ +### Viewing help : `help` + +Shows a message explaning how to access the help page. + +![help message](images/helpMessage.png) + +Format: `help` + + ### Exiting the program : `exit` Exits the program. @@ -157,37 +454,85 @@ Format: `exit` ### Saving the data -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +MyLib data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. ### Editing the data file -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +MyLib data are saved as a JSON file `[JAR file location]/data/library.json`. Advanced users are welcome to update data directly by editing that data file.
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. +If your changes to the data file makes its format invalid, MyLib will discard all data and start with an empty data file at the next run.
+[Back to Table of Contents](#table-of-contents) +
+ ### Archiving data files `[coming in v2.0]` _Details coming soon ..._ -------------------------------------------------------------------------------------------------------------------- -## FAQ + +## **FAQ** **Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous MyLib home folder. + +**Q**: Do I need an internet connection to run MyLib?
+**A**: No, MyLib can boot up and run all functionalities without an internet connection. + +**Q**: Can I use MyLib on my mobile device?
+**A**: Unfortunately, MyLib is only designed to run on your desktop/laptop such that you can use the command line interface. -------------------------------------------------------------------------------------------------------------------- -## Command summary +[Back to Table of Contents](#table-of-contents) +
+ +## **Command summary** -Action | Format, Examples + Action | Format, Examples --------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +**Add a bookmark** | `add n/TITLE g/GENRE [a/AUTHOR] [p/PROGRESS] [r/RATING] [u/URL] [t/TAG]…​`
e.g., `add n/The Odyssey 2 a/Homer p/1 ~ 32 g/Others t/Literature` +**Clear bookmarks** | `clear` +**Delete a bookmark** | `delete INDEX`
e.g., `delete 3` +**Edit a bookmark** | `edit INDEX [n/TITLE] [a/AUTHOR] [p/PROGRESS] [g/GENRE] [r/RATING] [u/URL] [t/TAG]…​`
e.g.,`edit 1 n/Hobbit a/J. R. R. Tolkien` +**Find bookmarks** | `find [n/TITLE] [a/AUTHOR] [g/GENRE] [t/TAG]…​`
e.g., `find n/ Chainsaw Man` +**GoTo url of bookmark** | `goto INDEX`
e.g., `goto 3` +**List all bookmarks** | `list` +**Sort bookmark by ratings** | `sort [ORDER]`
e.g., `sort asc`, `sort desc` +**Help** | `help` +**List all tags** | `tags` +**Add a tag** | `addtag [t/TAG]…`
e.g., `addtag t/Novel` +**Delete a tag** | `dtag TAGNAME`
e.g., `dtag MaleProtagonist` +**List all genres** | `genre` + +## **Parameters Summary** + +| Prefix | Parameter | Description | +|:---------:|:---------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `n/` | TITLE | This is the title that you want to give to the bookmark. Usually, this is the name of the content the bookmark is tracking.

**Restrictions:**
The title can contain any characters be it alphabets, numbers or symbols.

❗ **Caution**: certain non-English alphabets or symbols may not display correctly in the application.
| +| `a/` | AUTHOR | This is the author of the content the bookmark is tracking.

**Restrictions:**
The title can contain any characters be it alphabets, numbers or symbols.

❗ **Caution**: certain non-English alphabets or symbols may not display correctly in the application.
| +| `p/` | PROGRESS | This is used to remember your progress with the content being tracked by this bookmark. For example, if the bookmark is tracking a novel, the progress can be used to denote the latest read chapter.

PROGRESS should have the format: `VOLUME CHAPTER PAGE`.

`VOLUME`, `CHAPTER` and `PAGE` should either be a positive number (without +) or `~`.
`~` is used to denote an empty `VOLUME`, `CHAPTER` and `PAGE`. For example, if you only want to use `CHAPTER` to track your progress you would do: `~ CHAPTER ~`. At least one of `VOLUME`, `CHAPTER` and `PAGE` must not be `~`. | +| `p/` | PROGRESS | This is used to remember your progress with the content being tracked by this bookmark. For example, if the bookmark is tracking a novel, the progress can be used to denote the latest read chapter.

**Restrictions:**
PROGRESS should have the format: `VOLUME CHAPTER PAGE`.

`VOLUME`, `CHAPTER` and `PAGE` should either be a positive number (without +) or `~`.

`~` is used to denote an empty `VOLUME`, `CHAPTER` and `PAGE`. For example, if you only want to use `CHAPTER` to track your progress you would do: `~ CHAPTER ~`. At least one of `VOLUME`, `CHAPTER` and `PAGE` must not be `~`. | +| `g/` | GENRE | The genre of the content the bookmark is tracking.

**Restrictions:**
MyLib provides a fixed list of genres.
Only genres in that list can be used as the genre of a bookmark.
To find out more about this list, go to the [Genres](#genres) section. | +| `u/` | URL | This is the url to the website containing the bookmarked content.

**Restrictions:**
Must contain [Protocol][Domain name] for example: [http://]www.[example.com] | +| `r/` | RATING | This is the rating to give the bookmark.

**Restrictions:**
Must be a number from 0 to 5 (without any additional symbols like +/-) | +| `t/` | TAG | This is a tag that you want to attach to the bookmark.

**Restrictions:**
Only tags that are in your tag list can be added to a bookmark.
For more information on how to modify your tag list, go to the [Tags](#tags) section. | + +[Back to Table of Contents](#table-of-contents) +
+ +## **Glossary** + +| Term | Explanation | +|:------------------------:|:----------------------------------------------------------------------------------------------------------------------------------:| +| Command Line Interface | A text-based user interface used to run programs and interact with the computer by typing in textual commands | +| Graphical User Interface | interface that uses icons, menus and a mouse (to click on the icon or pull down the menus) to manage interaction with the system | +| Terminal | Text-based interface used to interact with the computer's operating system | +| Parameter | A named value that is passed in by the user. This value changes depending on what the user enters. | +| Hard disk | A data storage device that maintain stored data when computer is turned off | +| Bookmark | The type of data being tracked by our app, It contains a compulsory TITLE and GENRE associated to it and other optional Parameters | + +[Back to Table of Contents](#table-of-contents) diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..3aeef0b812a 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "MyLib" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S2-CS2103T-T13-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..fefa28d7117 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "MyLib"; font-size: 32px; } } diff --git a/docs/_sass/minima/_layout.scss b/docs/_sass/minima/_layout.scss index ca99f981701..f74ddfee89d 100644 --- a/docs/_sass/minima/_layout.scss +++ b/docs/_sass/minima/_layout.scss @@ -261,3 +261,8 @@ width: calc(50% - (#{$spacing-unit} / 2)); } } + +img[src*='#center'] { + display: block; + margin: auto; +} diff --git a/docs/diagrams/AddSequenceDiagram.puml b/docs/diagrams/AddSequenceDiagram.puml new file mode 100644 index 00000000000..e3298c4af34 --- /dev/null +++ b/docs/diagrams/AddSequenceDiagram.puml @@ -0,0 +1,87 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":LibaryParser" as LibraryParser LOGIC_COLOR +participant ":AddCommandParser" as AddCommandParser LOGIC_COLOR +participant "a:AddCommand" as AddCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":Bookmark" as Bookmark MODEL_COLOR +end box + +note left of LogicManager +Let {input} be +"n/Solo Leveling +a/Chugong +p/~ 110 ~ +r/4 +g/Fantasy +t/Hero" +end note + +[-> LogicManager : execute("add {input}") +activate LogicManager + +LogicManager -> LibraryParser : parseCommand("add {input}") +activate LibraryParser + +create AddCommandParser +LibraryParser -> AddCommandParser +activate AddCommandParser + +AddCommandParser --> LibraryParser +deactivate AddCommandParser + +LibraryParser -> AddCommandParser : parse("{input}") +activate AddCommandParser + +create Bookmark +AddCommandParser -> Bookmark +activate Bookmark + +Bookmark --> AddCommandParser : newBookmark +deactivate Bookmark + +create AddCommand +AddCommandParser -> AddCommand +activate AddCommand + +AddCommand --> AddCommandParser : a +deactivate AddCommand + +AddCommandParser --> LibraryParser : a +deactivate AddCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddCommandParser -[hidden]-> LibraryParser +destroy AddCommandParser + +LibraryParser --> LogicManager : a +deactivate LibraryParser + +LogicManager -> AddCommand : execute() +activate AddCommand + +AddCommand -> Model : addBookmark(newBookmark) +activate Model + +Model --> AddCommand +deactivate Model + +create CommandResult +AddCommand -> CommandResult +activate CommandResult + +CommandResult --> AddCommand +deactivate CommandResult + +AddCommand --> LogicManager : result +deactivate AddCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..dadce33b667 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -13,13 +13,13 @@ activate ui UI_COLOR ui -[UI_COLOR]> logic : execute("delete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteBookmark(b) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveLibrary(library) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 598474a5c82..f9be657143d 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,20 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +Library *-right-> "1" UniqueBookmarkList +Library *-right-> "1" UniqueTagList +UniqueBookmarkList -[hidden]down- UniqueBookmarkList +UniqueTagList -[hidden]down- UniqueBookmarkList UniqueTagList -right-> "*" Tag -UniquePersonList -right-> Person +UniqueBookmarkList -right-> Bookmark -Person -up-> "*" Tag +Bookmark -up-> "*" Tag -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address +Bookmark *--> Title +Bookmark *--> Author +Bookmark *--> Progress +Bookmark *--> Genre +Bookmark *--> URL +Bookmark *--> Rating @enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..d6d15eb58a4 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -1,14 +1,18 @@ @startuml +!include style.puml +skinparam activity { +BackgroundColor #FFFFD0 +} start :User executes command; 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([command commits AddressBook]) +if () then ([command commits Library]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save Library to + libraryStateList; else ([else]) endif stop diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..3afe48419e0 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,7 +3,7 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":LibaryParser" as LibraryParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR @@ -13,20 +13,20 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("del 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> LibraryParser : parseCommand("del 1") +activate LibraryParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +LibraryParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> LibraryParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +LibraryParser -> DeleteCommandParser : parse("1") activate DeleteCommandParser create DeleteCommand @@ -36,19 +36,19 @@ activate DeleteCommand DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> LibraryParser : d deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> LibraryParser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +LibraryParser --> LogicManager : d +deactivate LibraryParser LogicManager -> DeleteCommand : execute() activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deleteBookmark(1) activate Model Model --> DeleteCommand diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..b79951db179 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class LibraryParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" LibraryParser +LibraryParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..81a2a703ee2 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,46 +5,50 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +Class "<>\nReadOnlyLibrary" as ReadOnlyLibrary Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs -Class "<>\nModel" as Model -Class AddressBook +Class "<>\nModel" as Model1 +Class Library Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person -Class Address -Class Email -Class Name -Class Phone +Class UniqueBookmarkList +Class Bookmark +Class Genre +Class Progress +Class Title +Class Author Class Tag +Class URL +class Rating } Class HiddenOutside #FFFFFF -HiddenOutside ..> Model +HiddenOutside ..> Model1 -AddressBook .up.|> ReadOnlyAddressBook +Library .up.|> ReadOnlyLibrary -ModelManager .up.|> Model -Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +ModelManager .up.|> Model1 +Model1 .right.> ReadOnlyUserPrefs +Model1 .left.> ReadOnlyLibrary +ModelManager -left-> "1" Library ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag - -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email - -ModelManager -->"~* filtered" Person +Library *--> "1" UniqueBookmarkList +UniqueBookmarkList --> "~* all" Bookmark +Bookmark *--> Title +Bookmark *--> Author +Bookmark *--> Genre +Bookmark *--> Progress +Bookmark *--> "*" Tag +Bookmark *--> URL +Bookmark *--> Rating + +Title -[hidden]right-> Author +Author -[hidden]right-> Genre +Genre -[hidden]right-> Progress + +ModelManager -->"~* filtered" Bookmark @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..efde4561c70 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser -Class AddressBookParser +Class LibraryParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> LibraryParser -AddressBookParser .down.> XYZCommandParser: creates > +LibraryParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +LibraryParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..5e611e24d71 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,11 +14,11 @@ Class JsonUserPrefsStorage Class "<>\nStorage" as Storage Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson +package "Library Storage" #F4F6F6{ +Class "<>\nLibraryStorage" as LibraryStorage +Class JsonLibraryStorage +Class JsonSerializableLibrary +Class JsonAdaptedBookmark Class JsonAdaptedTag } @@ -29,15 +29,15 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" LibraryStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> LibraryStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonLibraryStorage .up.|> LibraryStorage +JsonLibraryStorage ..> JsonSerializableLibrary +JsonSerializableLibrary --> "*" JsonAdaptedBookmark +JsonAdaptedBookmark --> "*" JsonAdaptedTag @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..1cec9e4167c 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,10 +11,11 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class BookmarkListPanel +Class BookmarkCard Class StatusBarFooter Class CommandBox +Class ZoomView } package Model <> { @@ -30,28 +31,32 @@ HiddenOutside ..> Ui UiManager .left.|> Ui UiManager -down-> "1" MainWindow +MainWindow *-> "1" ZoomView MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" BookmarkListPanel MainWindow *-down-> "1" StatusBarFooter + MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard + +BookmarkListPanel -down-> "*" BookmarkCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +BookmarkListPanel --|> UiPart +BookmarkCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +ZoomView --|> UiPart -PersonCard ..> Model +BookmarkCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +BookmarkListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..10e689fb8ef 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -3,18 +3,19 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title Initial state +title After command "clear" state package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "lib0:Library"#FFFFD0 + class State2 as "lib1:Library"#FFFFD0 + class State3 as "lib2:Library"#FFFFD0 } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -hide State2 -hide State3 -class Pointer as "Current State" #FFFFF -Pointer -up-> State1 + + +class Pointer as "Current State" #FFFFFF +Pointer -up-> State3 +note right on link: State lib2 deleted. @end diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..0e2c8c72d33 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -16,7 +16,7 @@ State2 -[hidden]right-> State3 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..0ce7073e187 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 @end diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..50bf43b3f34 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..83cbe4c740c 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..fc89dd99d2d 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 note right on link: State ab2 deleted. diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..29714e2352a 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,42 +3,42 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":LibraryParser" as LibraryParser LOGIC_COLOR participant "u:UndoCommand" as UndoCommand LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR +participant ":VersionedLibrary" as VersionedLibrary MODEL_COLOR end box [-> LogicManager : execute(undo) activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> LibraryParser : parseCommand(undo) +activate LibraryParser create UndoCommand -AddressBookParser -> UndoCommand +LibraryParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> LibraryParser deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +LibraryParser --> LogicManager : u +deactivate LibraryParser LogicManager -> UndoCommand : execute() activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoLibrry() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedLibrary: undo() +activate VersionedLibrary -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedLibrary -> VersionedLibrary :resetData(ReadOnlyLibrary) +VersionedLibrary --> Model : +deactivate VersionedLibrary Model --> UndoCommand deactivate Model diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml index fdcbe1c0ccc..a0498898e2e 100644 --- a/docs/diagrams/tracing/LogicSequenceDiagram.puml +++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml @@ -2,7 +2,7 @@ !include ../style.puml Participant ":LogicManager" as logic LOGIC_COLOR -Participant ":AddressBookParser" as abp LOGIC_COLOR +Participant ":LibraryParser" as abp LOGIC_COLOR Participant ":EditCommandParser" as ecp LOGIC_COLOR Participant "command:EditCommand" as ec LOGIC_COLOR @@ -13,7 +13,7 @@ create ecp abp -> ecp abp -> ecp ++: parse(arguments) create ec -ecp -> ec ++: index, editPersonDescriptor +ecp -> ec ++: index, editBookmarkDescriptor ec --> ecp -- ecp --> abp --: command abp --> logic --: command diff --git a/docs/images/AddSequenceDiagram.png b/docs/images/AddSequenceDiagram.png new file mode 100644 index 00000000000..4072da03ad4 Binary files /dev/null and b/docs/images/AddSequenceDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..db420b9e41f 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 94440f0ac4a..8e678858c1a 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png index c08c13f5c8b..9f62e99b327 100644 Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..4629941ef3b 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/GoToActivityDiagram.png b/docs/images/GoToActivityDiagram.png new file mode 100644 index 00000000000..f302293aff3 Binary files /dev/null and b/docs/images/GoToActivityDiagram.png differ diff --git a/docs/images/GoToSequenceDiagram.png b/docs/images/GoToSequenceDiagram.png new file mode 100644 index 00000000000..618b995b43b Binary files /dev/null and b/docs/images/GoToSequenceDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..4a12692aaa4 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..4e9af4ea2e4 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/MyLibLogo.png b/docs/images/MyLibLogo.png new file mode 100644 index 00000000000..f81e8fc90a1 Binary files /dev/null and b/docs/images/MyLibLogo.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..f18d8e373e9 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..fd7ea971217 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..8b5e4879d63 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..367037cdfae 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png index 8f7538cd884..ccc5e380288 100644 Binary files a/docs/images/UndoRedoState0.png and b/docs/images/UndoRedoState0.png differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png index df9908d0948..36cee543cf8 100644 Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png index 36519c1015b..9e53448519d 100644 Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png index 19959d01712..a10c388155b 100644 Binary files a/docs/images/UndoRedoState3.png and b/docs/images/UndoRedoState3.png differ diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png index 4c623e4f2c5..e50b184322f 100644 Binary files a/docs/images/UndoRedoState4.png and b/docs/images/UndoRedoState4.png differ diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png index 84ad2afa6bd..d7864cbc263 100644 Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png index 6addcd3a8d9..666ead02294 100644 Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ diff --git a/docs/images/View-command.png b/docs/images/View-command.png new file mode 100644 index 00000000000..f85b1e1c4ea Binary files /dev/null and b/docs/images/View-command.png differ diff --git a/docs/images/Wong-Jia-Jun.png b/docs/images/Wong-Jia-Jun.png new file mode 100644 index 00000000000..a2f718cb0af Binary files /dev/null and b/docs/images/Wong-Jia-Jun.png differ diff --git a/docs/images/add-tag.png b/docs/images/add-tag.png new file mode 100644 index 00000000000..4441f1147b6 Binary files /dev/null and b/docs/images/add-tag.png differ diff --git a/docs/images/annotated-UI.png b/docs/images/annotated-UI.png new file mode 100644 index 00000000000..8d939066d2e Binary files /dev/null and b/docs/images/annotated-UI.png differ diff --git a/docs/images/bookmarkCardUI.png b/docs/images/bookmarkCardUI.png new file mode 100644 index 00000000000..c9c59add6d0 Binary files /dev/null and b/docs/images/bookmarkCardUI.png differ diff --git a/docs/images/chapter.png b/docs/images/chapter.png new file mode 100644 index 00000000000..96c85c54c01 Binary files /dev/null and b/docs/images/chapter.png differ diff --git a/docs/images/edit-0.png b/docs/images/edit-0.png new file mode 100644 index 00000000000..12c4f7a5802 Binary files /dev/null and b/docs/images/edit-0.png differ diff --git a/docs/images/edit-8.png b/docs/images/edit-8.png new file mode 100644 index 00000000000..7902e75c01f Binary files /dev/null and b/docs/images/edit-8.png differ diff --git a/docs/images/find-chainsaw man.png b/docs/images/find-chainsaw man.png new file mode 100644 index 00000000000..86f43d9cf79 Binary files /dev/null and b/docs/images/find-chainsaw man.png differ diff --git a/docs/images/genres.png b/docs/images/genres.png new file mode 100644 index 00000000000..3336e69fab1 Binary files /dev/null and b/docs/images/genres.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..07ab2fc7f04 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/noyaroet.png b/docs/images/noyaroet.png new file mode 100644 index 00000000000..6924722ec39 Binary files /dev/null and b/docs/images/noyaroet.png differ diff --git a/docs/images/page.png b/docs/images/page.png new file mode 100644 index 00000000000..89906dec5eb Binary files /dev/null and b/docs/images/page.png differ diff --git a/docs/images/volume.png b/docs/images/volume.png new file mode 100644 index 00000000000..8e26c3a372f Binary files /dev/null and b/docs/images/volume.png differ diff --git a/docs/images/wong-jia-jun.png b/docs/images/wong-jia-jun.png new file mode 100644 index 00000000000..a2f718cb0af Binary files /dev/null and b/docs/images/wong-jia-jun.png differ diff --git a/docs/images/wongyx.png b/docs/images/wongyx.png new file mode 100644 index 00000000000..b77c40d233a Binary files /dev/null and b/docs/images/wongyx.png differ diff --git a/docs/images/zenithyap.png b/docs/images/zenithyap.png new file mode 100644 index 00000000000..bf47bbe031c Binary files /dev/null and b/docs/images/zenithyap.png differ diff --git a/docs/images/zhoyx.png b/docs/images/zhoyx.png new file mode 100644 index 00000000000..8466079e305 Binary files /dev/null and b/docs/images/zhoyx.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..5e3a841ab09 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: AddressBook Level-3 +title: MyLib --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) @@ -8,10 +8,10 @@ title: AddressBook Level-3 ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +MyLib is a desktop application originally developed to bookmark webnovels and comics. However, its bookmarking features can accomodate basically any form of content you want to track. While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using MyLib, head over to the [**User Guide**](UserGuide.html). +* If you are interested about developing MyLib, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/Wong-Jia-Jun.md b/docs/team/Wong-Jia-Jun.md new file mode 100644 index 00000000000..d94a32d0db5 --- /dev/null +++ b/docs/team/Wong-Jia-Jun.md @@ -0,0 +1,45 @@ +--- +layout: page +title: Wong-Jia-Jun's Project Portfolio Page +--- + +### Project: MyLib + +MyLib is a desktop bookmarking application used for keeping track of books a user is reading. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Code Contributed**: [RepoSense hyperlink](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=wong-jia-jun&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17) + +* **New Feature**: Added GoTo command functionality. This involves opening url of bookmark in user's default browser [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files), [#184](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/184) + * What it does: Opens url of a bookmark in user's browser + * Justification: This provides convenience to the user instead of having to copy the url field and pasting after opening a browser + +* **New Feature**: Added the View Command functionality [#176](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/176/files) + * What it does: Allows the user to view specifics of a bookmark more clearly in a separate panel + * Justification: This feature allows users to see more clearly each field of the bookmark as well as longer fields like url which is not present in bookmark on left panel since it may be quite long + +* **Enhancements**: + * Revamp GUI and add on-click support for some commands [#80](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/80), [#99](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/99/files), [#153](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/153/files), [#160](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/160/files) + +* Add Test cases for Code Coverage [#253](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/253/files) + +* **Documentation**: + * User Guide: + * Added documentation for the features `view` and `goto` [#195](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/195/files), [#159](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/159), [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files) + * Added new pictures for UI, UI components, bookmark details and commands `find` and `view` [#256](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/256/files) + * Added Glossary table, GUI components table and Prefix table [#169](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/169/files), [#200](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/200/files) + * Added About User Guide + * Update FAQ [#184](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/184/files) + + * Developer Guide: + * Added implementation details of the `view` and `goto` features. [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files) + * Update delete sequence diagram + * Add diagrams for `goto` feature [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files) + * Update Appendix A Manual Testing [#278](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/278/files) + * Add Intended Audience, About MyLib, Using the Guide portion [#178](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/178/files) + * Update page breaks for pdf conversion [#337](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/337) + +* **Contributions to team-based tasks** + * Created release for V1.3 + * Maintain issue tracker for tasks diff --git a/docs/team/noyaroet.md b/docs/team/noyaroet.md new file mode 100644 index 00000000000..17a4e8799d0 --- /dev/null +++ b/docs/team/noyaroet.md @@ -0,0 +1,45 @@ +--- +layout: page +title: Teo Rayon's Project Portfolio Page +--- + +### Project: MyLib + +MyLib is a desktop bookmarking application used for keeping track of webnovels and comic a user is reading. Its bookmarking features are general enough to perform bookmarking tasks for other things like blogs, articles and basically anything. The user interacts with the application using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. +* **Code Contributed**: [Reposense Link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=CS2103T-T13-4&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=NoyaRoeT&tabRepo=AY2223S2-CS2103T-T13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **New Feature**: Added the ability to find bookmarks by title using keywords + * What it does: allows the user to find bookmarks whose title contains at least one of the provided keywords. + * Justification: this feature improves the product significantly since it allows a user to quickly find a bookmark whose title contains one of the provided keywords. Being able to quickly find a bookmark is an important aspect of any bookmarking application. + * Credits: the implementation of this feature made use of existing infrastructure from AB3. Specifically, AB3 already had a feature that allowed finding people whose name contains at least one word in a provided set of keywords. That existing implementation was modified to be used for finding bookmarks instead. + +* **New Feature**: Changed most of bookmark data to be optional + * What it does: allows the user to omit certain bookmark data when adding bookmarks + * Justification: many fields that a bookmark can contain may not be required for bookmarking tasks. For example, some users may not care about the author of a book. Hence, it is more convenient for the user if only certain fields were made compulsory. + * Highlights: many issues arose due to the fact that we inherited most of AB3's code, where all the fields in a Person object were compulsory. + * The Bookmark class was created by modifying the Person class. It inherited all of Person's test cases which expects all fields to be compulsory. These test cases needed to be tracked down, updated or deleted. + * When loading the data for MyLib from json, the conversion from json to a `Bookmark` object expects the fields to be present and non-null. Needed to modify the conversion from json to a Bookmark object to accept optional fields that are not present. +
+ +* **New Feature**: Added a `Progress` field to a bookmark + * What it does: allows a bookmark to track a user's progress with regards to the book being bookmarked. + * Justification: one of the goals of our application is to track books the user is reading, so they may revisit the book in the future. Tracking progress allows them to easily see where they last left off with a book and continue from there if they decide to do so. + * Highlights: as a field in the Bookmark class which forms the core data of our application, this enhancement will affect all future updates to the application. Hence, it required more thorough considerations of the various design alternatives. These include considerations such as how user input should be represented, how should the interface of the class look like to blend well with the other existing bookmark data, whether there is a need to separate volume, chapter and page which are contained in `Progress` into their own separate classes and how should progress input from the user be parsed and used to construct a `Progress` object. There was also a need to consider how `Progress` is adapted for JSON storage. + +* **Contributions to UG**: + * Added documentation for Introduction section, which gives a brief overview of what to expect from MyLib. + * Added documentation for Getting Started section, which gives a step-by-step guide on how to download and start using MyLib. + * Added documentation for About User Guide section, which informs the reader on the objective of the user guide and how to use it. + * Added documentation for Key Definitions section to explain what a bookmark, genre and a tag is. + * Added documentation for Adding a Bookmark in Commands section. + +* **Contributions to DG**: + * Added a detailed description of the implementation of the `Progress` class and its related classes. + * Added product scope, user stories, use cases, non-functional requirements and glossary. + +* **Contributions to team-based tasks**: + * Arranged and lead the discussion for project meetings. + * Lead the brainstorming and confirmation of features for each iteration. + * Set up most of the Github issues that need to be addressed for each milestone. diff --git a/docs/team/wong-jia-jun.md b/docs/team/wong-jia-jun.md new file mode 100644 index 00000000000..d94a32d0db5 --- /dev/null +++ b/docs/team/wong-jia-jun.md @@ -0,0 +1,45 @@ +--- +layout: page +title: Wong-Jia-Jun's Project Portfolio Page +--- + +### Project: MyLib + +MyLib is a desktop bookmarking application used for keeping track of books a user is reading. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Code Contributed**: [RepoSense hyperlink](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=wong-jia-jun&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17) + +* **New Feature**: Added GoTo command functionality. This involves opening url of bookmark in user's default browser [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files), [#184](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/184) + * What it does: Opens url of a bookmark in user's browser + * Justification: This provides convenience to the user instead of having to copy the url field and pasting after opening a browser + +* **New Feature**: Added the View Command functionality [#176](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/176/files) + * What it does: Allows the user to view specifics of a bookmark more clearly in a separate panel + * Justification: This feature allows users to see more clearly each field of the bookmark as well as longer fields like url which is not present in bookmark on left panel since it may be quite long + +* **Enhancements**: + * Revamp GUI and add on-click support for some commands [#80](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/80), [#99](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/99/files), [#153](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/153/files), [#160](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/160/files) + +* Add Test cases for Code Coverage [#253](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/253/files) + +* **Documentation**: + * User Guide: + * Added documentation for the features `view` and `goto` [#195](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/195/files), [#159](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/159), [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files) + * Added new pictures for UI, UI components, bookmark details and commands `find` and `view` [#256](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/256/files) + * Added Glossary table, GUI components table and Prefix table [#169](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/169/files), [#200](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/200/files) + * Added About User Guide + * Update FAQ [#184](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/184/files) + + * Developer Guide: + * Added implementation details of the `view` and `goto` features. [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files) + * Update delete sequence diagram + * Add diagrams for `goto` feature [#135](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/135/files) + * Update Appendix A Manual Testing [#278](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/278/files) + * Add Intended Audience, About MyLib, Using the Guide portion [#178](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/178/files) + * Update page breaks for pdf conversion [#337](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/337) + +* **Contributions to team-based tasks** + * Created release for V1.3 + * Maintain issue tracker for tasks diff --git a/docs/team/wongyx.md b/docs/team/wongyx.md new file mode 100644 index 00000000000..c44d4fa6c00 --- /dev/null +++ b/docs/team/wongyx.md @@ -0,0 +1,29 @@ +--- +layout: page +title: Wong Yong Xiang's Project Portfolio Page +--- + +### Project: MyLib + +MyLib is a desktop bookmarking application used for keeping track of books a user is reading. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Code Contributed**: [RepoSense hyperlink](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=wongyx&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=wongyx&tabRepo=AY2223S2-CS2103T-T13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **New Feature**: Add the ability for users to find bookmarks using multiple optional fields. + * What it does: Provide users with a more efficient way of searching for bookmarks. + * Justification: This provides convenience for the users. + +* **Enhancement to existing features**: + * Updated Title to only accept up to 180 characters. + * Updated BookmarkContainsPredicate to accept name, genre, tags, and author. + +* Added test cases to increase code coverage. (Pull request [\#276](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/276)) + +* **Documentation**: + * User Guide: + * Updated documentations of feature `find`. (Pull request [\#145](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/145)) + * Updated examples commands for `add` to fix inconsistency between UG and the app itself. (Pull request [\#252](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/252)) + * Developer Guide: + * Added implementation details of `find`. diff --git a/docs/team/zenithyap.md b/docs/team/zenithyap.md new file mode 100644 index 00000000000..c550f8ce24e --- /dev/null +++ b/docs/team/zenithyap.md @@ -0,0 +1,33 @@ +--- +layout: page +title: Zenith's Project Portfolio Page +--- + +### Project: MyLib + +MyLib is a desktop bookmarking application used for keeping track of books a user is reading. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Code Contributed**: [RepoSense hyperlink](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=zenithyap&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=zenithyap&tabRepo=AY2223S2-CS2103T-T13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **New Feature**: Added extra tags functionality. This includes, adding a tag to the tag list, deleting a tag from the tag list and listing all tags from the tag list. + * What it does: Provides the user with a personalized tag list. + * Justification: This provides a more consistent of the usage of tagging, to reduce errors such as typo errors. + * Highlights: This enhancement required the addition of several new functionalities as well as the need to store the tag list in a json file, which was quite challenging. + +* **New Feature**: Added the ability to sort bookmarks by rating. + * What it does: Allows the user to sort the bookmarks by rating in either ascending or descending order. + * Justification: This feature improves the user's ability to manage the bookmarks as the user may have many different bookmarks and the app should provide a way of sorting the bookmarks. + +* **Enhancements to existing features**: + * Changed the naming of the codebase to fit our application (Pull request [\#65](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/65)) + +* Added test cases to increase code coverage [\#270](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/270) [\#280](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/280) + +* **Documentation**: + * User Guide: + * Added documentation for the features `addtag`, `dtag`, and `tags` [\#159](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/159) + * Added documentation for the feature `sort` [\#190](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/190) + * Developer Guide: + * Added implementation details of the `addtag`, `dtag`, and `tags` features. diff --git a/docs/team/zhoyx.md b/docs/team/zhoyx.md new file mode 100644 index 00000000000..82f644ba4e4 --- /dev/null +++ b/docs/team/zhoyx.md @@ -0,0 +1,49 @@ +--- +layout: page +title: Zhou Yuxin's Project Portfolio Page +--- + +### Project: MyLib + +MyLib is a desktop bookmarking application used for keeping track of books a user is reading. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Code Contributed**: [RepoSense hyperlink](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=zhoyx&tabRepo=AY2223S2-CS2103T-T13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **New Feature**: Added new Rating field. [#157](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/157) + * What it does: Allow users add a numeric rating from 0 to 5 to each bookmark. + * Justification: This field can then be used for sorting based on rating to allow users to easily find the best and worst content stored in the library. + +* **New Feature**: Created fixed list of Genres. [#139](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/139) + * What it does: Added validation to only allow Genres from that list to be added. + * Justification: Ensure consistency of Genres and reduce overhead + +* **New Feature**: Added command to list valid Genres. [#139](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/139) + * What it does: Displays the list of valid Genres + * Justification: Allows users to check what are the valid genres available. + +* **Enhancements**: + * Created Progress field when modifying AddressBook to MyLib. [#75](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/75) + * Updated Test cases for Fixed set of Genres. [#155](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/155) + +
+ +* **Documentation**: + * User Guide: + * Added Target Audience [#287](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/287) + * Added Key Definitions Section [#287](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/287) + * Updated Add and Edit Section to include new Genre requirement and Ratings field. [#167](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/167) + * Developer Guide: + * Updated plantUML diagrams to reflect new fields and classes. [#140](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/140) + * Added Implementation for Rating Field. [#167](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/167) + * Added User Stories for Tag, Sort and Delete [#328](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/328) + * Added to Instructions for Manual testing for Tag related commands [#334](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/334) + * Added Implementation for Add Feature. [#335](https://github.com/AY2223S2-CS2103T-T13-4/tp/pull/335) + + +* **Contributions to team-based tasks** + * Created release for V1.3.1.trial and V1.4 + * Maintain issue tracker for tasks + + diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java deleted file mode 100644 index 92cd8fa605a..00000000000 --- a/src/main/java/seedu/address/logic/Logic.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic; - -import java.nio.file.Path; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * API of the Logic component - */ -public interface Logic { - /** - * Executes the command and returns the result. - * @param commandText The command as entered by the user. - * @return the result of the command execution. - * @throws CommandException If an error occurs during command execution. - * @throws ParseException If an error occurs during parsing. - */ - CommandResult execute(String commandText) throws CommandException, ParseException; - - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); - - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Set the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java deleted file mode 100644 index 9c86b1fa6e4..00000000000 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Edits the details of an existing person in the address book. - */ -public class EditCommand extends Command { - - public static final String COMMAND_WORD = "edit"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with - */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - public Optional getPhone() { - return Optional.ofNullable(phone); - } - - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setAddress(Address address) { - this.address = address; - } - - public Optional
getAddress() { - return Optional.ofNullable(address); - } - - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } -} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index d6b19b0a0de..00000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Finds and lists all persons in address book whose name contains any of the argument keywords. - * Keyword matching is case insensitive. - */ -public class FindCommand extends Command { - - public static final String COMMAND_WORD = "find"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; - - private final NameContainsKeywordsPredicate predicate; - - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; - } - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; -import java.util.stream.Stream; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new AddCommand object - */ -public class AddCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public AddCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); - } - - /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. - */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java deleted file mode 100644 index 75b1a9bf119..00000000000 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.address.logic.parser; - -/** - * Contains Command Line Interface (CLI) syntax definitions common to multiple commands - */ -public class CliSyntax { - - /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new EditCommand object - */ -public class EditCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public EditCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); - } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); - - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); - } - - return new EditCommand(index, editPersonDescriptor); - } - - /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 4fb71f23103..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods used for parsing strings in the various *Parser classes. - */ -public class ParserUtil { - - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; - - /** - * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be - * trimmed. - * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). - */ - public static Index parseIndex(String oneBasedIndex) throws ParseException { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * The API of the Model component. - */ -public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - - /** - * Replaces user prefs data with the data in {@code userPrefs}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * @throws NullPointerException if {@code predicate} is null. - */ - void updateFilteredPersonList(Predicate predicate); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 86c1df298d7..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Represents the in-memory model of the address book data. - */ -public class ModelManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index f866e7133de..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,71 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " - + "characters.\n" - + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" - + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" - + " - have each domain label start and end with alphanumeric characters\n" - + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters - private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore - private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" - + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE - + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars - private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java deleted file mode 100644 index c9b5868427c..00000000000 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ /dev/null @@ -1,31 +0,0 @@ -package seedu.address.model.person; - -import java.util.List; -import java.util.function.Predicate; - -import seedu.address.commons.util.StringUtil; - -/** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. - */ -public class NameContainsKeywordsPredicate implements Predicate { - private final List keywords; - - public NameContainsKeywordsPredicate(List keywords) { - this.keywords = keywords; - } - - @Override - public boolean test(Person person) { - return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java deleted file mode 100644 index 872c76b382f..00000000000 --- a/src/main/java/seedu/address/model/person/Phone.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -package seedu.address.storage; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @param filePath location of the data. Cannot be null. - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(Path filePath) throws DataConversionException { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 6cfa0162164..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 7fc927bc5d9..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,77 +0,0 @@ -package seedu.address.ui; - -import java.util.Comparator; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/library/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/library/AppParameters.java index ab552c398f3..512649ca35a 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/library/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.library; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/library/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/library/Main.java index 052a5068631..44cf616dcb4 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/library/Main.java @@ -1,7 +1,8 @@ -package seedu.address; +package seedu.library; import javafx.application.Application; + /** * The main entry point to the application. * diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/library/MainApp.java similarity index 58% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/library/MainApp.java index 4133aaa0151..0f1ee36a224 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/library/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.library; import java.io.IOException; import java.nio.file.Path; @@ -7,36 +7,40 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.library.commons.core.Config; +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.core.Version; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.commons.util.ConfigUtil; +import seedu.library.commons.util.StringUtil; +import seedu.library.logic.Logic; +import seedu.library.logic.LogicManager; +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; +import seedu.library.model.util.SampleDataUtil; +import seedu.library.storage.JsonLibraryStorage; +import seedu.library.storage.JsonTagsStorage; +import seedu.library.storage.JsonUserPrefsStorage; +import seedu.library.storage.LibraryStorage; +import seedu.library.storage.Storage; +import seedu.library.storage.StorageManager; +import seedu.library.storage.TagsStorage; +import seedu.library.storage.UserPrefsStorage; +import seedu.library.ui.Ui; +import seedu.library.ui.UiManager; /** * Runs the application. */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 2, 0, true); + public static final Version VERSION = new Version(1, 4, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +52,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing Library ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +60,9 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + LibraryStorage libraryStorage = new JsonLibraryStorage(userPrefs.getLibraryFilePath()); + TagsStorage tagsStorage = new JsonTagsStorage(userPrefs.getTagsFilePath()); + storage = new StorageManager(libraryStorage, userPrefsStorage, tagsStorage); initLogging(config); @@ -69,28 +74,45 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s library, {@code userPrefs} + * and {@code tagsStorage}.
The data from the sample library will be used instead if {@code storage}'s + * library is not found, or an empty library will be used instead if errors + * occur when reading {@code storage}'s library. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional libraryOptional; + ReadOnlyLibrary initialData; + Optional tagsOptional; + ReadOnlyTags initialTags; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + tagsOptional = storage.readTags(); + if (!tagsOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample Library"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialTags = tagsOptional.orElseGet(SampleDataUtil::getSampleTagList); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty tag list"); + initialTags = new Tags(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty tag list"); + initialTags = new Tags(); } - return new ModelManager(initialData, userPrefs); + try { + libraryOptional = storage.readLibrary(); + if (!libraryOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample Library"); + } + initialData = libraryOptional.orElseGet(SampleDataUtil::getSampleLibrary); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty Library"); + initialData = new Library(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty Library"); + initialData = new Library(); + } + + return new ModelManager(initialData, userPrefs, initialTags); } private void initLogging(Config config) { @@ -151,7 +173,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty Library"); initializedPrefs = new UserPrefs(); } @@ -167,13 +189,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting Library " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Library ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/library/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/library/commons/core/Config.java index 91145745521..9872524b681 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/library/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.library.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/library/commons/core/GuiSettings.java similarity index 93% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/library/commons/core/GuiSettings.java index ba33653be67..b69c2fb3fc0 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/library/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.library.commons.core; import java.awt.Point; import java.io.Serializable; @@ -10,8 +10,8 @@ */ public class GuiSettings implements Serializable { - private static final double DEFAULT_HEIGHT = 600; - private static final double DEFAULT_WIDTH = 740; + private static final double DEFAULT_HEIGHT = 800; + private static final double DEFAULT_WIDTH = 1050; private final double windowWidth; private final double windowHeight; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/library/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/library/commons/core/LogsCenter.java index 431e7185e76..09eba2216aa 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/library/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.library.commons.core; import java.io.IOException; import java.util.Arrays; @@ -18,7 +18,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "Library.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/library/commons/core/Messages.java b/src/main/java/seedu/library/commons/core/Messages.java new file mode 100644 index 00000000000..6ff1537fabf --- /dev/null +++ b/src/main/java/seedu/library/commons/core/Messages.java @@ -0,0 +1,14 @@ +package seedu.library.commons.core; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX = "The bookmark index provided is invalid"; + public static final String MESSAGE_INVALID_TAG_NAME = "The tag name provided does not exists in the tag list"; + public static final String MESSAGE_BOOKMARKS_LISTED_OVERVIEW = "%1$d bookmarks listed!"; + +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/library/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/library/commons/core/Version.java index 12142ec1e32..a85efe519de 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/library/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.library.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/library/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/library/commons/core/index/Index.java index 19536439c09..28474f7dae1 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/library/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.library.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/library/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/library/commons/exceptions/DataConversionException.java index 1f689bd8e3f..e4e0564751d 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/library/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.library.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/library/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/library/commons/exceptions/IllegalValueException.java index 19124db485c..02dd63dd35d 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/library/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.library.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/library/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/library/commons/util/AppUtil.java index 87aa89c0326..48c87508f8c 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/library/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.library.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/library/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/library/commons/util/CollectionUtil.java index eafe4dfd681..db6eecb1eaa 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/library/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/library/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/library/commons/util/ConfigUtil.java index f7f8a2bd44c..d76929ebf60 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/library/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.library.commons.core.Config; +import seedu.library.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/library/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/library/commons/util/FileUtil.java index b1e2767cdd9..0d9170025e9 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/library/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/library/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/library/commons/util/JsonUtil.java index 8ef609f055d..a9790d5c8fe 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/library/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/library/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/library/commons/util/StringUtil.java index 61cc8c9a1cb..e2a150c6a79 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/library/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.library.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/library/logic/Logic.java b/src/main/java/seedu/library/logic/Logic.java new file mode 100644 index 00000000000..eb3537879d4 --- /dev/null +++ b/src/main/java/seedu/library/logic/Logic.java @@ -0,0 +1,57 @@ +package seedu.library.logic; + +import java.nio.file.Path; + +import javafx.collections.ObservableList; +import seedu.library.commons.core.GuiSettings; +import seedu.library.logic.commands.CommandResult; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.bookmark.Bookmark; + +/** + * API of the Logic component + */ +public interface Logic { + /** + * Executes the command and returns the result. + * @param commandText The command as entered by the user. + * @return the result of the command execution. + * @throws CommandException If an error occurs during command execution. + * @throws ParseException If an error occurs during parsing. + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + + /** + * Returns the Library. + * + * @see seedu.library.model.Model#getLibrary() + */ + ReadOnlyLibrary getLibrary(); + + /** Returns an unmodifiable view of the filtered list of bookmarks */ + ObservableList getFilteredBookmarkList(); + + /** Returns selected bookmark */ + Bookmark getSelectedBookmark(); + + /** Returns selected indexx */ + int getSelectedIndex(); + + + /** + * Returns the user prefs' library file path. + */ + Path getLibraryFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); +} diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/library/logic/LogicManager.java similarity index 52% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/library/logic/LogicManager.java index 9d9c6d15bdc..10e5ae37293 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/library/logic/LogicManager.java @@ -1,21 +1,21 @@ -package seedu.address.logic; +package seedu.library.logic; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import seedu.library.commons.core.GuiSettings; +import seedu.library.commons.core.LogsCenter; +import seedu.library.logic.commands.Command; +import seedu.library.logic.commands.CommandResult; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.logic.parser.LibraryParser; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.Model; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.storage.Storage; /** * The main LogicManager of the app. @@ -26,7 +26,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final LibraryParser libraryParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +34,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + libraryParser = new LibraryParser(); } @Override @@ -42,11 +42,12 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = libraryParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveLibrary(model.getLibrary()); + storage.saveTags(model.getTags()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,18 +56,27 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyLibrary getLibrary() { + return model.getLibrary(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredBookmarkList() { + return model.getFilteredBookmarkList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Bookmark getSelectedBookmark() { + return model.getSelectedBookmark(); + } + @Override + public int getSelectedIndex() { + return model.getSelectedIndex(); + } + + @Override + public Path getLibraryFilePath() { + return model.getLibraryFilePath(); } @Override diff --git a/src/main/java/seedu/library/logic/commands/AddCommand.java b/src/main/java/seedu/library/logic/commands/AddCommand.java new file mode 100644 index 00000000000..9543ab5947d --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/AddCommand.java @@ -0,0 +1,91 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; + +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.bookmark.Bookmark; + +/** + * Adds a bookmark to the address book. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a bookmark to the library. " + + "Parameters: " + + PREFIX_TITLE + "TITLE " + + PREFIX_GENRE + "GENRE " + + "[" + PREFIX_AUTHOR + "AUTHOR] " + + "[" + PREFIX_PROGRESS + "PROGRESS] " + + "[" + PREFIX_RATING + "RATING] " + + "[" + PREFIX_URL + "URL] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TITLE + "Hobbit " + + PREFIX_AUTHOR + "J. R. R. Tolkien " + + PREFIX_PROGRESS + "1 1 1 " + + PREFIX_GENRE + "Fantasy " + + PREFIX_RATING + "5 " + + PREFIX_URL + "https://allnovel.net/the-hobbit.html " + + PREFIX_TAG + "Literature Reading " + + PREFIX_TAG + "Holiday reading list"; + + public static final String MESSAGE_SUCCESS = "New bookmark added: %1$s"; + public static final String MESSAGE_DUPLICATE_BOOKMARK = "This bookmark already exists in the library"; + public static final String MESSAGE_ERROR_TAG = "Tag not in tag list."; + + private final Bookmark toAdd; + private final boolean hasTags; + + /** + * Creates an AddCommand to add the specified {@code Bookmark} + */ + public AddCommand(Bookmark bookmark, boolean hasTags) { + requireNonNull(bookmark); + toAdd = bookmark; + this.hasTags = hasTags; + } + + /** + * Creates an AddCommand to add the specified {@code Bookmark} + * with default value false for hasTags + */ + public AddCommand(Bookmark bookmark) { + requireNonNull(bookmark); + this.toAdd = bookmark; + this.hasTags = false; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasBookmark(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_BOOKMARK); + } + + if (hasTags && !model.hasTag(toAdd.getTags())) { + throw new CommandException(MESSAGE_ERROR_TAG); + } + + model.addBookmark(toAdd); + model.updateSelectedIndex(-1); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddCommand // instanceof handles nulls + && toAdd.equals(((AddCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/library/logic/commands/AddTagCommand.java b/src/main/java/seedu/library/logic/commands/AddTagCommand.java new file mode 100644 index 00000000000..0a9ce0e9c4a --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/AddTagCommand.java @@ -0,0 +1,59 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; + +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.tag.Tag; + +/** + * Adds a bookmark to the address book. + */ +public class AddTagCommand extends Command { + + public static final String COMMAND_WORD = "addtag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds tags to list of tags. " + + "Parameters: " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TAG + "Literature Reading " + + PREFIX_TAG + "Holiday reading list"; + + public static final String MESSAGE_SUCCESS = "New tags added: %1$s"; + public static final String MESSAGE_DUPLICATE_TAGS = "This tag already exists in the tag list"; + + private final Set toAdd; + + /** + * Creates an AddTagCommand to add the specified {@code Tag} + */ + public AddTagCommand(Set tagList) { + requireNonNull(tagList); + toAdd = tagList; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasTag(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_TAGS); + } + + model.addTags(toAdd); + model.updateSelectedIndex(-1); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTagCommand // instanceof handles nulls + && toAdd.equals(((AddTagCommand) other).toAdd)); + } +} + diff --git a/src/main/java/seedu/library/logic/commands/ClearCommand.java b/src/main/java/seedu/library/logic/commands/ClearCommand.java new file mode 100644 index 00000000000..42d77c6857e --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/ClearCommand.java @@ -0,0 +1,24 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.library.model.Library; +import seedu.library.model.Model; + +/** + * Clears the library. + */ +public class ClearCommand extends Command { + + public static final String COMMAND_WORD = "clear"; + public static final String MESSAGE_SUCCESS = "Library has been cleared!"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.setLibrary(new Library()); + model.updateSelectedIndex(-1); + return new CommandResult(MESSAGE_SUCCESS, false, false, true); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/library/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/library/logic/commands/Command.java index 64f18992160..6f13418800c 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/library/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/library/logic/commands/CommandResult.java similarity index 84% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/library/logic/commands/CommandResult.java index 92f900b7916..0b89fe64f44 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/library/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; import static java.util.Objects.requireNonNull; @@ -17,13 +17,17 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + /** Update the display on rightside of UI */ + private final boolean update; + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean update) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.update = update; } /** @@ -31,7 +35,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false); } public String getFeedbackToUser() { @@ -45,6 +49,9 @@ public boolean isShowHelp() { public boolean isExit() { return exit; } + public boolean isUpdate() { + return update; + } @Override public boolean equals(Object other) { diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/library/logic/commands/DeleteCommand.java similarity index 52% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/seedu/library/logic/commands/DeleteCommand.java index 02fd256acba..7397e37b984 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/library/logic/commands/DeleteCommand.java @@ -1,28 +1,28 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; import static java.util.Objects.requireNonNull; import java.util.List; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.bookmark.Bookmark; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a bookmark identified using it's displayed index from the library. */ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" + + ": Deletes the bookmark identified by the index number used in the displayed bookmark list.\n" + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_BOOKMARK_SUCCESS = "Deleted Bookmark: %1$s"; private final Index targetIndex; @@ -33,15 +33,16 @@ public DeleteCommand(Index targetIndex) { @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getFilteredBookmarkList(); if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); } - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + Bookmark bookmarkToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteBookmark(bookmarkToDelete); + model.updateSelectedIndex(-1); + return new CommandResult(String.format(MESSAGE_DELETE_BOOKMARK_SUCCESS, bookmarkToDelete), false, false, true); } @Override diff --git a/src/main/java/seedu/library/logic/commands/DeleteTagCommand.java b/src/main/java/seedu/library/logic/commands/DeleteTagCommand.java new file mode 100644 index 00000000000..4c885b3f5fc --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/DeleteTagCommand.java @@ -0,0 +1,54 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.library.commons.core.Messages; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.tag.Tag; + +/** + * Deletes a tag identified by its name from the tag list. + */ +public class DeleteTagCommand extends Command { + + public static final String COMMAND_WORD = "dtag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the tag identified by the name used in displayed tag list.\n" + + "Parameters: Tag name\n" + + "Example: " + COMMAND_WORD + " MaleProtagonist"; + + public static final String MESSAGE_DELETE_TAG_SUCCESS = "Deleted Tag: %1$s"; + public static final String PREVENT_USED_TAG_DELETION = "Cannot delete tags that are currently used by bookmarks"; + + private final Tag tag; + + public DeleteTagCommand(Tag tag) { + this.tag = tag; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.tagInUse(tag)) { + throw new CommandException(PREVENT_USED_TAG_DELETION); + } + + if (!model.hasTag(tag)) { + throw new CommandException(Messages.MESSAGE_INVALID_TAG_NAME); + } + + model.deleteTag(tag); + model.updateSelectedIndex(-1); + return new CommandResult(String.format(MESSAGE_DELETE_TAG_SUCCESS, tag), false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteTagCommand // instanceof handles nulls + && tag.equals(((DeleteTagCommand) other).tag)); // state check + } +} diff --git a/src/main/java/seedu/library/logic/commands/EditCommand.java b/src/main/java/seedu/library/logic/commands/EditCommand.java new file mode 100644 index 00000000000..6f6640db8a7 --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/EditCommand.java @@ -0,0 +1,264 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; +import static seedu.library.model.Model.PREDICATE_SHOW_ALL_BOOKMARKS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.commons.util.CollectionUtil; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; + +/** + * Edits the details of an existing bookmark in the library. + */ +public class EditCommand extends Command { + + public static final String COMMAND_WORD = "edit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the bookmark identified " + + "by the index number used in the displayed bookmark list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_TITLE + "TITLE] " + + "[" + PREFIX_AUTHOR + "AUTHOR] " + + "[" + PREFIX_PROGRESS + "PROGRESS] " + + "[" + PREFIX_GENRE + "GENRE] " + + "[" + PREFIX_RATING + "RATING] " + + "[" + PREFIX_URL + "URL] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PROGRESS + "Finished " + + PREFIX_GENRE + "Fantasy"; + + public static final String MESSAGE_EDIT_BOOKMARK_SUCCESS = "Edited Bookmark: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_BOOKMARK = "This bookmark already exists in the library."; + public static final String MESSAGE_ERROR_TAG = "Tag not in tag list."; + + private final Index index; + private final EditBookmarkDescriptor editBookmarkDescriptor; + + /** + * @param index of the bookmark in the filtered bookmark list to edit + * @param editBookmarkDescriptor details to edit the bookmark with + */ + public EditCommand(Index index, EditBookmarkDescriptor editBookmarkDescriptor) { + requireNonNull(index); + requireNonNull(editBookmarkDescriptor); + + this.index = index; + this.editBookmarkDescriptor = new EditBookmarkDescriptor(editBookmarkDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredBookmarkList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + Bookmark bookmarkToEdit = lastShownList.get(index.getZeroBased()); + Bookmark editedBookmark = createEditedBookmark(bookmarkToEdit, editBookmarkDescriptor); + + if (!bookmarkToEdit.isSameBookmark(editedBookmark) && model.hasBookmark(editedBookmark)) { + throw new CommandException(MESSAGE_DUPLICATE_BOOKMARK); + } + + if (!model.hasTag(editedBookmark.getTags())) { + throw new CommandException(MESSAGE_ERROR_TAG); + } + + model.setBookmark(bookmarkToEdit, editedBookmark); + model.updateFilteredBookmarkList(PREDICATE_SHOW_ALL_BOOKMARKS); + model.updateSelectedIndex(-1); + return new CommandResult(String.format(MESSAGE_EDIT_BOOKMARK_SUCCESS, editedBookmark), false, false, true); + } + + /** + * Creates and returns a {@code Bookmark} with the details of {@code bookmarkToEdit} + * edited with {@code editBookmarkDescriptor}. + */ + private static Bookmark createEditedBookmark(Bookmark bookmarkToEdit, + EditBookmarkDescriptor editBookmarkDescriptor) { + assert bookmarkToEdit != null; + + Title updatedTitle = editBookmarkDescriptor.getTitle().orElse(bookmarkToEdit.getTitle()); + Progress updatedProgress = editBookmarkDescriptor.getProgress().orElse(bookmarkToEdit.getProgress()); + Genre updatedGenre = editBookmarkDescriptor.getGenre().orElse(bookmarkToEdit.getGenre()); + Author updatedAuthor = editBookmarkDescriptor.getAuthor().orElse(bookmarkToEdit.getAuthor()); + Rating updatedRating = editBookmarkDescriptor.getRating().orElse(bookmarkToEdit.getRating()); + Url updatedUrl = editBookmarkDescriptor.getUrl().orElse(bookmarkToEdit.getUrl()); + Set updatedTags = editBookmarkDescriptor.getTags().orElse(bookmarkToEdit.getTags()); + + return new Bookmark(updatedTitle, updatedProgress, updatedGenre, + updatedAuthor, updatedRating, updatedUrl, updatedTags); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditCommand)) { + return false; + } + + // state check + EditCommand e = (EditCommand) other; + return index.equals(e.index) + && editBookmarkDescriptor.equals(e.editBookmarkDescriptor); + } + + /** + * Stores the details to edit the bookmark with. Each non-empty field value will replace the + * corresponding field value of the bookmark. + */ + public static class EditBookmarkDescriptor { + private Title title; + private Progress progress; + private Genre genre; + private Author author; + private Rating rating; + private Set tags; + private Url url; + + public EditBookmarkDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditBookmarkDescriptor(EditBookmarkDescriptor toCopy) { + setTitle(toCopy.title); + setProgress(toCopy.progress); + setGenre(toCopy.genre); + setAuthor(toCopy.author); + setRating(toCopy.rating); + setTags(toCopy.tags); + setUrl(toCopy.url); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(title, progress, genre, author, rating, url, tags); + } + + public void setTitle(Title title) { + this.title = title; + } + + public Optional getTitle() { + return Optional.ofNullable(title); + } + + public void setProgress(Progress progress) { + this.progress = progress; + } + + public Optional<Progress> getProgress() { + return Optional.ofNullable(progress); + } + + public void setGenre(Genre genre) { + this.genre = genre; + } + + public Optional<Genre> getGenre() { + return Optional.ofNullable(genre); + } + + public void setAuthor(Author author) { + this.author = author; + } + + public Optional<Author> getAuthor() { + return Optional.ofNullable(author); + } + + public void setUrl(Url url) { + this.url = url; + } + + public Optional<Url> getUrl() { + return Optional.ofNullable(url); + } + + public void setRating(Rating rating) { + this.rating = rating; + } + + public Optional<Rating> getRating() { + return Optional.ofNullable(rating); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set<Tag> tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + /** + * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code tags} is null. + */ + public Optional<Set<Tag>> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditBookmarkDescriptor)) { + return false; + } + + // state check + EditBookmarkDescriptor e = (EditBookmarkDescriptor) other; + + return getTitle().equals(e.getTitle()) + && getProgress().equals(e.getProgress()) + && getGenre().equals(e.getGenre()) + && getAuthor().equals(e.getAuthor()) + && getTags().equals(e.getTags()) + && getUrl().equals(e.getUrl()) + && getRating().equals(e.getRating()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/library/logic/commands/ExitCommand.java similarity index 72% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/library/logic/commands/ExitCommand.java index 3dd85a8ba90..348364a124b 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/library/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; -import seedu.address.model.Model; +import seedu.library.model.Model; /** * Terminates the program. @@ -9,11 +9,11 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Library as requested ..."; @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); } } diff --git a/src/main/java/seedu/library/logic/commands/FindCommand.java b/src/main/java/seedu/library/logic/commands/FindCommand.java new file mode 100644 index 00000000000..7609fce5a5f --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/FindCommand.java @@ -0,0 +1,56 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; + +import seedu.library.commons.core.Messages; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; +import seedu.library.model.tag.Tag; + +/** + * Finds and lists all bookmarks in library whose title contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindCommand extends Command { + + public static final String COMMAND_WORD = "find"; + + public static final String MESSAGE_ERROR_TAG = "Tag not in tag list."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all bookmarks whose information flagged by the prefixes contain any of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: PREFIX KEYWORD [MORE_PREFIX] [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_TITLE + " Ranker's Chainsaw Solo " + PREFIX_GENRE + " Action"; + + private final BookmarkContainsKeywordsPredicate predicate; + + public FindCommand(BookmarkContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + assert(predicate != null); + if (predicate.getTagKeywords() != null + && predicate.getTagKeywords().stream().anyMatch(tagName -> !model.hasTag(new Tag(tagName)))) { + throw new CommandException(MESSAGE_ERROR_TAG); + } + model.updateFilteredBookmarkList(predicate); + model.updateSelectedIndex(-1); + return new CommandResult( + String.format(Messages.MESSAGE_BOOKMARKS_LISTED_OVERVIEW, + model.getFilteredBookmarkList().size()), false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindCommand // instanceof handles nulls + && predicate.equals(((FindCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/library/logic/commands/GenreCommand.java b/src/main/java/seedu/library/logic/commands/GenreCommand.java new file mode 100644 index 00000000000..ca6c2ca3658 --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/GenreCommand.java @@ -0,0 +1,24 @@ +package seedu.library.logic.commands; + +import static seedu.library.model.bookmark.Genre.getValidGenres; + +import java.util.List; + +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; + +/** + * Lists all valid genres to the user + */ +public class GenreCommand extends Command { + + public static final String COMMAND_WORD = "genre"; + + @Override + public CommandResult execute(Model model) throws CommandException { + List<String> validGenres = getValidGenres(); + String genresString = String.join(", ", validGenres); + model.updateSelectedIndex(-1); + return new CommandResult("Valid genres: " + genresString); + } +} diff --git a/src/main/java/seedu/library/logic/commands/GoToCommand.java b/src/main/java/seedu/library/logic/commands/GoToCommand.java new file mode 100644 index 00000000000..c2491b0e656 --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/GoToCommand.java @@ -0,0 +1,99 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.util.List; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.bookmark.Bookmark; + + + +/** + * open up a browser and goes to specified index bookmark's url if present. + */ +public class GoToCommand extends Command { + public static final String COMMAND_WORD = "goto"; + public static final String URI_OPS_ERROR = "could not go to url"; + public static final String NOT_SUPPORTED = "this function is not supported on your OS "; + public static final String EMPTY_URL_ERROR = "url field is not present cannot goto site"; + + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": opens up specified index bookmark's url if present\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + public static final String MESSAGE_GOTO_BOOKMARK_SUCCESS = "Goto url of Bookmark: %1$s"; + private final Index targetIndex; + public GoToCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List<Bookmark> lastShownList = model.getFilteredBookmarkList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + Bookmark bookmarkToGoto = lastShownList.get(targetIndex.getZeroBased()); + String url = bookmarkToGoto.getUrl().toString(); + try { + if (url.isEmpty()) { + throw new CommandException(EMPTY_URL_ERROR); + } + openUrl(url); + } catch (IOException ioe) { + throw new CommandException(URI_OPS_ERROR + ioe, ioe); + + } + model.updateSelectedIndex(-1); + return new CommandResult(String.format(MESSAGE_GOTO_BOOKMARK_SUCCESS, bookmarkToGoto), false, false, false); + } + + /** + * Opens url in users default browser + * @param url + * @throws IOException + */ + public static void openUrl(String url) throws IOException, CommandException { + URI targetUrl = URI.create(url); + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win") || os.contains("mac")) { + Desktop.getDesktop().browse(targetUrl); + } else { //Desktop package not supported in this system + if (os.contains("nix") || os.contains("nux")) { + Runtime runtime = Runtime.getRuntime(); + runtime.exec(String.format("xdg-open %s", url)); + } else { + throw new CommandException(NOT_SUPPORTED); + } + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof GoToCommand)) { + return false; + } + + // state check + GoToCommand e = (GoToCommand) other; + return other == this + || (other instanceof GoToCommand + && targetIndex.equals(e.targetIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/library/logic/commands/HelpCommand.java similarity index 81% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/library/logic/commands/HelpCommand.java index bf824f91bd0..7085e189d8d 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/library/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; -import seedu.address.model.Model; +import seedu.library.model.Model; /** * Format full help instructions for every command for display. @@ -16,6 +16,7 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + model.updateSelectedIndex(-1); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); } } diff --git a/src/main/java/seedu/library/logic/commands/ListCommand.java b/src/main/java/seedu/library/logic/commands/ListCommand.java new file mode 100644 index 00000000000..57426c399fa --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/ListCommand.java @@ -0,0 +1,25 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.library.model.Model.PREDICATE_SHOW_ALL_BOOKMARKS; + +import seedu.library.model.Model; + +/** + * Lists all bookmarks in the library to the user. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_SUCCESS = "Listed all bookmarks"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredBookmarkList(PREDICATE_SHOW_ALL_BOOKMARKS); + model.updateSelectedIndex(-1); + return new CommandResult(MESSAGE_SUCCESS, false, false, true); + } +} diff --git a/src/main/java/seedu/library/logic/commands/ListTagsCommand.java b/src/main/java/seedu/library/logic/commands/ListTagsCommand.java new file mode 100644 index 00000000000..c97af1a0097 --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/ListTagsCommand.java @@ -0,0 +1,25 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.library.model.Model; + +/** + * Lists all tags in the tag list to the user. + */ +public class ListTagsCommand extends Command { + + public static final String COMMAND_WORD = "tags"; + + public static final String MESSAGE_SUCCESS = "Listed all tags: "; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + String test = model.tagListToString(); + model.updateSelectedIndex(-1); + return new CommandResult(MESSAGE_SUCCESS + test); + } +} + diff --git a/src/main/java/seedu/library/logic/commands/SortCommand.java b/src/main/java/seedu/library/logic/commands/SortCommand.java new file mode 100644 index 00000000000..924d9d0177d --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/SortCommand.java @@ -0,0 +1,51 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.library.model.Model; +import seedu.library.model.RatingOrder; + +/** + * Sorts by rating and lists all bookmarks in the library to the user. + */ +public class SortCommand extends Command { + + public static final String COMMAND_WORD = "sort"; + + public static final String MESSAGE_SUCCESS_DESC = "Listed all bookmarks in descending order of rating"; + + public static final String MESSAGE_SUCCESS_ASC = "Listed all bookmarks in ascending order of rating"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Sorts bookmarks by ratings in either ascending or descending order.\n" + + "Parameters: ORDER (either 'asc' or 'desc')\n" + + "Example: " + COMMAND_WORD + " asc"; + + public final String order; + + /** + * Creates an SortCommand to add the specified {@code String} + */ + public SortCommand(String order) { + this.order = order; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateSortedBookmarkList(order); + model.updateSelectedIndex(-1); + if (order.equals(RatingOrder.DESC)) { + return new CommandResult(MESSAGE_SUCCESS_DESC); + } else { + return new CommandResult(MESSAGE_SUCCESS_ASC); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortCommand // instanceof handles nulls + && order.equals(((SortCommand) other).order)); // state check + } +} diff --git a/src/main/java/seedu/library/logic/commands/ViewCommand.java b/src/main/java/seedu/library/logic/commands/ViewCommand.java new file mode 100644 index 00000000000..a58eee0d336 --- /dev/null +++ b/src/main/java/seedu/library/logic/commands/ViewCommand.java @@ -0,0 +1,52 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Model; +import seedu.library.model.bookmark.Bookmark; + +/** + * Views a bookmark identified using it's displayed index from the library. + */ +public class ViewCommand extends Command { + + public static final String COMMAND_WORD = "view"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Views the bookmark identified by the index number used in the displayed bookmark list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_VIEW_VIEWED_SUCCESS = "Viewed Bookmark: %1$s"; + + private final Index targetIndex; + + public ViewCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List<Bookmark> lastShownList = model.getFilteredBookmarkList(); + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + Bookmark bookmarkToView = lastShownList.get(targetIndex.getZeroBased()); + model.updateSelectedBookmark(bookmarkToView); + model.updateSelectedIndex(targetIndex.getZeroBased()); + return new CommandResult(String.format(MESSAGE_VIEW_VIEWED_SUCCESS, bookmarkToView), false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ViewCommand // instanceof handles nulls + && targetIndex.equals(((ViewCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/library/logic/commands/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/library/logic/commands/exceptions/CommandException.java index a16bd14f2cd..b2a1597d183 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/library/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package seedu.library.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/library/logic/parser/AddCommandParser.java b/src/main/java/seedu/library/logic/parser/AddCommandParser.java new file mode 100644 index 00000000000..4ab68865163 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/AddCommandParser.java @@ -0,0 +1,75 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.library.logic.commands.AddCommand; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class AddCommandParser implements Parser<AddCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_AUTHOR, + PREFIX_PROGRESS, PREFIX_GENRE, PREFIX_RATING, PREFIX_URL, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, PREFIX_TITLE, PREFIX_GENRE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + Title title = ParserUtil.parseTitle(argMultimap.getValue(PREFIX_TITLE).get()); + Progress progress = argMultimap.getValue(PREFIX_PROGRESS).isPresent() + ? ParserUtil.parseProgress(argMultimap.getValue(PREFIX_PROGRESS).get()) + : null; + Genre genre = ParserUtil.parseGenre(argMultimap.getValue(PREFIX_GENRE).get()); + Author author = argMultimap.getValue(PREFIX_AUTHOR).isPresent() + ? ParserUtil.parseAuthor(argMultimap.getValue(PREFIX_AUTHOR).get()) + : null; + Rating rating = argMultimap.getValue(PREFIX_RATING).isPresent() + ? ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).get()) + : Rating.DEFAULT_RATING; + Url url = argMultimap.getValue(PREFIX_URL).isPresent() + ? ParserUtil.parseUrl(argMultimap.getValue(PREFIX_URL).get()) + : new Url(""); + Set<Tag> tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + Bookmark bookmark = new Bookmark(title, progress, genre, author, rating, url, tagList); + boolean hasTags = arePrefixesPresent(argMultimap, PREFIX_TAG); + return new AddCommand(bookmark, hasTags); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/library/logic/parser/AddTagCommandParser.java b/src/main/java/seedu/library/logic/parser/AddTagCommandParser.java new file mode 100644 index 00000000000..cd6b59a9163 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/AddTagCommandParser.java @@ -0,0 +1,42 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.library.logic.commands.AddTagCommand; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddTagCommand object + */ +public class AddTagCommandParser implements Parser<AddTagCommand> { + /** + * Parses the given {@code String} of arguments in the context of the AddTagCommand + * and returns an AddTagCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddTagCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, PREFIX_TAG) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE)); + } + + Set<Tag> tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + return new AddTagCommand(tagList); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/library/logic/parser/ArgumentMultimap.java similarity index 98% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/library/logic/parser/ArgumentMultimap.java index 954c8e18f8e..fba6072c902 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/library/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/library/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/library/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..f91c4c989e2 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/library/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/library/logic/parser/CliSyntax.java b/src/main/java/seedu/library/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..013f94d6434 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/CliSyntax.java @@ -0,0 +1,17 @@ +package seedu.library.logic.parser; + +/** + * Contains Command Line Interface (CLI) syntax definitions common to multiple commands + */ +public class CliSyntax { + + /* Prefix definitions */ + public static final Prefix PREFIX_TITLE = new Prefix("n/"); + public static final Prefix PREFIX_AUTHOR = new Prefix("a/"); + public static final Prefix PREFIX_PROGRESS = new Prefix("p/"); + public static final Prefix PREFIX_GENRE = new Prefix("g/"); + public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_URL = new Prefix("u/"); + public static final Prefix PREFIX_RATING = new Prefix("r/"); + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/library/logic/parser/DeleteCommandParser.java similarity index 75% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/seedu/library/logic/parser/DeleteCommandParser.java index 522b93081cc..24518a0a8d7 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/library/logic/parser/DeleteCommandParser.java @@ -1,10 +1,10 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.DeleteCommand; +import seedu.library.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object diff --git a/src/main/java/seedu/library/logic/parser/DeleteTagCommandParser.java b/src/main/java/seedu/library/logic/parser/DeleteTagCommandParser.java new file mode 100644 index 00000000000..eaac106b325 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/DeleteTagCommandParser.java @@ -0,0 +1,29 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.library.logic.commands.DeleteTagCommand; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.tag.Tag; + +/** + * Parses input arguments and creates a new DeleteTagCommand object + */ +public class DeleteTagCommandParser implements Parser<DeleteTagCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteCommand + * and returns a DeleteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteTagCommand parse(String args) throws ParseException { + try { + Tag tag = ParserUtil.parseTag(args); + return new DeleteTagCommand(tag); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/library/logic/parser/EditCommandParser.java b/src/main/java/seedu/library/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..f00d9e51cc4 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/EditCommandParser.java @@ -0,0 +1,91 @@ +package seedu.library.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.EditCommand; +import seedu.library.logic.commands.EditCommand.EditBookmarkDescriptor; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditCommandParser implements Parser<EditCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the EditCommand + * and returns an EditCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TITLE, + PREFIX_PROGRESS, PREFIX_GENRE, PREFIX_AUTHOR, PREFIX_RATING, PREFIX_URL, PREFIX_TAG); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + } + + EditBookmarkDescriptor editBookmarkDescriptor = new EditCommand.EditBookmarkDescriptor(); + if (argMultimap.getValue(PREFIX_TITLE).isPresent()) { + editBookmarkDescriptor.setTitle(ParserUtil.parseTitle(argMultimap.getValue(PREFIX_TITLE).get())); + } + if (argMultimap.getValue(PREFIX_PROGRESS).isPresent()) { + editBookmarkDescriptor.setProgress(ParserUtil.parseProgress(argMultimap.getValue(PREFIX_PROGRESS).get())); + } + if (argMultimap.getValue(PREFIX_GENRE).isPresent()) { + editBookmarkDescriptor.setGenre(ParserUtil.parseGenre(argMultimap.getValue(PREFIX_GENRE).get())); + } + if (argMultimap.getValue(PREFIX_AUTHOR).isPresent()) { + editBookmarkDescriptor.setAuthor(ParserUtil.parseAuthor(argMultimap.getValue(PREFIX_AUTHOR).get())); + } + if (argMultimap.getValue(PREFIX_RATING).isPresent()) { + editBookmarkDescriptor.setRating(ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).get())); + } + if (argMultimap.getValue(PREFIX_URL).isPresent()) { + editBookmarkDescriptor.setUrl((ParserUtil.parseUrl(argMultimap.getValue(PREFIX_URL).get()))); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editBookmarkDescriptor::setTags); + + if (!editBookmarkDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + return new EditCommand(index, editBookmarkDescriptor); + } + + /** + * Parses {@code Collection<String> tags} into a {@code Set<Tag>} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set<Tag>} containing zero tags. + */ + private Optional<Set<Tag>> parseTagsForEdit(Collection<String> tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection<String> tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } + +} diff --git a/src/main/java/seedu/library/logic/parser/FindCommandParser.java b/src/main/java/seedu/library/logic/parser/FindCommandParser.java new file mode 100644 index 00000000000..2c6a02c16c0 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/FindCommandParser.java @@ -0,0 +1,117 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import seedu.library.logic.commands.FindCommand; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.tag.Tag; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindCommandParser implements Parser<FindCommand> { + + public static final String MESSAGE_EMPTY_KEYWORD = "Keyword field cannot be empty"; + public static final String MESSAGE_INVALID_GENRE = "Genre provided does not exist"; + public static final String MESSAGE_INVALID_TAG = "Tag provided does not exist"; + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns a FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_AUTHOR, PREFIX_GENRE, PREFIX_TAG); + if (!areAnyPrefixesPresent(argMultimap, PREFIX_TITLE, PREFIX_TAG, PREFIX_GENRE, PREFIX_AUTHOR) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + + List<String> titleKeywords = null; + List<String> genreKeywords = null; + List<String> tagKeywords = null; + List<String> authorKeywords = null; + + if (isPrefixPresent(argMultimap, PREFIX_TITLE)) { + titleKeywords = parseTitle(argMultimap); + } + + if (isPrefixPresent(argMultimap, PREFIX_GENRE)) { + genreKeywords = parseGenre(argMultimap); + } + + if (isPrefixPresent(argMultimap, PREFIX_TAG)) { + tagKeywords = parseTag(argMultimap); + } + + if (isPrefixPresent(argMultimap, PREFIX_AUTHOR)) { + authorKeywords = parseAuthor(argMultimap); + } + + + return new FindCommand(new BookmarkContainsKeywordsPredicate( + titleKeywords, genreKeywords, tagKeywords, authorKeywords)); + } + + private static boolean areAnyPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + private static boolean isPrefixPresent(ArgumentMultimap argumentMultimap, Prefix prefix) { + return argumentMultimap.getValue(prefix).isPresent(); + } + + private static List<String> parseTitle(ArgumentMultimap argMultimap) throws ParseException { + if (argMultimap.getValue(PREFIX_TITLE).get().equals("")) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_EMPTY_KEYWORD)); + } + return Arrays.asList( + argMultimap.getValue(PREFIX_TITLE).get().trim().split("\\s+")); + } + + private static List<String> parseGenre(ArgumentMultimap argMultimap) throws ParseException { + if (argMultimap.getValue(PREFIX_GENRE).get().equals("")) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_EMPTY_KEYWORD)); + } + List<String> genreKeywords = Arrays.asList( + argMultimap.getValue(PREFIX_GENRE).get().trim().split("\\s+")); + if (genreKeywords.stream().anyMatch(keyword -> !Genre.isValidGenre(keyword))) { + throw new ParseException(MESSAGE_INVALID_GENRE); + } + return genreKeywords; + } + + private static List<String> parseTag(ArgumentMultimap argMultimap) throws ParseException { + if (argMultimap.getValue(PREFIX_TAG).get().equals("")) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_EMPTY_KEYWORD)); + } + List<String> tagKeywords = Arrays.asList( + argMultimap.getValue(PREFIX_TAG).get().trim().split("\\s+")); + if (tagKeywords.stream().anyMatch(keyword -> !Tag.isValidTagName(keyword))) { + throw new ParseException(MESSAGE_INVALID_TAG); + } + return tagKeywords; + } + + private static List<String> parseAuthor(ArgumentMultimap argMultimap) throws ParseException { + if (argMultimap.getValue(PREFIX_AUTHOR).get().equals("")) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_EMPTY_KEYWORD)); + } + List<String> authorKeywords = Arrays.asList( + argMultimap.getValue(PREFIX_AUTHOR).get().trim().split("\\s+")); + return authorKeywords; + } + +} diff --git a/src/main/java/seedu/library/logic/parser/GoToCommandParser.java b/src/main/java/seedu/library/logic/parser/GoToCommandParser.java new file mode 100644 index 00000000000..32748a79aa9 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/GoToCommandParser.java @@ -0,0 +1,32 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.GoToCommand; +import seedu.library.logic.parser.exceptions.ParseException; + + + +/** + * Parses input arguments and creates a new GoToCommand object + */ +public class GoToCommandParser implements Parser<GoToCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the GoToCommand + * and returns a GoToCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public GoToCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new GoToCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, GoToCommand.MESSAGE_USAGE), pe); + } + } + +} + diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/library/logic/parser/LibraryParser.java similarity index 51% rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java rename to src/main/java/seedu/library/logic/parser/LibraryParser.java index 1e466792b46..90c9d492b13 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/library/logic/parser/LibraryParser.java @@ -1,26 +1,33 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; import java.util.regex.Matcher; import java.util.regex.Pattern; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.library.logic.commands.AddCommand; +import seedu.library.logic.commands.AddTagCommand; +import seedu.library.logic.commands.ClearCommand; +import seedu.library.logic.commands.Command; +import seedu.library.logic.commands.DeleteCommand; +import seedu.library.logic.commands.DeleteTagCommand; +import seedu.library.logic.commands.EditCommand; +import seedu.library.logic.commands.ExitCommand; +import seedu.library.logic.commands.FindCommand; +import seedu.library.logic.commands.GenreCommand; +import seedu.library.logic.commands.GoToCommand; +import seedu.library.logic.commands.HelpCommand; +import seedu.library.logic.commands.ListCommand; +import seedu.library.logic.commands.ListTagsCommand; +import seedu.library.logic.commands.SortCommand; +import seedu.library.logic.commands.ViewCommand; +import seedu.library.logic.parser.exceptions.ParseException; /** * Parses user input. */ -public class AddressBookParser { +public class LibraryParser { /** * Used for initial separation of command word and args. @@ -47,12 +54,21 @@ public Command parseCommand(String userInput) throws ParseException { case AddCommand.COMMAND_WORD: return new AddCommandParser().parse(arguments); + case AddTagCommand.COMMAND_WORD: + return new AddTagCommandParser().parse(arguments); + case EditCommand.COMMAND_WORD: return new EditCommandParser().parse(arguments); case DeleteCommand.COMMAND_WORD: return new DeleteCommandParser().parse(arguments); + case GoToCommand.COMMAND_WORD: + return new GoToCommandParser().parse(arguments); + + case DeleteTagCommand.COMMAND_WORD: + return new DeleteTagCommandParser().parse(arguments); + case ClearCommand.COMMAND_WORD: return new ClearCommand(); @@ -62,12 +78,24 @@ public Command parseCommand(String userInput) throws ParseException { case ListCommand.COMMAND_WORD: return new ListCommand(); + case ListTagsCommand.COMMAND_WORD: + return new ListTagsCommand(); + + case SortCommand.COMMAND_WORD: + return new SortCommandParser().parse(arguments); + + case ViewCommand.COMMAND_WORD: + return new ViewCommandParser().parse(arguments); + case ExitCommand.COMMAND_WORD: return new ExitCommand(); case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case GenreCommand.COMMAND_WORD: + return new GenreCommand(); + default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/library/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/library/logic/parser/Parser.java index d6551ad8e3f..79028f442a4 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/library/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.library.logic.commands.Command; +import seedu.library.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/library/logic/parser/ParserUtil.java b/src/main/java/seedu/library/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..1dd5f413f69 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/ParserUtil.java @@ -0,0 +1,172 @@ +package seedu.library.logic.parser; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import seedu.library.commons.core.index.Index; +import seedu.library.commons.util.StringUtil; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.RatingOrder; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; + +/** + * Contains utility methods used for parsing strings in the various *Parser classes. + */ +public class ParserUtil { + + public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + + /** + * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be + * trimmed. + * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). + */ + public static Index parseIndex(String oneBasedIndex) throws ParseException { + String trimmedIndex = oneBasedIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + + /** + * Parses a {@code String title} into a {@code Title}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code title} is invalid. + */ + public static Title parseTitle(String title) throws ParseException { + requireNonNull(title); + String trimmedTitle = title.trim(); + if (!Title.isValidTitle(trimmedTitle)) { + throw new ParseException(Title.MESSAGE_CONSTRAINTS); + } + return new Title(trimmedTitle); + } + + /** + * Parses a {@code String progress} into a {@code Progress}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code progress} is invalid. + */ + public static Progress parseProgress(String progress) throws ParseException { + requireNonNull(progress); + String trimmedProgress = progress.trim(); + String[] splitProgress = trimmedProgress.split(" "); + if (!Progress.isValidProgress(splitProgress)) { + throw new ParseException(Progress.MESSAGE_CONSTRAINTS); + } + return new Progress(splitProgress); + } + + /** + * Parses a {@code String author} into an {@code Author}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code author} is invalid. + */ + public static Author parseAuthor(String author) throws ParseException { + requireNonNull(author); + String trimmedAuthor = author.trim(); + if (!Author.isValidAuthor(trimmedAuthor)) { + throw new ParseException(Author.MESSAGE_CONSTRAINTS); + } + return new Author(trimmedAuthor); + } + + /** + * Parses a {@code String genre} into an {@code Genre}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code genre} is invalid. + */ + public static Genre parseGenre(String genre) throws ParseException { + requireNonNull(genre); + String trimmedGenre = genre.trim(); + if (!Genre.isValidGenre(trimmedGenre)) { + throw new ParseException(Genre.MESSAGE_CONSTRAINTS); + } + return new Genre(trimmedGenre); + } + /** + * Parses a {@code String url} into an {@code Url}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code url} is invalid. + */ + public static Url parseUrl(String url) throws ParseException { + requireNonNull(url); + String trimmedUrl = url.trim(); + if (!Url.isValidUrlLink(trimmedUrl)) { + throw new ParseException(Url.MESSAGE_CONSTRAINTS); + } + return new Url(trimmedUrl); + } + + /** + * Parses a {@code String rating} into an {@code Rating}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code rating} is invalid. + */ + public static Rating parseRating(String rating) throws ParseException { + requireNonNull(rating); + String trimmedRating = rating.trim(); + if (!Rating.isValidRating(trimmedRating)) { + throw new ParseException(Rating.MESSAGE_CONSTRAINTS); + } + return new Rating(trimmedRating); + } + + /** + * Parses a {@code String sort} into an {@code String}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code String} is invalid. + */ + public static String parseSort(String sort) throws ParseException { + requireNonNull(sort); + String trimmedSort = sort.trim(); + if (!RatingOrder.isValidRatingOrder(trimmedSort)) { + throw new ParseException(RatingOrder.MESSAGE_CONSTRAINT); + } + return trimmedSort; + } + + /** + * Parses a {@code String tag} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code tag} is invalid. + */ + public static Tag parseTag(String tag) throws ParseException { + requireNonNull(tag); + String trimmedTag = tag.trim(); + if (!Tag.isValidTagName(trimmedTag)) { + throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + } + return new Tag(trimmedTag); + } + + /** + * Parses {@code Collection<String> tags} into a {@code Set<Tag>}. + */ + public static Set<Tag> parseTags(Collection<String> tags) throws ParseException { + requireNonNull(tags); + final Set<Tag> tagSet = new HashSet<>(); + for (String tagName : tags) { + tagSet.add(parseTag(tagName)); + } + return tagSet; + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/library/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/library/logic/parser/Prefix.java index c859d5fa5db..9c2af9d86ec 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/library/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/library/logic/parser/SortCommandParser.java b/src/main/java/seedu/library/logic/parser/SortCommandParser.java new file mode 100644 index 00000000000..e077abcb26a --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/SortCommandParser.java @@ -0,0 +1,25 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.library.logic.commands.SortCommand; +import seedu.library.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new SortCommand object. + */ +public class SortCommandParser implements Parser<SortCommand> { + /** + * Parses the given {@code String} of arguments in the context of the SortCommand + * and returns an SortCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public SortCommand parse(String args) throws ParseException { + try { + String order = ParserUtil.parseSort(args); + return new SortCommand(order); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/library/logic/parser/ViewCommandParser.java b/src/main/java/seedu/library/logic/parser/ViewCommandParser.java new file mode 100644 index 00000000000..a5c0ad6eb26 --- /dev/null +++ b/src/main/java/seedu/library/logic/parser/ViewCommandParser.java @@ -0,0 +1,26 @@ +package seedu.library.logic.parser; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.ViewCommand; +import seedu.library.logic.parser.exceptions.ParseException; +/** + * Parses input arguments and creates a new ViewCommand object. + */ +public class ViewCommandParser implements Parser<ViewCommand> { + + /** + * Parses the given {@code String} of arguments in the context of the ViewCommand + * and returns a ViewCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ViewCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ViewCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/library/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/library/logic/parser/exceptions/ParseException.java index 158a1a54c1c..032e88eecab 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/library/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.library.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.library.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/library/model/Library.java b/src/main/java/seedu/library/model/Library.java new file mode 100644 index 00000000000..3b896e96e1c --- /dev/null +++ b/src/main/java/seedu/library/model/Library.java @@ -0,0 +1,156 @@ +package seedu.library.model; + +import static java.util.Objects.requireNonNull; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javafx.collections.ObservableList; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.UniqueBookmarkList; +import seedu.library.model.tag.Tag; + +/** + * Wraps all data at the library level + * Duplicates are not allowed (by .isSameBookmark comparison) + */ +public class Library implements ReadOnlyLibrary { + + private final UniqueBookmarkList bookmarks; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + bookmarks = new UniqueBookmarkList(); + } + + public Library() {} + + /** + * Creates a Library using the Bookmarks in the {@code toBeCopied} + */ + public Library(ReadOnlyLibrary toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the bookmark list with {@code bookmarks}. + * {@code bookmarks} must not contain duplicate bookmarks. + */ + public void setBookmarks(List<Bookmark> bookmarks) { + this.bookmarks.setBookmarks(bookmarks); + } + + /** + * Resets the existing data of this {@code Library} with {@code newData}. + */ + public void resetData(ReadOnlyLibrary newData) { + requireNonNull(newData); + + setBookmarks(newData.getBookmarkList()); + } + + //// bookmark-level operations + + /** + * Returns true if a bookmark with the same identity as {@code bookmark} exists in the library. + */ + public boolean hasBookmark(Bookmark bookmark) { + requireNonNull(bookmark); + return bookmarks.contains(bookmark); + } + + /** + * Adds a bookmark to the library. + * The bookmark must not already exist in the library. + */ + public void addBookmark(Bookmark b) { + bookmarks.add(b); + } + + /** + * Replaces the given bookmark {@code target} in the list with {@code editedBookmark}. + * {@code target} must exist in the library. + * The bookmark identity of {@code editedBookmark} must not be the same as another existing bookmark in the library. + */ + public void setBookmark(Bookmark target, Bookmark editedBookmark) { + requireNonNull(editedBookmark); + + bookmarks.setBookmark(target, editedBookmark); + } + + /** + * Removes {@code key} from this {@code Library}. + * {@code key} must exist in the library. + */ + public void removeBookmark(Bookmark key) { + bookmarks.remove(key); + } + /** + * View {@code key} from this {@code Library}. + * {@code key} must exist in the library. + */ + public void viewBookmark(Bookmark key) { + bookmarks.view(key); + } + + //// library-level operations + + /** + * Sorts bookmark by order of rating. + * + * @param order Either ascending or descending order. + */ + public void sortBookmarks(String order) { + bookmarks.sortBookmarks(order); + } + + /** + * Gets all the tags that are used by all bookmarks. + * + * @return Set of all the tags used by all bookmarks. + */ + public Set<Tag> getAllTags() { + Set<Tag> allTags = new HashSet<>(); + for (Bookmark bookmark : getBookmarkList()) { + allTags.addAll(bookmark.getTags()); + } + return allTags; + } + + + + //// util methods + + @Override + public String toString() { + return bookmarks.asUnmodifiableObservableList().size() + " bookmarks"; + // TODO: refine later + } + + @Override + public ObservableList<Bookmark> getBookmarkList() { + return bookmarks.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Library // instanceof handles nulls + && bookmarks.equals(((Library) other).bookmarks)); + } + + @Override + public int hashCode() { + return bookmarks.hashCode(); + } +} diff --git a/src/main/java/seedu/library/model/Model.java b/src/main/java/seedu/library/model/Model.java new file mode 100644 index 00000000000..a3f565be4e8 --- /dev/null +++ b/src/main/java/seedu/library/model/Model.java @@ -0,0 +1,156 @@ +package seedu.library.model; + +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.library.commons.core.GuiSettings; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.tag.Tag; + +/** + * The API of the Model component. + */ +public interface Model { + /** {@code Predicate} that always evaluate to true */ + Predicate<Bookmark> PREDICATE_SHOW_ALL_BOOKMARKS = unused -> true; + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' library file path. + */ + Path getLibraryFilePath(); + + /** + * Sets the user prefs' library file path. + */ + void setLibraryFilePath(Path libraryFilePath); + + /** + * Replaces library data with the data in {@code Library}. + */ + void setLibrary(ReadOnlyLibrary library); + + /** Returns the Library */ + ReadOnlyLibrary getLibrary(); + + /** Returns the tags */ + ReadOnlyTags getTags(); + + /** Returns the tag list as a string */ + String tagListToString(); + + /** + * Returns true if a bookmark with the same identity as {@code bookmark} exists in the library. + */ + boolean hasBookmark(Bookmark bookmark); + + /** + * Deletes the given bookmark. + * The bookmark must exist in the library. + */ + void deleteBookmark(Bookmark target); + + /** + * Adds the given bookmark. + * {@code bookmark} must not already exist in the library. + */ + void addBookmark(Bookmark bookmark); + + /** + * Replaces the given bookmark {@code target} with {@code editedBookmark}. + * {@code target} must exist in the library. + * The bookmark identity of {@code editedBookmark} must not be the same as another existing bookmark in the library. + */ + void setBookmark(Bookmark target, Bookmark editedBookmark); + + /** Returns an unmodifiable view of the filtered bookmark list */ + ObservableList<Bookmark> getFilteredBookmarkList(); + + /** + * Updates the filter of the filtered bookmark list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredBookmarkList(Predicate<Bookmark> predicate); + + /** + * Updates the filtered list to be sorted by rating. + */ + public void updateSortedBookmarkList(String order); + + /** + * Gets the Bookmark that is currently being selected for zoomview + * + * @return the currently selected bookmark + */ + Bookmark getSelectedBookmark(); + + /** + * Gets the indek that is currently being selected for zoomview + * + * @return the currently selected bookmark + */ + int getSelectedIndex(); + + /** + * Updates the Bookmark that is currently being selected for zoomview + * + * @param target The bookmark that is selected for viewing + */ + void updateSelectedBookmark(Bookmark target); + + /** + * Updates the index that is currently being selected for zoomview + * + * @param index The index that is selected for viewing + */ + void updateSelectedIndex(int index); + + /** + * Returns true if any tag with the same identity as {@code tag} exists in the tag list. + */ + boolean hasTag(Set<Tag> tag); + + /** + * Returns true if a tag with the same identity as {@code tag} exists in the tag list. + */ + boolean hasTag(Tag tag); + + /** + * Adds the given tags. + * {@code tag} must not already exist in the tag list. + */ + void addTags(Set<Tag> tags); + + /** + * Deletes the given tag. + * The tag must exist in the tag list. + */ + void deleteTag(Tag target); + + /** + * Returns true if the {@code target} is in use by a bookmark. + */ + boolean tagInUse(Tag target); +} + diff --git a/src/main/java/seedu/library/model/ModelManager.java b/src/main/java/seedu/library/model/ModelManager.java new file mode 100644 index 00000000000..e12c3161eef --- /dev/null +++ b/src/main/java/seedu/library/model/ModelManager.java @@ -0,0 +1,229 @@ +package seedu.library.model; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Predicate; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.library.commons.core.GuiSettings; +import seedu.library.commons.core.LogsCenter; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.tag.Tag; + + +/** + * Represents the in-memory model of the library data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final Library library; + private final Tags tagList; + private final UserPrefs userPrefs; + private final FilteredList<Bookmark> filteredBookmarks; + private int selectedIndex; + private Bookmark selectedBookmark; + + + /** + * Initializes a ModelManager with the given library and userPrefs, tagList. + */ + public ModelManager(ReadOnlyLibrary library, ReadOnlyUserPrefs userPrefs, ReadOnlyTags tagList) { + requireAllNonNull(library, userPrefs); + + logger.fine("Initializing with library: " + library + + " and user prefs " + userPrefs + + " and tag list " + tagList); + + this.library = new Library(library); + this.userPrefs = new UserPrefs(userPrefs); + this.tagList = new Tags(tagList); + filteredBookmarks = new FilteredList<>(this.library.getBookmarkList()); + selectedBookmark = null; + selectedIndex = -1; + } + + public ModelManager() { + this(new Library(), new UserPrefs(), new Tags()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getLibraryFilePath() { + return userPrefs.getLibraryFilePath(); + } + + @Override + public void setLibraryFilePath(Path libraryFilePath) { + requireNonNull(libraryFilePath); + userPrefs.setLibraryFilePath(libraryFilePath); + } + + //=========== Library ================================================================================ + + @Override + public void setLibrary(ReadOnlyLibrary library) { + this.library.resetData(library); + } + + @Override + public ReadOnlyLibrary getLibrary() { + return library; + } + + @Override + public boolean hasBookmark(Bookmark bookmark) { + requireNonNull(bookmark); + return library.hasBookmark(bookmark); + } + + @Override + public void deleteBookmark(Bookmark target) { + library.removeBookmark(target); + } + + @Override + public void addBookmark(Bookmark bookmark) { + library.addBookmark(bookmark); + updateFilteredBookmarkList(PREDICATE_SHOW_ALL_BOOKMARKS); + } + + @Override + public void setBookmark(Bookmark target, Bookmark editedBookmark) { + requireAllNonNull(target, editedBookmark); + + library.setBookmark(target, editedBookmark); + } + public void viewBookmark(Bookmark target) { + library.viewBookmark(target); + } + + //=========== tagList ================================================================================ + + @Override + public ReadOnlyTags getTags() { + return tagList; + } + + @Override + public void addTags(Set<Tag> toAdd) { + for (Tag tagToAdd : toAdd) { + tagList.addTag(tagToAdd); + } + } + + @Override + public boolean hasTag(Set<Tag> tags) { + return tagList.containsAll(tags); + } + + @Override + public boolean hasTag(Tag tag) { + return tagList.contains(tag); + } + + @Override + public String tagListToString() { + return tagList.tagListToString(); + } + + @Override + public boolean tagInUse(Tag target) { + Set<Tag> allTags = library.getAllTags(); + return allTags.contains(target); + } + + @Override + public void deleteTag(Tag target) { + tagList.removeTag(target); + } + + //=========== Filtered Bookmark List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Bookmark} backed by the internal list of + * {@code versionedLibrary} + */ + @Override + public ObservableList<Bookmark> getFilteredBookmarkList() { + return filteredBookmarks; + } + + @Override + public void updateFilteredBookmarkList(Predicate<Bookmark> predicate) { + requireNonNull(predicate); + filteredBookmarks.setPredicate(predicate); + } + + @Override + public void updateSortedBookmarkList(String order) { + requireNonNull(order); + library.sortBookmarks(order); + } + + @Override + public Bookmark getSelectedBookmark() { + return this.selectedBookmark; + } + @Override + public int getSelectedIndex() { + return this.selectedIndex; + } + + @Override + public void updateSelectedBookmark(Bookmark target) { + this.selectedBookmark = target; + } + @Override + public void updateSelectedIndex(int index) { + this.selectedIndex = index; + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return library.equals(other.library) + && userPrefs.equals(other.userPrefs) + && filteredBookmarks.equals(other.filteredBookmarks); + } + +} diff --git a/src/main/java/seedu/library/model/RatingOrder.java b/src/main/java/seedu/library/model/RatingOrder.java new file mode 100644 index 00000000000..74c26a0d8f3 --- /dev/null +++ b/src/main/java/seedu/library/model/RatingOrder.java @@ -0,0 +1,23 @@ +package seedu.library.model; + +/** + * Order of the bookmark list by rating. + */ +public class RatingOrder { + + public static final String ASC = "asc"; + + public static final String DESC = "desc"; + + public static final String MESSAGE_CONSTRAINT = "Sort by either asc or desc"; + + /** + * Checks if the valid rating order is either asc or desc. + * + * @param order Order specified by user. + * @return true if rating order is valid and false otherwise. + */ + public static boolean isValidRatingOrder(String order) { + return order.equals(ASC) || order.equals(DESC); + } +} diff --git a/src/main/java/seedu/library/model/ReadOnlyLibrary.java b/src/main/java/seedu/library/model/ReadOnlyLibrary.java new file mode 100644 index 00000000000..17d079dddad --- /dev/null +++ b/src/main/java/seedu/library/model/ReadOnlyLibrary.java @@ -0,0 +1,17 @@ +package seedu.library.model; + +import javafx.collections.ObservableList; +import seedu.library.model.bookmark.Bookmark; + +/** + * Unmodifiable view of a library + */ +public interface ReadOnlyLibrary { + + /** + * Returns an unmodifiable view of the bookmarks list. + * This list will not contain any duplicate bookmarks. + */ + ObservableList<Bookmark> getBookmarkList(); + +} diff --git a/src/main/java/seedu/library/model/ReadOnlyTags.java b/src/main/java/seedu/library/model/ReadOnlyTags.java new file mode 100644 index 00000000000..aae8a93d3b9 --- /dev/null +++ b/src/main/java/seedu/library/model/ReadOnlyTags.java @@ -0,0 +1,15 @@ +package seedu.library.model; + +import javafx.collections.ObservableList; +import seedu.library.model.tag.Tag; + +/** + * Unmodifiable view of tags + */ +public interface ReadOnlyTags { + /** + * Returns an unmodifiable view of the tags list. + * This list will not contain any duplicate tags. + */ + ObservableList<Tag> getTagList(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/library/model/ReadOnlyUserPrefs.java similarity index 52% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/library/model/ReadOnlyUserPrefs.java index befd58a4c73..697f7a6413d 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/library/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.library.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.library.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,7 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getLibraryFilePath(); + Path getTagsFilePath(); } diff --git a/src/main/java/seedu/library/model/Tags.java b/src/main/java/seedu/library/model/Tags.java new file mode 100644 index 00000000000..18c04e1d75b --- /dev/null +++ b/src/main/java/seedu/library/model/Tags.java @@ -0,0 +1,136 @@ +package seedu.library.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Set; + +import javafx.collections.ObservableList; +import seedu.library.model.tag.Tag; + +/** + * Personal tag list for users to tag bookmark + * Duplicates are not allowed (by .isSameTag comparison) + */ +public class Tags implements ReadOnlyTags { + private final UniqueTagList tags; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + tags = new UniqueTagList(); + } + + public Tags() {} + + /** + * Creates a Tags using the tags in the {@code toBeCopied} + */ + public Tags(ReadOnlyTags toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the tag list with {@code tags}. + * {@code tags} must not contain duplicate tags. + */ + public void setTags(List<Tag> tags) { + this.tags.setTags(tags); + } + + /** + * Resets the existing data of this {@code Tags} with {@code newData}. + */ + public void resetData(ReadOnlyTags newData) { + requireNonNull(newData); + + setTags(newData.getTagList()); + } + + //// tag-level operations + + /** + * Returns true if a tag with the same identity as {@code tag} exists in the tag list. + */ + public boolean hasTag(Tag tag) { + requireNonNull(tag); + return tags.contains(tag); + } + + public boolean containsAll(Set<Tag> tags) { + return this.tags.containsAll(tags); + } + + public boolean contains(Tag tag) { + return this.tags.contains(tag); + } + + /** + * Adds a tag to the tag list. + * The tag must not already exist in the tag list. + */ + public void addTag(Tag b) { + tags.add(b); + } + + /** + * Replaces the given tag {@code target} in the list with {@code editedTag}. + * {@code target} must exist in the tag list. + * The tag identity of {@code editedTag} must not be the same as another existing tag in the tag list. + */ + public void setTag(Tag target, Tag editedTag) { + requireNonNull(editedTag); + + tags.setTag(target, editedTag); + } + + /** + * Removes {@code key} from this {@code Tags}. + * {@code key} must exist in the tag list. + */ + public void removeTag(Tag key) { + tags.remove(key); + } + + //// util methods + + @Override + public String toString() { + return tags.asUnmodifiableObservableList().size() + " tags"; + // TODO: refine later + } + + @Override + public ObservableList<Tag> getTagList() { + return tags.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Tags // instanceof handles nulls + && tags.equals(((Tags) other).tags)); + } + + @Override + public int hashCode() { + return tags.hashCode(); + } + + public boolean isEmpty() { + return tags.isEmpty(); + } + + public String tagListToString() { + return tags.tagListToString(); + } + +} diff --git a/src/main/java/seedu/library/model/UniqueTagList.java b/src/main/java/seedu/library/model/UniqueTagList.java new file mode 100644 index 00000000000..04a269c664a --- /dev/null +++ b/src/main/java/seedu/library/model/UniqueTagList.java @@ -0,0 +1,160 @@ +package seedu.library.model; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.library.model.bookmark.exceptions.BookmarkNotFoundException; +import seedu.library.model.bookmark.exceptions.DuplicateBookmarkException; +import seedu.library.model.tag.Tag; + +/** + * A list of tags that enforces uniqueness between its elements and does not allow nulls. + * A tag is considered unique by comparing using {@code Tag#isSameTag(Tag)}. + * As such, adding and updating of Tags uses Tag#isSameTag(Tag) for equality so as to + * ensure that the Tag being added or updated is unique in terms of identity in the UniqueTagList. + * However, the removal of a tag uses Tag#equals(Object) so as to ensure that + * the Tag with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Tag#isSameTag(Tag) + */ +public class UniqueTagList implements Iterable<Tag> { + + private final ObservableList<Tag> internalList = FXCollections.observableArrayList(); + private final ObservableList<Tag> internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent tag as the given argument. + */ + public boolean contains(Tag toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameTag); + } + + /** + * Returns true if all the tags in toCheck are in the tag list. + * @param toCheck Set of tags to check. + * @return true if all the tags in toCheck are in the tag list. + */ + public boolean containsAll(Set<Tag> toCheck) { + requireNonNull(toCheck); + return internalList.containsAll(toCheck); + } + + /** + * Adds a tag to the list. + * The tag must not already exist in the list. + */ + public void add(Tag toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateBookmarkException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the tag {@code target} in the list with {@code editedTag}. + * {@code target} must exist in the list. + * The tag identity of {@code editedTag} must not be the same as another existing tag in the list. + */ + public void setTag(Tag target, Tag editedTag) { + requireAllNonNull(target, editedTag); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new BookmarkNotFoundException(); + } + + if (!target.isSameTag(editedTag) && contains(editedTag)) { + throw new DuplicateBookmarkException(); + } + + internalList.set(index, editedTag); + } + + /** + * Removes the equivalent tag from the list. + * The tag must exist in the list. + */ + public void remove(Tag toRemove) { + requireNonNull(toRemove); + if (!internalList.removeIf(tagName -> tagName.isSameTag(toRemove))) { + throw new BookmarkNotFoundException(); + } + } + + + public void setTags(UniqueTagList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code tags}. + * {@code tag} must not contain duplicate tags. + */ + public void setTags(List<Tag> tags) { + requireAllNonNull(tags); + if (!tagsAreUnique(tags)) { + throw new DuplicateBookmarkException(); + } + + internalList.setAll(tags); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList<Tag> asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator<Tag> iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueTagList // instanceof handles nulls + && internalList.equals(((UniqueTagList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code tags} contains only unique tags. + */ + private boolean tagsAreUnique(List<Tag> tags) { + for (int i = 0; i < tags.size() - 1; i++) { + for (int j = i + 1; j < tags.size(); j++) { + if (tags.get(i).isSameTag(tags.get(j))) { + return false; + } + } + } + return true; + } + + public boolean isEmpty() { + return internalList.isEmpty(); + } + + public String tagListToString() { + return internalUnmodifiableList.toString(); + } +} + diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/library/model/UserPrefs.java similarity index 63% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/library/model/UserPrefs.java index 25a5fd6eab9..3d4a08cd7d6 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/library/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.library.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.library.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,8 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path libraryFilePath = Paths.get("data" , "library.json"); + private Path tagsFilePath = Paths.get("data", "tags.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +36,8 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setLibraryFilePath(newUserPrefs.getLibraryFilePath()); + setTagsFilePath(newUserPrefs.getTagsFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +49,22 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getLibraryFilePath() { + return libraryFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setLibraryFilePath(Path libraryFilePath) { + requireNonNull(libraryFilePath); + this.libraryFilePath = libraryFilePath; + } + + public void setTagsFilePath(Path tagsFilePath) { + requireNonNull(libraryFilePath); + this.tagsFilePath = tagsFilePath; + } + + public Path getTagsFilePath() { + return tagsFilePath; } @Override @@ -68,19 +79,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && libraryFilePath.equals(o.libraryFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, libraryFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + libraryFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/library/model/bookmark/Author.java b/src/main/java/seedu/library/model/bookmark/Author.java new file mode 100644 index 00000000000..24ed733de35 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Author.java @@ -0,0 +1,57 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.AppUtil.checkArgument; + +/** + * Represents a Bookmark's author in the library. + * Guarantees: immutable; is valid as declared in {@link #isValidAuthor(String)} + */ +public class Author { + + public static final String MESSAGE_CONSTRAINTS = "Author can take any values, and it should not be blank"; + + /* + * The first character of the author must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs an {@code Author}. + * + * @param author A valid author. + */ + public Author(String author) { + requireNonNull(author); + checkArgument(isValidAuthor(author), MESSAGE_CONSTRAINTS); + value = author; + } + + /** + * Returns true if a given string is a valid author. + */ + public static boolean isValidAuthor(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Author // instanceof handles nulls + && value.equals(((Author) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/library/model/bookmark/Bookmark.java b/src/main/java/seedu/library/model/bookmark/Bookmark.java new file mode 100644 index 00000000000..77eecdcb364 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Bookmark.java @@ -0,0 +1,146 @@ +package seedu.library.model.bookmark; + +import static seedu.library.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.library.model.tag.Tag; + +/** + * Represents a Bookmark in the library. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Bookmark implements Comparable<Bookmark> { + + // Identity fields + private final Title title; + private final Progress progress; + private final Genre genre; + + // Data fields + private final Author author; + private final Rating rating; + private final Url url; + private final Set<Tag> tags = new HashSet<>(); + + /** + * Title, genre and tags must be present and not null. + */ + public Bookmark(Title title, Progress progress, Genre genre, Author author, Rating rating, Url url, Set<Tag> tags) { + requireAllNonNull(title, genre, tags); + this.title = title; + this.progress = progress; + this.genre = genre; + this.author = author; + this.rating = rating; + this.url = url; + this.tags.addAll(tags); + } + + public Title getTitle() { + return title; + } + + public Progress getProgress() { + return progress; + } + + public Genre getGenre() { + return genre; + } + + public Author getAuthor() { + return author; + } + + public Rating getRating() { + return rating; + } + + public Url getUrl() { + return url; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set<Tag> getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both bookmarks have the same title. + * This defines a weaker notion of equality between two bookmarks. + */ + public boolean isSameBookmark(Bookmark otherBookmark) { + if (otherBookmark == this) { + return true; + } + + return otherBookmark != null + && otherBookmark.getTitle().equals(getTitle()); + } + + /** + * Returns true if both bookmarks have the same identity and data fields. + * This defines a stronger notion of equality between two bookmarks. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Bookmark)) { + return false; + } + + Bookmark otherBookmark = (Bookmark) other; + return otherBookmark.getTitle().equals(getTitle()) + //&& otherBookmark.getProgress().equals(getProgress()) + && otherBookmark.getGenre().equals(getGenre()); + //&& otherBookmark.getAuthor().equals(getAuthor()) + //&& otherBookmark.getRating().equals(getRating()) + //&& otherBookmark.getUrl().equals(getUrl()) + //&& otherBookmark.getTags().equals(getTags()); + } + + @Override + public int compareTo(Bookmark other) { + return this.rating.compareTo(other.rating); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(title, progress, genre, author, tags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getTitle()) + .append("; Progress: ") + .append(getProgress()) + .append("; Genre: ") + .append(getGenre()) + .append("; Author: ") + .append(getAuthor()) + .append("; Rating: ") + .append(getRating()) + .append("; Url: ") + .append(getUrl()); + + Set<Tag> tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/library/model/bookmark/BookmarkContainsKeywordsPredicate.java b/src/main/java/seedu/library/model/bookmark/BookmarkContainsKeywordsPredicate.java new file mode 100644 index 00000000000..5e6a590b5fe --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/BookmarkContainsKeywordsPredicate.java @@ -0,0 +1,122 @@ +package seedu.library.model.bookmark; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.library.commons.util.StringUtil; + +/** + * Tests that a {@code Bookmark}'s {@code Title} matches any of the keywords given. + */ +public class BookmarkContainsKeywordsPredicate implements Predicate<Bookmark> { + private final List<String> titleKeywords; + private final List<String> genreKeywords; + private final List<String> tagKeywords; + private final List<String> authorKeywords; + + /** + * Constructs an {@code TitleContainsKeywordsPredicate}. + * + * @param titleKeywords keywords for searching title + * @param genreKeywords keywords for searching genre + * @param tagKeywords keywords for searching tag + * @param authorKeywords keywords for searching author + */ + public BookmarkContainsKeywordsPredicate(List<String> titleKeywords, List<String> genreKeywords, + List<String> tagKeywords, List<String> authorKeywords) { + this.titleKeywords = titleKeywords; + this.genreKeywords = genreKeywords; + this.tagKeywords = tagKeywords; + this.authorKeywords = authorKeywords; + } + + @Override + public boolean test(Bookmark bookmark) { + return testTitle(bookmark) && testGenre(bookmark) && testTag(bookmark) && testAuthor(bookmark); + } + + public List<String> getTagKeywords() { + return tagKeywords; + } + + private boolean testTitle(Bookmark bookmark) { + if (titleKeywords == null) { + return true; + } else { + return titleKeywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(bookmark.getTitle().value, keyword)); + } + } + + private boolean testGenre(Bookmark bookmark) { + if (genreKeywords == null) { + return true; + } else { + return genreKeywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(bookmark.getGenre().value, keyword)); + } + } + + private boolean testTag(Bookmark bookmark) { + if (tagKeywords == null) { + return true; + } else { + return tagKeywords.stream() + .allMatch(keyword -> bookmark.getTags() + .stream().anyMatch(tag -> keyword.equalsIgnoreCase(tag.tagName))); + } + } + + private boolean testAuthor(Bookmark bookmark) { + if (authorKeywords == null) { + return true; + } else if (bookmark.getAuthor() == null) { + return false; + } else { + return authorKeywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(bookmark.getAuthor().value, keyword)); + } + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof BookmarkContainsKeywordsPredicate)) { + return false; + } + boolean sameTitleKeyword; + boolean sameGenreKeyword; + boolean sameTagKeyword; + boolean sameAuthorKeyword; + + if (this.titleKeywords == null) { + sameTitleKeyword = this.titleKeywords == ((BookmarkContainsKeywordsPredicate) other).titleKeywords; + } else { + sameTitleKeyword = titleKeywords.equals(((BookmarkContainsKeywordsPredicate) other).titleKeywords); + } + + if (this.genreKeywords == null) { + sameGenreKeyword = this.genreKeywords == ((BookmarkContainsKeywordsPredicate) other).genreKeywords; + } else { + sameGenreKeyword = genreKeywords.equals(((BookmarkContainsKeywordsPredicate) other).genreKeywords); + } + + if (this.tagKeywords == null) { + sameTagKeyword = this.tagKeywords == ((BookmarkContainsKeywordsPredicate) other).tagKeywords; + } else { + sameTagKeyword = tagKeywords.equals(((BookmarkContainsKeywordsPredicate) other).tagKeywords); + } + + if (this.authorKeywords == null) { + sameAuthorKeyword = this.authorKeywords == ((BookmarkContainsKeywordsPredicate) other).authorKeywords; + } else { + sameAuthorKeyword = authorKeywords.equals(((BookmarkContainsKeywordsPredicate) other).authorKeywords); + } + + + return sameTitleKeyword && sameGenreKeyword && sameTagKeyword && sameAuthorKeyword; // state check + } +} diff --git a/src/main/java/seedu/library/model/bookmark/Genre.java b/src/main/java/seedu/library/model/bookmark/Genre.java new file mode 100644 index 00000000000..a751f636341 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Genre.java @@ -0,0 +1,73 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.AppUtil.checkArgument; + +import java.util.Arrays; +import java.util.List; + +/** + * Represents a Bookmark's genre in the library. + * Guarantees: immutable; is valid as declared in {@link #isValidGenre(String)} + */ +public class Genre { + + public static final String MESSAGE_CONSTRAINTS = "Genre can take on values in the pre-defined list, " + + "and it should not be blank\n" + + "View list of valid genres with the command: genre"; + + public static final List<String> VALID_GENRES = Arrays.asList("Action", "Adventure", "Comedy", "Drama", + "Fantasy", "Historical", "Horror", "Martial Arts", "Sci-Fi", "Mystery", "Romance", "Sports", "Others"); + + public final String value; + + /** + * Constructs a {@code Genre}. + * + * @param genre A valid genre. + */ + public Genre(String genre) { + requireNonNull(genre); + checkArgument(isValidGenre(genre), MESSAGE_CONSTRAINTS); + value = genre; + } + + /** + * Constructs a regex string to match valid genres. + * @return Regex string. + */ + private static String getValidationRegex() { + String validGenreOptions = String.join("|", VALID_GENRES); + return "^(" + validGenreOptions + ")$"; + } + + public static List<String> getValidGenres() { + return VALID_GENRES; + } + + /** + * Returns true if a given string is a valid genre. + */ + public static boolean isValidGenre(String test) { + String validationRegex = getValidationRegex(); + return test.matches(validationRegex); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Genre // instanceof handles nulls + && value.equals(((Genre) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/library/model/bookmark/Progress.java b/src/main/java/seedu/library/model/bookmark/Progress.java new file mode 100644 index 00000000000..8b8dfaa8994 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Progress.java @@ -0,0 +1,110 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.AppUtil.checkArgument; + +/** + * Represents a Book's Progress status in the Library. + * Guarantees: immutable; is valid as declared in {@link #isValidProgress(String[])} + */ +public class Progress { + + + + public static final String MESSAGE_CONSTRAINTS = + "Progress should have only 3 values. Each of which is either a numeric or ~. The values cannot all be ~."; + public static final String VALIDATION_REGEX = "^(~|0|[1-9][0-9]*)$"; + + public final String volume; + public final String chapter; + public final String page; + + /** + * Constructs a {@code Progress}. + * + * @param progress a valid progress + */ + public Progress(String[] progress) { + requireNonNull(progress); + checkArgument(isValidProgress(progress), MESSAGE_CONSTRAINTS); + + volume = progress[0]; + chapter = progress[1]; + page = progress[2]; + } + + /** + * Returns true if a given string is a valid progress. + */ + public static boolean isValidProgress(String[] tests) { + if (tests.length != 3) { + return false; + } + + for (int i = 0; i != 3; ++i) { + if (!tests[i].matches(VALIDATION_REGEX)) { + return false; + } + } + + if (tests[0] == "~" && tests[1] == "~" && tests[2] == "~") { + return false; + } + + return true; + } + + public String getVolume() { + return volume; + } + + public String getChapter() { + return chapter; + } + public String getPage() { + return page; + } + + @Override + public String toString() { + String accumulator = ""; + + if (!volume.equals("~")) { + accumulator += "Vol. " + volume + " "; + } + + if (!chapter.equals("~")) { + accumulator += "Ch. " + chapter + " "; + } + + if (!page.equals("~")) { + accumulator += "Pg. " + page; + } + + return accumulator.trim(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Progress // instanceof handles nulls + && volume.equals(((Progress) other).volume) + && chapter.equals(((Progress) other).chapter) + && page.equals(((Progress) other).page)); // state check + } + + /** + * Gets the command string for this Progress object. + * + * @return the command string for this Progress object. + */ + public String getDetails() { + return volume + " " + chapter + " " + page; + } + + @Override + public int hashCode() { + return (volume + chapter + page).hashCode(); + } + +} diff --git a/src/main/java/seedu/library/model/bookmark/Rating.java b/src/main/java/seedu/library/model/bookmark/Rating.java new file mode 100644 index 00000000000..1365c28e425 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Rating.java @@ -0,0 +1,59 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.AppUtil.checkArgument; + +/** + * Represents the rating of a book in the library. + * Guarantees: immutable; is valid as declared in {@link #isValidRating(String)} + */ +public class Rating implements Comparable<Rating> { + + public static final Rating DEFAULT_RATING = new Rating("0"); + + public static final String MESSAGE_CONSTRAINTS = + "Rating should be integer value from 0 to 5(inclusive)"; + public static final String VALIDATION_REGEX = "[0-5]"; + + public final String value; + + /** + * Constructs a {@code Rating}. + * @param rating of a book. + */ + public Rating(String rating) { + requireNonNull(rating); + checkArgument(isValidRating(rating), MESSAGE_CONSTRAINTS); + value = rating; + } + + @Override + public String toString() { + return value; + } + /** + * Returns true if a given string is a valid url. + */ + public static boolean isValidRating(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Rating // instanceof handles nulls + && value.equals(((Rating) other).value)); // state check + } + + @Override + public int compareTo(Rating other) { + return this.value.compareTo(other.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + +} diff --git a/src/main/java/seedu/library/model/bookmark/Title.java b/src/main/java/seedu/library/model/bookmark/Title.java new file mode 100644 index 00000000000..3eb68ba8ec9 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Title.java @@ -0,0 +1,63 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.AppUtil.checkArgument; + +/** + * Represents a Bookmark's title in the library. + * Guarantees: immutable; is valid as declared in {@link #isValidTitle(String)} + */ +public class Title { + + public static final int TITLE_MAX_LENGTH = 180; + + public static final String MESSAGE_CONSTRAINTS = + "Title can take any values, it should not be blank and length of title should not be longer than " + + TITLE_MAX_LENGTH + + "characters"; + + /* + * The first character of the title must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs a {@code title}. + * + * @param title A valid title. + */ + public Title(String title) { + requireNonNull(title); + checkArgument(isValidTitle(title), MESSAGE_CONSTRAINTS); + value = title; + } + + /** + * Returns true if a given string is a valid title. + */ + public static boolean isValidTitle(String test) { + return test.matches(VALIDATION_REGEX) && test.length() <= TITLE_MAX_LENGTH; + } + + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Title // instanceof handles nulls + && value.equals(((Title) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/library/model/bookmark/UniqueBookmarkList.java b/src/main/java/seedu/library/model/bookmark/UniqueBookmarkList.java new file mode 100644 index 00000000000..a214d7ce72b --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/UniqueBookmarkList.java @@ -0,0 +1,162 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.library.model.bookmark.exceptions.BookmarkNotFoundException; +import seedu.library.model.bookmark.exceptions.DuplicateBookmarkException; + +/** + * A list of bookmarks that enforces uniqueness between its elements and does not allow nulls. + * A bookmark is considered unique by comparing using {@code Bookmark#isSameBookmark(Bookmark)}. + * As such, adding and updating of Bookmarks uses Bookmark#isSameBookmark(Bookmark) for equality so as to + * ensure that the Bookmark being added or updated is unique in terms of identity in the UniqueBookmarkList. + * However, the removal of a bookmark uses Bookmark#equals(Object) so as to ensure that + * the Bookmark with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Bookmark#isSameBookmark(Bookmark) + */ +public class UniqueBookmarkList implements Iterable<Bookmark> { + + private final ObservableList<Bookmark> internalList = FXCollections.observableArrayList(); + private final ObservableList<Bookmark> internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent bookmark as the given argument. + */ + public boolean contains(Bookmark toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameBookmark); + } + + /** + * Adds a bookmark to the list. + * The bookmark must not already exist in the list. + */ + public void add(Bookmark toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateBookmarkException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the bookmark {@code target} in the list with {@code editedBookmark}. + * {@code target} must exist in the list. + * The bookmark identity of {@code editedBookmark} must not be the same as another existing bookmark in the list. + */ + public void setBookmark(Bookmark target, Bookmark editedBookmark) { + requireAllNonNull(target, editedBookmark); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new BookmarkNotFoundException(); + } + + if (!target.isSameBookmark(editedBookmark) && contains(editedBookmark)) { + throw new DuplicateBookmarkException(); + } + + internalList.set(index, editedBookmark); + } + + /** + * Removes the equivalent bookmark from the list. + * The bookmark must exist in the list. + */ + public void remove(Bookmark toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new BookmarkNotFoundException(); + } + } + /** + * Views the equivalent bookmark from the list. + * The bookmark must exist in the list. + */ + public void view(Bookmark toView) { + requireNonNull(toView); + if (!contains(toView)) { + throw new BookmarkNotFoundException(); + } + } + + public void setBookmarks(UniqueBookmarkList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code bookmarks}. + * {@code bookmarks} must not contain duplicate bookmarks. + */ + public void setBookmarks(List<Bookmark> bookmarks) { + requireAllNonNull(bookmarks); + if (!bookmarksAreUnique(bookmarks)) { + throw new DuplicateBookmarkException(); + } + + internalList.setAll(bookmarks); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList<Bookmark> asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + /** + * Sorts the bookmark list in either ascending or descending order. + * @param order Order to sort the list by. + */ + public void sortBookmarks(String order) { + if (order.equals("asc")) { + Collections.sort(internalList); + } + if (order.equals("desc")) { + internalList.sort(Collections.reverseOrder()); + } + } + + @Override + public Iterator<Bookmark> iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueBookmarkList // instanceof handles nulls + && internalList.equals(((UniqueBookmarkList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code bookmarks} contains only unique bookmarks. + */ + private boolean bookmarksAreUnique(List<Bookmark> bookmarks) { + for (int i = 0; i < bookmarks.size() - 1; i++) { + for (int j = i + 1; j < bookmarks.size(); j++) { + if (bookmarks.get(i).isSameBookmark(bookmarks.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/library/model/bookmark/Url.java b/src/main/java/seedu/library/model/bookmark/Url.java new file mode 100644 index 00000000000..220a7bd2de2 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/Url.java @@ -0,0 +1,51 @@ +package seedu.library.model.bookmark; + +import static java.util.Objects.requireNonNull; +import static seedu.library.commons.util.AppUtil.checkArgument; + +/** + * Represents a Bookmark's url in the library. + */ +public class Url { + + public static final String MESSAGE_CONSTRAINTS = + "UrlLink should be in valid format [Protocol][Domain name] for example: [http://]www.[example.com] "; + public static final String VALIDATION_REGEX = + "^$|^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; + + public final String value; + + /** + * Constructs a {@code Url}. + * @param url + */ + public Url(String url) { + requireNonNull(url); + checkArgument(isValidUrlLink(url), MESSAGE_CONSTRAINTS); + value = url; + } + + @Override + public String toString() { + return value; + } + /** + * Returns true if a given string is a valid url. + */ + public static boolean isValidUrlLink(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Url // instanceof handles nulls + && value.equals(((Url) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/library/model/bookmark/exceptions/BookmarkNotFoundException.java b/src/main/java/seedu/library/model/bookmark/exceptions/BookmarkNotFoundException.java new file mode 100644 index 00000000000..b4b599d4797 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/exceptions/BookmarkNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.library.model.bookmark.exceptions; + +/** + * Signals that the operation is unable to find the specified bookmark. + */ +public class BookmarkNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/library/model/bookmark/exceptions/DuplicateBookmarkException.java b/src/main/java/seedu/library/model/bookmark/exceptions/DuplicateBookmarkException.java new file mode 100644 index 00000000000..8d2160237a2 --- /dev/null +++ b/src/main/java/seedu/library/model/bookmark/exceptions/DuplicateBookmarkException.java @@ -0,0 +1,11 @@ +package seedu.library.model.bookmark.exceptions; + +/** + * Signals that the operation will result in duplicate + * Bookmarks (Bookmarks are considered duplicates if they have the same identity). + */ +public class DuplicateBookmarkException extends RuntimeException { + public DuplicateBookmarkException() { + super("Operation would result in duplicate bookmarks"); + } +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/library/model/tag/Tag.java similarity index 67% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/seedu/library/model/tag/Tag.java index b0ea7e7dad7..29cb3a3ccd2 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/library/model/tag/Tag.java @@ -1,15 +1,15 @@ -package seedu.address.model.tag; +package seedu.library.model.tag; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.library.commons.util.AppUtil.checkArgument; /** - * Represents a Tag in the address book. + * Represents a Tag in the library. * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} */ public class Tag { - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; + public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric, no space allowed"; public static final String VALIDATION_REGEX = "\\p{Alnum}+"; public final String tagName; @@ -32,6 +32,24 @@ public static boolean isValidTagName(String test) { return test.matches(VALIDATION_REGEX); } + + public String getTagName() { + return this.tagName; + } + + /** + * Returns true if both tags have the same name. + * This defines a weaker notion of equality between two tags. + */ + public boolean isSameTag(Tag otherTag) { + if (otherTag == this) { + return true; + } + + return otherTag != null + && otherTag.getTagName().equals(getTagName()); + } + @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/main/java/seedu/library/model/util/SampleDataUtil.java b/src/main/java/seedu/library/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..514a5776965 --- /dev/null +++ b/src/main/java/seedu/library/model/util/SampleDataUtil.java @@ -0,0 +1,90 @@ +package seedu.library.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.library.model.Library; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.Tags; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; + +/** + * Contains utility methods for populating {@code Library} with sample data. + */ +public class SampleDataUtil { + public static Bookmark[] getSampleBookmarks() { + String[] sampleProgressA = {"1", "40", "~"}; + String[] sampleProgressB = {"~", "2", "~"}; + String[] sampleProgressC = {"~", "~", "50"}; + return new Bookmark[] { + new Bookmark(new Title("Rankers Guide"), new Progress(sampleProgressA), + new Genre("Fantasy"), new Author("TeJe"), new Rating("4"), + new Url("https://www.wuxiaworld.eu/novel/the-rankers-guide-to-live-an-ordinary-life"), + getTagSet("Hunters")), + new Bookmark(new Title("Chainsaw Man"), new Progress(sampleProgressB), new Genre("Action"), + new Author("Tatsuki Fujimoto"), new Rating("5"), + new Url("https://www.chainsaw-man-manga.online/"), + getTagSet("Gore")), + new Bookmark(new Title("Solo Leveling"), new Progress(sampleProgressC), new Genre("Fantasy"), + new Author("Chugong"), new Rating("3"), + new Url("https://sololeveling-manhwa.online/"), + getTagSet("Hunters", "System", "Cheats")), + new Bookmark(new Title("Dungeon Defense"), new Progress(sampleProgressA), new Genre("Fantasy"), + new Author("Yoo Heonhwa"), new Rating("4"), + new Url("https://www.wuxiaworld.eu/novel/dungeon-defense-wn"), + getTagSet("Antihero")) + }; + } + public static ReadOnlyLibrary getSampleLibrary() { + Library sampleAb = new Library(); + for (Bookmark sampleBookmark : getSampleBookmarks()) { + sampleAb.addBookmark(sampleBookmark); + } + return sampleAb; + } + + public static Tag[] getSampleTags() { + return new Tag[] { + new Tag("MaleProtagonist"), + new Tag("FemaleProtagonist"), + new Tag("Manhwa"), + new Tag("Manhua"), + new Tag("Manga"), + new Tag("Novel"), + new Tag("Hunters"), + new Tag("Gore"), + new Tag("Cheats"), + new Tag("System"), + new Tag("Antihero"), + new Tag("Literature"), + new Tag("School") + }; + } + + public static ReadOnlyTags getSampleTagList() { + Tags sampleTags = new Tags(); + for (Tag sampleTag : getSampleTags()) { + sampleTags.addTag(sampleTag); + } + return sampleTags; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set<Tag> getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/seedu/library/storage/JsonAdaptedBookmark.java b/src/main/java/seedu/library/storage/JsonAdaptedBookmark.java new file mode 100644 index 00000000000..604d26eb01a --- /dev/null +++ b/src/main/java/seedu/library/storage/JsonAdaptedBookmark.java @@ -0,0 +1,137 @@ +package seedu.library.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; + +/** + * Jackson-friendly version of {@link Bookmark}. + */ +class JsonAdaptedBookmark { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Bookmark's %s field is missing!"; + + private final String title; + private final JsonAdaptedProgress progress; + private final String genre; + private final String author; + private final String url; + private final String rating; + private final List<JsonAdaptedTag> tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedBookmark} with the given bookmark details. + */ + @JsonCreator + public JsonAdaptedBookmark(@JsonProperty("title") String title, + @JsonProperty("progress") JsonAdaptedProgress progress, + @JsonProperty("genre") String genre, + @JsonProperty("author") String author, + @JsonProperty("rating") String rating, + @JsonProperty("url") String url, + @JsonProperty("tagged") List<JsonAdaptedTag> tagged) { + this.title = title; + this.progress = progress; + this.genre = genre; + this.author = author; + this.rating = rating; + this.url = url; + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a given {@code Bookmark} into this class for Jackson use. + */ + public JsonAdaptedBookmark(Bookmark source) { + title = source.getTitle().value; + progress = (source.getProgress() == null) ? null : new JsonAdaptedProgress(source.getProgress()); + genre = source.getGenre().value; + author = (source.getAuthor() == null) ? null : source.getAuthor().value; + rating = (source.getRating() == null) ? null : source.getRating().value; + url = source.getUrl().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted bookmark object into the model's {@code Bookmark} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted bookmark. + */ + public Bookmark toModelType() throws IllegalValueException { + final List<Tag> bookmarkTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + bookmarkTags.add(tag.toModelType()); + } + + if (title == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Title.class.getSimpleName())); + } + if (!Title.isValidTitle(title)) { + throw new IllegalValueException(Title.MESSAGE_CONSTRAINTS); + } + final Title modelTitle = new Title(title); + + + Progress modelProgress = null; + if (progress != null) { + modelProgress = progress.toModelType(); + } + + + if (genre == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Genre.class.getSimpleName())); + } + if (!Genre.isValidGenre(genre)) { + throw new IllegalValueException(Genre.MESSAGE_CONSTRAINTS); + } + final Genre modelGenre = new Genre(genre); + + Author modelAuthor = null; + if (author != null) { + if (!Author.isValidAuthor(author)) { + throw new IllegalValueException(Author.MESSAGE_CONSTRAINTS); + } + modelAuthor = new Author(author); + } + + Rating modelRating = null; + if (rating != null) { + if (!Rating.isValidRating(rating)) { + throw new IllegalValueException(Rating.MESSAGE_CONSTRAINTS); + } + modelRating = new Rating(rating); + } + + if (url == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Url.class.getSimpleName())); + } + if (!Url.isValidUrlLink(url)) { + throw new IllegalValueException(Url.MESSAGE_CONSTRAINTS); + } + final Url modelUrl = new Url(url); + + + final Set<Tag> modelTags = new HashSet<>(bookmarkTags); + return new Bookmark(modelTitle, modelProgress, modelGenre, modelAuthor, modelRating, modelUrl, modelTags); + } + +} diff --git a/src/main/java/seedu/library/storage/JsonAdaptedProgress.java b/src/main/java/seedu/library/storage/JsonAdaptedProgress.java new file mode 100644 index 00000000000..68f5b2c0619 --- /dev/null +++ b/src/main/java/seedu/library/storage/JsonAdaptedProgress.java @@ -0,0 +1,50 @@ +package seedu.library.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.bookmark.Progress; + +/** + * Jackson-friendly version of {@link Progress}. + */ +public class JsonAdaptedProgress { + private final String volume; + private final String chapter; + private final String page; + + /** + * Constructs a {@code JsonAdaptedProgress} with the given {@code volume}, {@code chapter} and {@code page}. + */ + @JsonCreator + public JsonAdaptedProgress(@JsonProperty("volume") String volume, + @JsonProperty("chapter") String chapter, + @JsonProperty("page") String page) { + this.volume = volume; + this.chapter = chapter; + this.page = page; + } + + /** + * Converts a given {@code Progress} into this class for Jackson use. + */ + public JsonAdaptedProgress(Progress source) { + volume = source.volume; + chapter = source.chapter; + page = source.page; + } + + /** + * Converts this Jackson-friendly adapted progress object into the model's {@code Progress} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted progress. + */ + public Progress toModelType() throws IllegalValueException { + String[] splitProgress = {volume, chapter, page}; + if (!Progress.isValidProgress(splitProgress)) { + throw new IllegalValueException(Progress.MESSAGE_CONSTRAINTS); + } + return new Progress(splitProgress); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/library/storage/JsonAdaptedTag.java similarity index 60% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/seedu/library/storage/JsonAdaptedTag.java index 0df22bdb754..9fc2594d8e6 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/library/storage/JsonAdaptedTag.java @@ -1,36 +1,31 @@ -package seedu.address.storage; +package seedu.library.storage; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonProperty; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.tag.Tag; /** * Jackson-friendly version of {@link Tag}. */ class JsonAdaptedTag { - private final String tagName; + private final String tag; /** * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}. */ @JsonCreator - public JsonAdaptedTag(String tagName) { - this.tagName = tagName; + public JsonAdaptedTag(@JsonProperty("tag") String tag) { + this.tag = tag; } /** * Converts a given {@code Tag} into this class for Jackson use. */ public JsonAdaptedTag(Tag source) { - tagName = source.tagName; - } - - @JsonValue - public String getTagName() { - return tagName; + tag = source.tagName; } /** @@ -39,10 +34,10 @@ public String getTagName() { * @throws IllegalValueException if there were any data constraints violated in the adapted tag. */ public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { + if (!Tag.isValidTagName(tag)) { throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); } - return new Tag(tagName); + return new Tag(tag); } } diff --git a/src/main/java/seedu/library/storage/JsonLibraryStorage.java b/src/main/java/seedu/library/storage/JsonLibraryStorage.java new file mode 100644 index 00000000000..4b6109e9479 --- /dev/null +++ b/src/main/java/seedu/library/storage/JsonLibraryStorage.java @@ -0,0 +1,80 @@ +package seedu.library.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.commons.util.FileUtil; +import seedu.library.commons.util.JsonUtil; +import seedu.library.model.ReadOnlyLibrary; + +/** + * A class to access Library data stored as a json file on the hard disk. + */ +public class JsonLibraryStorage implements LibraryStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonLibraryStorage.class); + + private Path filePath; + + public JsonLibraryStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getLibraryFilePath() { + return filePath; + } + + @Override + public Optional<ReadOnlyLibrary> readLibrary() throws DataConversionException { + return readLibrary(filePath); + } + + /** + * Similar to {@link #readLibrary()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional<ReadOnlyLibrary> readLibrary(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional<JsonSerializableLibrary> jsonLibrary = JsonUtil.readJsonFile( + filePath, JsonSerializableLibrary.class); + if (!jsonLibrary.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonLibrary.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveLibrary(ReadOnlyLibrary library) throws IOException { + saveLibrary(library, filePath); + } + + /** + * Similar to {@link #saveLibrary(ReadOnlyLibrary)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveLibrary(ReadOnlyLibrary library, Path filePath) throws IOException { + requireNonNull(library); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableLibrary(library), filePath); + } + +} diff --git a/src/main/java/seedu/library/storage/JsonSerializableLibrary.java b/src/main/java/seedu/library/storage/JsonSerializableLibrary.java new file mode 100644 index 00000000000..32ca925603c --- /dev/null +++ b/src/main/java/seedu/library/storage/JsonSerializableLibrary.java @@ -0,0 +1,60 @@ +package seedu.library.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.Library; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.bookmark.Bookmark; + +/** + * An Immutable Library that is serializable to JSON format. + */ +@JsonRootName(value = "library") +class JsonSerializableLibrary { + + public static final String MESSAGE_DUPLICATE_BOOKMARK = "Bookmarks list contains duplicate bookmark(s)."; + + private final List<JsonAdaptedBookmark> bookmarks = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableLibrary} with the given bookmarks. + */ + @JsonCreator + public JsonSerializableLibrary(@JsonProperty("bookmarks") List<JsonAdaptedBookmark> bookmarks) { + this.bookmarks.addAll(bookmarks); + } + + /** + * Converts a given {@code ReadOnlyLibrary} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableLibrary}. + */ + public JsonSerializableLibrary(ReadOnlyLibrary source) { + bookmarks.addAll(source.getBookmarkList().stream().map(JsonAdaptedBookmark::new).collect(Collectors.toList())); + } + + /** + * Converts this library into the model's {@code Library} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public Library toModelType() throws IllegalValueException { + Library library = new Library(); + for (JsonAdaptedBookmark jsonAdaptedBookmark : bookmarks) { + Bookmark bookmark = jsonAdaptedBookmark.toModelType(); + if (library.hasBookmark(bookmark)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_BOOKMARK); + } + library.addBookmark(bookmark); + } + return library; + } + +} diff --git a/src/main/java/seedu/library/storage/JsonSerializableTags.java b/src/main/java/seedu/library/storage/JsonSerializableTags.java new file mode 100644 index 00000000000..a3a23ef7b01 --- /dev/null +++ b/src/main/java/seedu/library/storage/JsonSerializableTags.java @@ -0,0 +1,59 @@ +package seedu.library.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.Tags; +import seedu.library.model.tag.Tag; + +/** + * An Immutable tag list that is serializable to JSON format. + */ +@JsonRootName(value = "tags") +public class JsonSerializableTags { + + public static final String MESSAGE_DUPLICATE_TAGS = "Tags list contains duplicate tag(s)."; + + private final List<JsonAdaptedTag> tagList = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableTags} with the given tags. + */ + @JsonCreator + public JsonSerializableTags(@JsonProperty("tagList") List<JsonAdaptedTag> tagList) { + this.tagList.addAll(tagList); + } + + /** + * Converts a given {@code ReadOnlyTags} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableTag}. + */ + public JsonSerializableTags(ReadOnlyTags source) { + tagList.addAll(source.getTagList().stream().map(JsonAdaptedTag::new).collect(Collectors.toList())); + } + + /** + * Converts tags into the model's {@code Tags} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public Tags toModelType() throws IllegalValueException { + Tags tags = new Tags(); + for (JsonAdaptedTag jsonAdaptedTag : tagList) { + Tag tag = jsonAdaptedTag.toModelType(); + if (tags.hasTag(tag)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_TAGS); + } + tags.addTag(tag); + } + return tags; + } +} diff --git a/src/main/java/seedu/library/storage/JsonTagsStorage.java b/src/main/java/seedu/library/storage/JsonTagsStorage.java new file mode 100644 index 00000000000..8dfe8cbb7a8 --- /dev/null +++ b/src/main/java/seedu/library/storage/JsonTagsStorage.java @@ -0,0 +1,78 @@ +package seedu.library.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.commons.util.FileUtil; +import seedu.library.commons.util.JsonUtil; +import seedu.library.model.ReadOnlyTags; + +/** + * A class to access tag list data stored as a json file on the hard disk. + */ +public class JsonTagsStorage implements TagsStorage { + private static final Logger logger = LogsCenter.getLogger(JsonLibraryStorage.class); + + private Path filePath; + + public JsonTagsStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getTagsFilePath() { + return filePath; + } + + @Override + public Optional<ReadOnlyTags> readTags() throws DataConversionException { + return readTags(filePath); + } + + /** + * Similar to {@link #readTags()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional<ReadOnlyTags> readTags(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional<JsonSerializableTags> jsonTags = JsonUtil.readJsonFile( + filePath, JsonSerializableTags.class); + if (!jsonTags.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTags.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveTags(ReadOnlyTags tags) throws IOException { + saveTags(tags, filePath); + } + + /** + * Similar to {@link #saveTags(ReadOnlyTags)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveTags(ReadOnlyTags tags, Path filePath) throws IOException { + requireNonNull(tags); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableTags(tags), filePath); + } +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/library/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/library/storage/JsonUserPrefsStorage.java index bc2bbad84aa..530fd250b53 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/library/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.library.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.commons.util.JsonUtil; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/library/storage/LibraryStorage.java b/src/main/java/seedu/library/storage/LibraryStorage.java new file mode 100644 index 00000000000..05ad48848f3 --- /dev/null +++ b/src/main/java/seedu/library/storage/LibraryStorage.java @@ -0,0 +1,46 @@ +package seedu.library.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.Library; +import seedu.library.model.ReadOnlyLibrary; + +/** + * Represents a storage for {@link Library}. + */ +public interface LibraryStorage { + + /** + * Returns the file path of the data file. + */ + Path getLibraryFilePath(); + + /** + * Returns Library data as a {@link ReadOnlyLibrary}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional<ReadOnlyLibrary> readLibrary() throws DataConversionException, IOException; + + /** + * @see #getLibraryFilePath() + */ + Optional<ReadOnlyLibrary> readLibrary(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyLibrary} to the storage. + * @param library cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveLibrary(ReadOnlyLibrary library) throws IOException; + + /** + * @see #saveLibrary(ReadOnlyLibrary) + */ + void saveLibrary(ReadOnlyLibrary library, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/library/storage/Storage.java b/src/main/java/seedu/library/storage/Storage.java new file mode 100644 index 00000000000..86edf4bb648 --- /dev/null +++ b/src/main/java/seedu/library/storage/Storage.java @@ -0,0 +1,39 @@ +package seedu.library.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends LibraryStorage, UserPrefsStorage, TagsStorage { + + @Override + Optional<UserPrefs> readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getLibraryFilePath(); + + @Override + Optional<ReadOnlyLibrary> readLibrary() throws DataConversionException, IOException; + + @Override + void saveLibrary(ReadOnlyLibrary library) throws IOException; + + @Override + Optional<ReadOnlyTags> readTags() throws DataConversionException, IOException; + + @Override + void saveTags(ReadOnlyTags tags) throws IOException; + +} diff --git a/src/main/java/seedu/library/storage/StorageManager.java b/src/main/java/seedu/library/storage/StorageManager.java new file mode 100644 index 00000000000..d71c89c6a36 --- /dev/null +++ b/src/main/java/seedu/library/storage/StorageManager.java @@ -0,0 +1,112 @@ +package seedu.library.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.UserPrefs; + +/** + * Manages storage of Library data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private LibraryStorage libraryStorage; + private UserPrefsStorage userPrefsStorage; + private TagsStorage tagsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code LibraryStorage} and {@code UserPrefStorage}. + */ + public StorageManager(LibraryStorage libraryStorage, + UserPrefsStorage userPrefsStorage, + TagsStorage tagsStorage) { + this.libraryStorage = libraryStorage; + this.userPrefsStorage = userPrefsStorage; + this.tagsStorage = tagsStorage; + } + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional<UserPrefs> readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ Library methods ============================== + + @Override + public Path getLibraryFilePath() { + return libraryStorage.getLibraryFilePath(); + } + + @Override + public Optional<ReadOnlyLibrary> readLibrary() throws DataConversionException, IOException { + return readLibrary(libraryStorage.getLibraryFilePath()); + } + + @Override + public Optional<ReadOnlyLibrary> readLibrary(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return libraryStorage.readLibrary(filePath); + } + + @Override + public void saveLibrary(ReadOnlyLibrary library) throws IOException { + saveLibrary(library, libraryStorage.getLibraryFilePath()); + } + + @Override + public void saveLibrary(ReadOnlyLibrary library, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + libraryStorage.saveLibrary(library, filePath); + } + + // ================ Tags methods ============================== + + @Override + public Path getTagsFilePath() { + return tagsStorage.getTagsFilePath(); + } + + @Override + public Optional<ReadOnlyTags> readTags() throws DataConversionException, IOException { + return readTags(tagsStorage.getTagsFilePath()); + } + + @Override + public Optional<ReadOnlyTags> readTags(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return tagsStorage.readTags(filePath); + } + + @Override + public void saveTags(ReadOnlyTags tags) throws IOException { + saveTags(tags, tagsStorage.getTagsFilePath()); + } + + @Override + public void saveTags(ReadOnlyTags tags, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + tagsStorage.saveTags(tags, filePath); + } + +} diff --git a/src/main/java/seedu/library/storage/TagsStorage.java b/src/main/java/seedu/library/storage/TagsStorage.java new file mode 100644 index 00000000000..6b29d2edf41 --- /dev/null +++ b/src/main/java/seedu/library/storage/TagsStorage.java @@ -0,0 +1,44 @@ +package seedu.library.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.Tags; + +/** + * Represents a storage for {@link Tags}. + */ +public interface TagsStorage { + /** + * Returns the file path of the data file. + */ + Path getTagsFilePath(); + + /** + * Returns Tags data as a {@link ReadOnlyTags}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional<ReadOnlyTags> readTags() throws DataConversionException, IOException; + + /** + * @see #getTagsFilePath() + */ + Optional<ReadOnlyTags> readTags(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTags} to the storage. + * @param tags cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTags(ReadOnlyTags tags) throws IOException; + + /** + * @see #saveTags(ReadOnlyTags) + */ + void saveTags(ReadOnlyTags tags, Path filePath) throws IOException; +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/library/storage/UserPrefsStorage.java similarity index 73% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/library/storage/UserPrefsStorage.java index 29eef178dbc..0ccd45de77a 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/library/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.library.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.library.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional<UserPrefs> readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.library.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/library/ui/BookmarkCard.java b/src/main/java/seedu/library/ui/BookmarkCard.java new file mode 100644 index 00000000000..6eaca389dab --- /dev/null +++ b/src/main/java/seedu/library/ui/BookmarkCard.java @@ -0,0 +1,161 @@ +package seedu.library.ui; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Rating; + +/** + * An UI component that displays information of a {@code Bookmark}. + */ +public class BookmarkCard extends UiPart<Region> { + + + private static final String FXML = "BookmarkListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see <a href="https://github.com/se-edu/Library-level4/issues/336">The issue on Library level 4</a> + */ + + public final Bookmark bookmark; + + @FXML + private HBox cardPane; + @FXML + private Label title; + @FXML + private Label id; + @FXML + private Label progress; + @FXML + private Label author; + @FXML + private Label genre; + @FXML + private FlowPane tags; + + @FXML + private Hyperlink link; + @FXML + private ImageView vol; + @FXML + private ImageView chap; + @FXML + private ImageView page; + @FXML + private Label chaptert; + @FXML + private Label paget; + @FXML + private ImageView rateHead; + @FXML + private ImageView ratingI; + + + + + /** + * Creates a {@code BookmarkCode} with the given {@code Bookmark} and index to display. + */ + public BookmarkCard(Bookmark bookmark, int displayedIndex) throws IOException { + super(FXML); + InputStream svol = this.getClass().getResourceAsStream("/images/volume.png"); + InputStream schap = this.getClass().getResourceAsStream("/images/chapter.png"); + InputStream spage = this.getClass().getResourceAsStream("/images/page.png"); + InputStream ratingImage = this.getClass().getResourceAsStream("/images/ratingHead.png"); + this.bookmark = bookmark; + id.setText(displayedIndex + ". "); + title.setText(bookmark.getTitle().value); + + if (bookmark.getProgress() != null) { + progress.setText(bookmark.getProgress().getVolume()); + chaptert.setText(bookmark.getProgress().getChapter()); + paget.setText(bookmark.getProgress().getPage()); + } else { + progress.setText("~"); + chaptert.setText("~"); + paget.setText("~"); + } + + vol.setImage(new Image(svol)); + chap.setImage(new Image(schap)); + page.setImage(new Image(spage)); + + if (bookmark.getAuthor() != null) { + author.setText(bookmark.getAuthor().value); + } else { + author.setText(""); + } + + genre.setText(bookmark.getGenre().value); + rateHead.setImage(new Image(ratingImage)); + rateCard(bookmark); + bookmark.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } + /** + * Helps set rating image in bookmarkcard + * @param bookmark + */ + public void rateCard(Bookmark bookmark) throws IOException { + InputStream rating0 = this.getClass().getResourceAsStream("/images/Rating0.png"); + InputStream rating1 = this.getClass().getResourceAsStream("/images/Rating1.png"); + InputStream rating2 = this.getClass().getResourceAsStream("/images/Rating2.png"); + InputStream rating3 = this.getClass().getResourceAsStream("/images/Rating3.png"); + InputStream rating4 = this.getClass().getResourceAsStream("/images/Rating4.png"); + InputStream rating5 = this.getClass().getResourceAsStream("/images/Rating5.png"); + + if (bookmark.getRating().equals(Rating.DEFAULT_RATING)) { + ratingI.setImage(new Image(rating0)); + return; + } + + String rating = bookmark.getRating().toString(); + + if (rating.equals("1")) { + ratingI.setImage(new Image(rating1)); + } else if (rating.equals("2")) { + ratingI.setImage(new Image(rating2)); + } else if (rating.equals("3")) { + ratingI.setImage(new Image(rating3)); + } else if (rating.equals("4")) { + ratingI.setImage(new Image(rating4)); + } else if (rating.equals("5")) { + ratingI.setImage(new Image(rating5)); + } + + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof BookmarkCard)) { + return false; + } + + // state check + BookmarkCard card = (BookmarkCard) other; + return id.getText().equals(card.id.getText()) + && bookmark.equals(card.bookmark); + } +} diff --git a/src/main/java/seedu/library/ui/BookmarkListPanel.java b/src/main/java/seedu/library/ui/BookmarkListPanel.java new file mode 100644 index 00000000000..00e6325b794 --- /dev/null +++ b/src/main/java/seedu/library/ui/BookmarkListPanel.java @@ -0,0 +1,138 @@ +package seedu.library.ui; + +import java.io.IOException; +import java.util.logging.Logger; + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.library.commons.core.LogsCenter; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.util.SampleDataUtil; + + + +/** + * Panel containing the list of bookmarks. + */ +public class BookmarkListPanel extends UiPart<Region> { + private static final String FXML = "BookmarkListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(BookmarkListPanel.class); + + @FXML + private ListView<Bookmark> bookmarkListView; + private Bookmark selectedItem; + + private ObservableList<Bookmark> bookmarks; + private boolean flag; + private boolean isStartEmpty; + + + /** + * Creates a {@code BookmarkListPanel} with the given {@code ObservableList}. + */ + public BookmarkListPanel(ObservableList<Bookmark> bookmarkList) { + super(FXML); + bookmarks = bookmarkList; + bookmarkListView.setItems(bookmarkList); + bookmarkListView.setCellFactory(listView -> new BookmarkListViewCell()); + flag = false; + isStartEmpty = false; + bookmarkListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Bookmark>() { + @Override + public void changed(ObservableValue<? extends Bookmark> observable, Bookmark oldValue, Bookmark newValue) { + selectedItem = bookmarkListView.getSelectionModel().getSelectedItem(); + flag = true; + } + }); + + + } + + /** + * Method to get the Bookmark currently selected in bookmarklistpanel + */ + public Bookmark getSelectedItem() { + return selectedItem; + } + + /** + * Unselects the item in bookmarklistpanel + */ + public void unSelect() { + bookmarkListView.getSelectionModel().clearSelection(); + } + + /** + * selects the item in bookmarklistpanel + */ + public void select(int index) { + bookmarkListView.getSelectionModel().select(index); + } + + /** + * Return the flag which indicates if item selected has changed + */ + public boolean isChangedSelect() { + return flag; + } + + /** + * Method to get the first bookmark in bookmarklistpanel + */ + public Bookmark getFirstItem() { + try { + bookmarkListView.getSelectionModel().select(0); + flag = true; + return bookmarks.get(0); + } catch (IndexOutOfBoundsException e) { + // just a default bookmark change + isStartEmpty = true; + String[] sampleProgress = {"1", "32", "56"}; + return new Bookmark(new Title("Attack on Titans"), new Progress(sampleProgress), new Genre("Fantasy"), + new Author("Hajime Isayama"), new Rating("5"), new Url(""), + SampleDataUtil.getTagSet("friends")); + + } + + } + /** + * Return the flag which indicates if starting bookmarklistpanel is empty + */ + public boolean isStartEmpty() { + return isStartEmpty; + } + + + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Bookmark} using a {@code BookmarkCard}. + */ + class BookmarkListViewCell extends ListCell<Bookmark> { + @Override + protected void updateItem(Bookmark bookmark, boolean empty) { + super.updateItem(bookmark, empty); + try { + if (empty || bookmark == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new BookmarkCard(bookmark, getIndex() + 1).getRoot()); + } + } catch (IOException e) { + throw new AssertionError(e); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/library/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/library/ui/CommandBox.java index 9e75478664b..d99d2f86493 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/library/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.library.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.library.logic.commands.CommandResult; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.library.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/library/ui/HelpWindow.java similarity index 93% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/library/ui/HelpWindow.java index 3f16b2fcf26..2fe31485108 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/library/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import java.util.logging.Logger; @@ -8,14 +8,14 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.library.commons.core.LogsCenter; /** * Controller for a help page */ public class HelpWindow extends UiPart<Stage> { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay2223s2-cs2103t-t13-4.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/library/ui/MainWindow.java similarity index 57% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/library/ui/MainWindow.java index 9106c3aa6e5..383f2e08846 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/library/ui/MainWindow.java @@ -1,21 +1,26 @@ -package seedu.address.ui; +package seedu.library.ui; +import java.time.DayOfWeek; +import java.time.LocalDate; import java.util.logging.Logger; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.TextInputControl; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.library.commons.core.GuiSettings; +import seedu.library.commons.core.LogsCenter; +import seedu.library.logic.Logic; +import seedu.library.logic.commands.CommandResult; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.bookmark.Bookmark; /** * The Main Window. Provides the basic application layout containing @@ -31,10 +36,13 @@ public class MainWindow extends UiPart<Stage> { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private BookmarkListPanel bookmarkListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; + private ZoomView zoomView; + + @FXML private StackPane commandBoxPlaceholder; @@ -42,13 +50,20 @@ public class MainWindow extends UiPart<Stage> { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane bookmarkListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @FXML private StackPane statusbarPlaceholder; + @FXML + private StackPane zoomViewPlaceholder; + + @FXML + private Label date; + + /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. @@ -59,13 +74,19 @@ public MainWindow(Stage primaryStage, Logic logic) { // Set dependencies this.primaryStage = primaryStage; this.logic = logic; - // Configure the UI setWindowDefaultSize(logic.getGuiSettings()); - + // prevent from resizing smaller than specified width + primaryStage.widthProperty().addListener((o, oldValue, newValue) -> { + if (newValue.intValue() < 1000) { + primaryStage.setResizable(false); + primaryStage.setWidth(1000); + primaryStage.setResizable(true); + } + }); setAccelerators(); - helpWindow = new HelpWindow(); + } public Stage getPrimaryStage() { @@ -105,22 +126,38 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { } }); } - + void setDate() { + LocalDate today = LocalDate.now(); + DayOfWeek day = today.getDayOfWeek(); + date.setText(day.name()); + } /** * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); - + bookmarkListPanel = new BookmarkListPanel(logic.getFilteredBookmarkList()); + bookmarkListPanelPlaceholder.getChildren().add(bookmarkListPanel.getRoot()); + //update right panel when clicking different bookmarks + bookmarkListPanelPlaceholder.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> handleChange()); + //Prevent user from using arrow keys to change selection + bookmarkListPanelPlaceholder.addEventFilter(KeyEvent.KEY_PRESSED, event -> event.consume()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getLibraryFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); - CommandBox commandBox = new CommandBox(this::executeCommand); commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + zoomView = new ZoomView(bookmarkListPanel.getFirstItem()); + if (bookmarkListPanel.isStartEmpty()) { + zoomView.hideFields(); + } + zoomViewPlaceholder.getChildren().add(zoomView.getRoot()); + + + + + + } /** @@ -147,6 +184,22 @@ public void handleHelp() { } } + /** + * Event handler that triggers when the selected bookmark is possibly changed. + */ + public void handleChange() { + Bookmark selectedItem = bookmarkListPanel.getSelectedItem(); + if (selectedItem == null || !bookmarkListPanel.isChangedSelect()) { + handleUpdate(); + } else { + ZoomView newView = new ZoomView(selectedItem); + zoomViewPlaceholder.getChildren().add(newView.getRoot()); + if (zoomView.getIsGuiAction() || bookmarkListPanel.isChangedSelect()) { + resultDisplay.clearDisplay(); + } + } + } + void show() { primaryStage.show(); } @@ -163,17 +216,51 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + /** + * updates view on right panel when interacted with CLI or text command + */ + private void separateUpdate() { + Bookmark selectedBookmark = logic.getSelectedBookmark(); + int selectedIndex = logic.getSelectedIndex(); + if (selectedBookmark != null && selectedIndex != -1) { + //Update with bookmark information + zoomView = new ZoomView(selectedBookmark); + zoomViewPlaceholder.getChildren().add(zoomView.getRoot()); + } else { + zoomView = new ZoomView(); + zoomViewPlaceholder.getChildren().add(zoomView.getRoot()); + + } + } + + + /** + * updates view on right panel when interacted with mouse + */ + private void handleUpdate() { + Bookmark currentSelect = bookmarkListPanel.getSelectedItem(); + if (currentSelect == null) { + zoomView = new ZoomView(); + zoomViewPlaceholder.getChildren().add(zoomView.getRoot()); + } else { + bookmarkListPanel.unSelect(); + zoomView = new ZoomView(); + zoomViewPlaceholder.getChildren().add(zoomView.getRoot()); + } + } + + public BookmarkListPanel getBookmarkListPanel() { + return bookmarkListPanel; } /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.library.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { + CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); @@ -185,6 +272,14 @@ private CommandResult executeCommand(String commandText) throws CommandException if (commandResult.isExit()) { handleExit(); } + if (commandResult.isUpdate()) { + handleUpdate(); + } + int index = logic.getSelectedIndex(); + if (index != -1) { + bookmarkListPanel.select(index); + } + separateUpdate(); return commandResult; } catch (CommandException | ParseException e) { diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/library/ui/ResultDisplay.java similarity index 79% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/library/ui/ResultDisplay.java index 7d98e84eedf..15343838345 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/library/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import static java.util.Objects.requireNonNull; @@ -25,4 +25,11 @@ public void setFeedbackToUser(String feedbackToUser) { resultDisplay.setText(feedbackToUser); } + /** + * Method which cleats result display + */ + public void clearDisplay() { + resultDisplay.setText(""); + } + } diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/library/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/library/ui/StatusBarFooter.java index b577f829423..d998887cdd9 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/library/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/library/ui/Ui.java similarity index 86% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/library/ui/Ui.java index 17aa0b494fe..17ca8dc8abe 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/library/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/library/ui/UiManager.java similarity index 90% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/library/ui/UiManager.java index fdf024138bc..c03ac0aa275 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/library/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.library.MainApp; +import seedu.library.commons.core.LogsCenter; +import seedu.library.commons.util.StringUtil; +import seedu.library.logic.Logic; /** * The manager of the UI component. @@ -20,7 +20,7 @@ public class UiManager implements Ui { public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/library.png"; private Logic logic; private MainWindow mainWindow; @@ -43,6 +43,7 @@ public void start(Stage primaryStage) { mainWindow = new MainWindow(primaryStage, logic); mainWindow.show(); //This should be called before creating other UI parts mainWindow.fillInnerParts(); + mainWindow.setDate(); } catch (Throwable e) { logger.severe(StringUtil.getDetails(e)); diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/library/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/library/ui/UiPart.java index fc820e01a9c..38cd238dbf2 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/library/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.library.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/java/seedu/library/ui/ZoomView.java b/src/main/java/seedu/library/ui/ZoomView.java new file mode 100644 index 00000000000..5e9b48e4d72 --- /dev/null +++ b/src/main/java/seedu/library/ui/ZoomView.java @@ -0,0 +1,205 @@ +package seedu.library.ui; + +import java.awt.Desktop; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Url; + + +/** + * UI component that is responsible for displaying the details of a single bookmark. + */ +public class ZoomView extends UiPart<Region> { + private static final String NOT_SUPPORTED = "this function is not supported on your OS "; + private static final String FXML = "ZoomView.fxml"; + private Bookmark bookmark; + private boolean isGuiAction; + + @FXML + private Label viewTitle; + @FXML + private Label authorView; + @FXML + private Label genreView; + + @FXML + private FlowPane tagsView; + @FXML + private Label progressView; + @FXML + private Label zoomTag; + @FXML + private ImageView avatar; + @FXML + private Hyperlink urlLink; + @FXML + private Label urlView; + @FXML + private ImageView ratingStar; + @FXML + private Label labelHeader; + @FXML + private HBox hyperBox; + @FXML + private HBox authorBox; + + + /** + * Constructs a ZoomView that is empty + * + */ + public ZoomView() { + super(FXML); + InputStream image = this.getClass().getResourceAsStream("/images/default-avatar.png"); + avatar.setImage(new Image(image)); + hideFields(); + this.isGuiAction = false; + + } + + /** + * Constructs a ZoomView that displays the details of the provided bookmark. + * + * @param bookmark a single Bookmark object + */ + public ZoomView(Bookmark bookmark) { + super(FXML); + this.bookmark = bookmark; + this.isGuiAction = false; + viewTitle.setText("Title: " + bookmark.getTitle().value); + + String authorString = (bookmark.getAuthor() == null) ? "-" : bookmark.getAuthor().value; + authorView.setText("Author: " + authorString); + authorBox.setMinHeight(authorView.getMaxHeight()); + + genreView.setText("Genre: " + bookmark.getGenre().value); + + String progressString = (bookmark.getProgress() == null) ? "-" : bookmark.getProgress().toString(); + progressView.setText("Progress: " + progressString); + + Url url = bookmark.getUrl(); + String urlString = url.toString(); + if (urlString.equals("")) { + urlString = "-"; + } + urlLink.setText("Url: " + urlString); + hyperBox.setMinHeight(urlLink.getMaxHeight()); + + if (bookmark.getTags().size() == 0 ) { + zoomTag.setText("Tags: -"); + } else { + bookmark.getTags().stream().sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tagsView.getChildren().add(new Label(tag.tagName))); + } + + InputStream image = this.getClass().getResourceAsStream("/images/default-avatar.png"); + avatar.setImage(new Image(image)); + urlLink.setOnAction(e -> { + this.isGuiAction = true; + openLink(bookmark.getUrl().value); + }); + rate(bookmark); + + + } + + /** + * Open url in default browser + * @param url url to open + */ + public void openLink(String url) { + try { + URI targetUrl = URI.create(url); + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win") || os.contains("mac")) { + Desktop.getDesktop().browse(targetUrl); + } else { //Desktop package not supported in this system + if (os.contains("nix") || os.contains("nux")) { + Runtime runtime = Runtime.getRuntime(); + runtime.exec(String.format("xdg-open %s", url)); + } + } + } catch (IOException e) { + throw new AssertionError(e); + } + } + + + /** + * Helps set rating image in bookmarkcard + * @param bookmark book to get rating value from + */ + public void rate(Bookmark bookmark) { + InputStream rating0 = this.getClass().getResourceAsStream("/images/Rating0.png"); + InputStream rating1 = this.getClass().getResourceAsStream("/images/Rating1.png"); + InputStream rating2 = this.getClass().getResourceAsStream("/images/Rating2.png"); + InputStream rating3 = this.getClass().getResourceAsStream("/images/Rating3.png"); + InputStream rating4 = this.getClass().getResourceAsStream("/images/Rating4.png"); + InputStream rating5 = this.getClass().getResourceAsStream("/images/Rating5.png"); + + if (bookmark.getRating().equals(Rating.DEFAULT_RATING)) { + ratingStar.setImage(new Image(rating0)); + ratingStar.setVisible(true); + return; + } + + String rating = bookmark.getRating().toString(); + + if (rating.equals("1")) { + ratingStar.setImage(new Image(rating1)); + ratingStar.setVisible(true); + } else if (rating.equals("2")) { + ratingStar.setImage(new Image(rating2)); + ratingStar.setVisible(true); + } else if (rating.equals("3")) { + ratingStar.setImage(new Image(rating3)); + ratingStar.setVisible(true); + } else if (rating.equals("4")) { + ratingStar.setImage(new Image(rating4)); + ratingStar.setVisible(true); + } else if (rating.equals("5")) { + ratingStar.setImage(new Image(rating5)); + ratingStar.setVisible(true); + } else { + ratingStar.setVisible(false); + } + + } + /** + * Method to get flag whether action is a gui interaction + */ + public boolean getIsGuiAction() { + return this.isGuiAction; + } + + /** + * Method to hide the contents of ZoomView + */ + public void hideFields() { + viewTitle.setVisible(false); + authorView.setVisible(false); + genreView.setVisible(false); + progressView.setVisible(false); + tagsView.setVisible(false); + zoomTag.setVisible(false); + urlLink.setVisible(false); + ratingStar.setVisible(false); + labelHeader.setVisible(false); + + } + + +} diff --git a/src/main/resources/images/Logo.png b/src/main/resources/images/Logo.png new file mode 100644 index 00000000000..0a7498b040d Binary files /dev/null and b/src/main/resources/images/Logo.png differ diff --git a/src/main/resources/images/Rating0.png b/src/main/resources/images/Rating0.png new file mode 100644 index 00000000000..76ebef63194 Binary files /dev/null and b/src/main/resources/images/Rating0.png differ diff --git a/src/main/resources/images/Rating1.png b/src/main/resources/images/Rating1.png new file mode 100644 index 00000000000..e2775789837 Binary files /dev/null and b/src/main/resources/images/Rating1.png differ diff --git a/src/main/resources/images/Rating2.png b/src/main/resources/images/Rating2.png new file mode 100644 index 00000000000..99ec5b41e22 Binary files /dev/null and b/src/main/resources/images/Rating2.png differ diff --git a/src/main/resources/images/Rating3.png b/src/main/resources/images/Rating3.png new file mode 100644 index 00000000000..bcbf09d037a Binary files /dev/null and b/src/main/resources/images/Rating3.png differ diff --git a/src/main/resources/images/Rating4.png b/src/main/resources/images/Rating4.png new file mode 100644 index 00000000000..2281e6f8219 Binary files /dev/null and b/src/main/resources/images/Rating4.png differ diff --git a/src/main/resources/images/Rating5.png b/src/main/resources/images/Rating5.png new file mode 100644 index 00000000000..cb0d552b33d Binary files /dev/null and b/src/main/resources/images/Rating5.png differ diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png old mode 100644 new mode 100755 diff --git a/src/main/resources/images/chapter.png b/src/main/resources/images/chapter.png new file mode 100644 index 00000000000..96c85c54c01 Binary files /dev/null and b/src/main/resources/images/chapter.png differ diff --git a/src/main/resources/images/default-avatar.png b/src/main/resources/images/default-avatar.png new file mode 100644 index 00000000000..413d3a21446 Binary files /dev/null and b/src/main/resources/images/default-avatar.png differ diff --git a/src/main/resources/images/library.png b/src/main/resources/images/library.png new file mode 100644 index 00000000000..b4a2b269776 Binary files /dev/null and b/src/main/resources/images/library.png differ diff --git a/src/main/resources/images/page.png b/src/main/resources/images/page.png new file mode 100644 index 00000000000..8e26c3a372f Binary files /dev/null and b/src/main/resources/images/page.png differ diff --git a/src/main/resources/images/ratingHead.png b/src/main/resources/images/ratingHead.png new file mode 100644 index 00000000000..b946022a023 Binary files /dev/null and b/src/main/resources/images/ratingHead.png differ diff --git a/src/main/resources/images/urokodaki.png b/src/main/resources/images/urokodaki.png new file mode 100644 index 00000000000..d3c6a7af768 Binary files /dev/null and b/src/main/resources/images/urokodaki.png differ diff --git a/src/main/resources/images/volume.png b/src/main/resources/images/volume.png new file mode 100644 index 00000000000..89906dec5eb Binary files /dev/null and b/src/main/resources/images/volume.png differ diff --git a/src/main/resources/view/BookmarkListCard.fxml b/src/main/resources/view/BookmarkListCard.fxml new file mode 100644 index 00000000000..920c9dcb3f2 --- /dev/null +++ b/src/main/resources/view/BookmarkListCard.fxml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.FlowPane?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Pane?> +<?import javafx.scene.layout.Region?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> + +<HBox id="cardPane" fx:id="cardPane" prefWidth="168.0" style="-fx-background-radius: 5; -fx-border-radius: 5;" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> + <GridPane prefWidth="237.0" style="-fx-background-radius: 5; -fx-border-radius: 5;" HBox.hgrow="ALWAYS"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10" prefWidth="150" /> + </columnConstraints> + <VBox alignment="CENTER_LEFT" minHeight="105" prefWidth="192.0" GridPane.columnIndex="0"> + <padding> + <Insets bottom="5" left="15" right="5" top="5" /> + </padding> + <HBox alignment="CENTER_LEFT" prefHeight="10.0" prefWidth="370.0" spacing="5" style="-fx-padding: 5,5,0,0;"> + <VBox maxHeight="1.7976931348623157E308" prefWidth="200.0"> + <children> + <HBox prefWidth="154.0"> + <children> + <Label fx:id="id" styleClass="cell_big_label"> + <minWidth> + <!-- Ensures that the label text is never truncated --> + <Region fx:constant="USE_PREF_SIZE" /> + </minWidth> + </Label> + <Label fx:id="title" styleClass="cell_big_label" text="\$first" /> + </children> + </HBox> + <Label fx:id="author" alignment="CENTER_LEFT" maxHeight="500.0" maxWidth="200.0" minWidth="200.0" prefHeight="16.0" prefWidth="460.0" style="-fx-font-size: 11; -fx-font-family: SE;" text="\$author" wrapText="true" /> + </children> + </VBox> + <Pane prefHeight="150.0" prefWidth="0.0" HBox.hgrow="ALWAYS"> + <HBox.margin> + <Insets /> + </HBox.margin> + </Pane> + <VBox> + <children> + <HBox alignment="CENTER_LEFT" prefHeight="123.0" prefWidth="0.0"> + <children> + <ImageView fx:id="vol" fitHeight="14.0" fitWidth="20.0" pickOnBounds="true" preserveRatio="true" translateX="75.0" /> + <Label fx:id="progress" minWidth="30.0" prefWidth="30.0" styleClass="cell_small_label" text="\\$progress" translateX="75.0" visible="true" /> + <ImageView fx:id="chap" fitHeight="14.0" fitWidth="20.0" pickOnBounds="true" preserveRatio="true" translateX="72.0" /> + <Label fx:id="chaptert" minWidth="30.0" prefWidth="30.0" styleClass="cell_small_label" text="Label" translateX="75.0" /> + <ImageView fx:id="page" fitHeight="14.0" fitWidth="20.0" pickOnBounds="true" preserveRatio="true" translateX="75.0" /> + <Label fx:id="paget" minWidth="30.0" prefWidth="30.0" styleClass="cell_small_label" text="Label" translateX="75.0" /> + </children> + </HBox> + <HBox prefHeight="100.0" prefWidth="200.0"> + <children> + <ImageView fx:id="rateHead" fitHeight="19.0" fitWidth="22.0" pickOnBounds="true" preserveRatio="true" translateX="75.0" translateY="15.0" /> + <ImageView fx:id="ratingI" fitHeight="18.0" fitWidth="122.0" pickOnBounds="true" preserveRatio="true" translateX="77.0" translateY="15.0" /> + </children> + </HBox> + </children> + </VBox> + </HBox> + <Label fx:id="genre" style="-fx-label-padding: 1 3 1 3; -fx-border-radius: 2; -fx-background-radius: 2; -fx-font-size: 11; -fx-background-color: #10E07A; back: 0.9;" styleClass="cell_small_label" text="\$genre" wrapText="true"> + <font> + <Font size="11.0" /> + </font></Label> + <FlowPane prefHeight="5.0" prefWidth="130.0" prefWrapLength="100.0" /> + <FlowPane fx:id="tags" alignment="CENTER_LEFT" prefWidth="128.0" rowValignment="BASELINE" /> + </VBox> + <rowConstraints> + <RowConstraints /> + </rowConstraints> + </GridPane> +</HBox> diff --git a/src/main/resources/view/BookmarkListPanel.fxml b/src/main/resources/view/BookmarkListPanel.fxml new file mode 100644 index 00000000000..7b46966c8d0 --- /dev/null +++ b/src/main/resources/view/BookmarkListPanel.fxml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.ListView?> +<?import javafx.scene.layout.VBox?> + +<VBox style="-fx-background-radius: 5; -fx-border-radius: 5; -fx-padding: 5;" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> + <ListView fx:id="bookmarkListView" style="-fx-background-radius: 20; -fx-border-radius: 20; -fx-padding: 20; -fx-background-color: #F4F6F8; -fx-border-color: black;" VBox.vgrow="ALWAYS" /> +</VBox> diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 09f6d6fe9e4..9b2a8a69e53 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -3,7 +3,6 @@ <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.StackPane?> -<StackPane styleClass="stack-pane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <TextField fx:id="commandTextField" onAction="#handleCommandEntered" promptText="Enter command here..."/> +<StackPane style="-fx-background-radius: 10; -fx-border-radius: 10; -fx-background-color: black; -fx-border-color: black;" styleClass="stack-pane" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> + <TextField fx:id="commandTextField" onAction="#handleCommandEntered" promptText="Enter command here..." style="-fx-background-radius: 10; -fx-border-radius: 10; -fx-border-color: black; -fx-background-color: #F4F6F8;" /> </StackPane> - diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..541c8c85b72 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -6,21 +6,22 @@ .label { -fx-font-size: 11pt; -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: #555555; - -fx-opacity: 0.9; + -fx-text-fill: black; + -fx-opacity: 1; + -fx-wrap-text: true; } .label-bright { -fx-font-size: 11pt; -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-opacity: 1; } .label-header { -fx-font-size: 32pt; -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-opacity: 1; } @@ -57,10 +58,10 @@ -fx-border-width: 0 0 1 0; -fx-background-color: transparent; -fx-border-color: - transparent - transparent - derive(-fx-base, 80%) - transparent; + transparent + transparent + derive(-fx-base, 80%) + transparent; -fx-border-insets: 0 10 1 0; } @@ -97,18 +98,21 @@ -fx-label-padding: 0 0 0 0; -fx-graphic-text-gap : 0; -fx-padding: 0 0 0 0; + -fx-background-radius: 5; + -fx-border-radius: 5; + -fx-background-color:#F4F6F8; } .list-cell:filled:even { - -fx-background-color: #3c3e3f; + -fx-background-color: #F4F6F8; } .list-cell:filled:odd { - -fx-background-color: #515658; + -fx-background-color: #F0F0F0; } .list-cell:filled:selected { - -fx-background-color: #424d5f; + -fx-background-color: #b1dde8; } .list-cell:filled:selected #cardPane { @@ -117,40 +121,40 @@ } .list-cell .label { - -fx-text-fill: white; + -fx-text-fill: black; } .cell_big_label { -fx-font-family: "Segoe UI Semibold"; -fx-font-size: 16px; - -fx-text-fill: #010504; + -fx-text-fill: black; } .cell_small_label { -fx-font-family: "Segoe UI"; -fx-font-size: 13px; - -fx-text-fill: #010504; + -fx-text-fill: black; } .stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#1d1d1d, 20%); } .pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; + -fx-background-color: #FDF6E3; + -fx-border-color: #FDF6E3; + -fx-border-top-width: 1px; } .status-bar { - -fx-background-color: derive(#1d1d1d, 30%); + -fx-background-color: #F1E4C2; } .result-display { - -fx-background-color: transparent; + -fx-background-color: #FDF6E3 ; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: black; } .result-display .label { @@ -159,7 +163,7 @@ .status-bar .label { -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-padding: 4px; -fx-pref-height: 30px; } @@ -171,7 +175,7 @@ } .status-bar-with-border .label { - -fx-text-fill: white; + -fx-text-fill: black; } .grid-pane { @@ -189,17 +193,17 @@ } .context-menu .label { - -fx-text-fill: white; + -fx-text-fill: black; } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: #F1E4C2; } .menu-bar .label { -fx-font-size: 14pt; -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-opacity: 0.9; } @@ -212,25 +216,14 @@ * Author: Pedro Duque Vieira * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/ */ -.button { - -fx-padding: 5 22 5 22; - -fx-border-color: #e2e2e2; - -fx-border-width: 2; - -fx-background-radius: 0; - -fx-background-color: #1d1d1d; - -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; - -fx-font-size: 11pt; - -fx-text-fill: #d8d8d8; - -fx-background-insets: 0 0 0 0, 0, 1, 2; -} .button:hover { -fx-background-color: #3a3a3a; } .button:pressed, .button:default:hover:pressed { - -fx-background-color: white; - -fx-text-fill: #1d1d1d; + -fx-background-color: white; + -fx-text-fill: #1d1d1d; } .button:focused { @@ -242,9 +235,9 @@ } .button:disabled, .button:default:disabled { - -fx-opacity: 0.4; + -fx-opacity: 1; -fx-background-color: #1d1d1d; - -fx-text-fill: white; + -fx-text-fill: black; } .button:default { @@ -267,7 +260,7 @@ .dialog-pane > *.label.content { -fx-font-size: 14px; -fx-font-weight: bold; - -fx-text-fill: white; + -fx-text-fill: black; } .dialog-pane:header *.header-panel { @@ -282,7 +275,7 @@ } .scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: #F4F6F8; } .scroll-bar .thumb { @@ -307,6 +300,7 @@ -fx-padding: 8 1 8 1; } + #cardPane { -fx-background-color: transparent; -fx-border-width: 0; @@ -323,30 +317,46 @@ -fx-border-color: #383838 #383838 #ffffff #383838; -fx-border-insets: 0; -fx-border-width: 1; - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Segoe UI "; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: black; } -#filterField, #personListPanel, #personWebpage { +#filterField, #BookmarkListPanel, #BookmarkWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } #resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; + -fx-background-color: #F4F6F8; -fx-background-radius: 0; } + #tags { -fx-hgap: 7; -fx-vgap: 3; } #tags .label { - -fx-text-fill: white; - -fx-background-color: #3e7b91; + -fx-text-fill: black; + -fx-background-color: #F7A05C; + -fx-opacity: 0.9; -fx-padding: 1 3 1 3; -fx-border-radius: 2; -fx-background-radius: 2; -fx-font-size: 11; } +#tagsView { + -fx-hgap: 7; + -fx-vgap: 3; +} + +#tagsView .label { + -fx-text-fill: black; + -fx-background-color: #F7A05C; + -fx-opacity: 0.9; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 15; +} diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index 5dea0adef70..b68c2cd285b 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -10,35 +10,35 @@ <?import javafx.stage.Stage?> <fx:root resizable="false" title="Help" type="javafx.stage.Stage" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> - <icons> - <Image url="@/images/help_icon.png" /> - </icons> - <scene> - <Scene> - <stylesheets> - <URL value="@HelpWindow.css" /> - </stylesheets> + <icons> + <Image url="@/images/help_icon.png" /> + </icons> + <scene> + <Scene> + <stylesheets> + <URL value="@HelpWindow.css" /> + </stylesheets> - <HBox alignment="CENTER" fx:id="helpMessageContainer"> - <children> - <Label fx:id="helpMessage" text="Label"> - <HBox.margin> - <Insets right="5.0" /> - </HBox.margin> - </Label> - <Button fx:id="copyButton" mnemonicParsing="false" onAction="#copyUrl" text="Copy URL"> - <HBox.margin> - <Insets left="5.0" /> - </HBox.margin> - </Button> - </children> - <opaqueInsets> - <Insets bottom="10.0" left="5.0" right="10.0" top="5.0" /> - </opaqueInsets> - <padding> - <Insets bottom="10.0" left="5.0" right="10.0" top="5.0" /> - </padding> - </HBox> - </Scene> - </scene> + <HBox alignment="CENTER" fx:id="helpMessageContainer"> + <children> + <Label fx:id="helpMessage" text="Label"> + <HBox.margin> + <Insets right="5.0" /> + </HBox.margin> + </Label> + <Button fx:id="copyButton" mnemonicParsing="false" onAction="#copyUrl" text="Copy URL"> + <HBox.margin> + <Insets left="5.0" /> + </HBox.margin> + </Button> + </children> + <opaqueInsets> + <Insets bottom="10.0" left="5.0" right="10.0" top="5.0" /> + </opaqueInsets> + <padding> + <Insets bottom="10.0" left="5.0" right="10.0" top="5.0" /> + </padding> + </HBox> + </Scene> + </scene> </fx:root> diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..a57c4e18571 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -3,58 +3,74 @@ <?import java.net.URL?> <?import javafx.geometry.Insets?> <?import javafx.scene.Scene?> +<?import javafx.scene.control.Label?> <?import javafx.scene.control.Menu?> <?import javafx.scene.control.MenuBar?> <?import javafx.scene.control.MenuItem?> -<?import javafx.scene.control.SplitPane?> <?import javafx.scene.image.Image?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.VBox?> +<?import javafx.stage.Stage?> -<fx:root type="javafx.stage.Stage" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" - title="Address App" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> - <icons> - <Image url="@/images/address_book_32.png" /> - </icons> - <scene> - <Scene> - <stylesheets> - <URL value="@DarkTheme.css" /> - <URL value="@Extensions.css" /> - </stylesheets> - - <VBox> - <MenuBar fx:id="menuBar" VBox.vgrow="NEVER"> - <Menu mnemonicParsing="false" text="File"> - <MenuItem mnemonicParsing="false" onAction="#handleExit" text="Exit" /> - </Menu> - <Menu mnemonicParsing="false" text="Help"> - <MenuItem fx:id="helpMenuItem" mnemonicParsing="false" onAction="#handleHelp" text="Help" /> - </Menu> - </MenuBar> - - <StackPane VBox.vgrow="NEVER" fx:id="commandBoxPlaceholder" styleClass="pane-with-border"> - <padding> - <Insets top="5" right="10" bottom="5" left="10" /> - </padding> - </StackPane> - - <StackPane VBox.vgrow="NEVER" fx:id="resultDisplayPlaceholder" styleClass="pane-with-border" - minHeight="100" prefHeight="100" maxHeight="100"> - <padding> - <Insets top="5" right="10" bottom="5" left="10" /> - </padding> - </StackPane> - - <VBox fx:id="personList" styleClass="pane-with-border" minWidth="340" prefWidth="340" VBox.vgrow="ALWAYS"> - <padding> - <Insets top="10" right="10" bottom="10" left="10" /> - </padding> - <StackPane fx:id="personListPanelPlaceholder" VBox.vgrow="ALWAYS"/> - </VBox> - - <StackPane fx:id="statusbarPlaceholder" VBox.vgrow="NEVER" /> - </VBox> - </Scene> - </scene> +<fx:root minHeight="600" minWidth="450" onCloseRequest="#handleExit" title="MyLib" type="javafx.stage.Stage" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> + <icons> + <Image url="@/images/library.png" /> + </icons> + <scene> + <Scene> + <stylesheets> + <URL value="@DarkTheme.css" /> + <URL value="@Extensions.css" /> + </stylesheets> + + <VBox> + <HBox style="-fx-background-color: #F1E4C2;" VBox.vgrow="NEVER"> + <children> + <MenuBar fx:id="menuBar"> + <Menu mnemonicParsing="false" text="File"> + <MenuItem mnemonicParsing="false" onAction="#handleExit" text="Exit" /> + </Menu> + <Menu mnemonicParsing="false" text="Help"> + <MenuItem fx:id="helpMenuItem" mnemonicParsing="false" onAction="#handleHelp" text="Help" /> + </Menu> + + </MenuBar> + <Pane style="-fx-background-color: #F1E4C2;" HBox.hgrow="ALWAYS" /> + <Label fx:id="date" alignment="TOP_RIGHT" minWidth="100.0" prefHeight="21.0" prefWidth="100.0" style="-fx-text-fill: black;" text="temporary" textFill="WHITE" translateX="-5.0" translateY="7.0" wrapText="true" /> + </children> + </HBox> + + <StackPane fx:id="commandBoxPlaceholder" styleClass="pane-with-border" VBox.vgrow="NEVER"> + <padding> + <Insets bottom="5" left="10" right="10" top="5" /> + </padding> + </StackPane> + + <StackPane fx:id="resultDisplayPlaceholder" maxHeight="115.0" minHeight="100" prefHeight="100.0" prefWidth="340.0" style="-fx-background-color: #FDF6E3;" styleClass="pane-with-border" VBox.vgrow="NEVER"> + <padding> + <Insets bottom="5" left="10" right="10" top="5" /> + </padding> + </StackPane> + + <VBox fx:id="BookmarkList" minWidth="340" prefWidth="340" styleClass="pane-with-border" VBox.vgrow="ALWAYS"> + <padding> + <Insets bottom="10" left="10" right="10" top="10" /> + </padding> + <HBox VBox.vgrow="ALWAYS"> + <children> + <StackPane fx:id="bookmarkListPanelPlaceholder" prefHeight="150.0" prefWidth="665.0" style="-fx-padding: 5,0,0,0;" /> + <StackPane fx:id="zoomViewPlaceholder" alignment="CENTER_RIGHT" prefHeight="150.0" prefWidth="650.0" HBox.hgrow="ALWAYS"> + <HBox.margin> + <Insets /> + </HBox.margin></StackPane> + </children> + </HBox> + </VBox> + + <StackPane fx:id="statusbarPlaceholder" alignment="TOP_LEFT" prefHeight="0.0" prefWidth="340.0" VBox.vgrow="NEVER" /> + </VBox> + </Scene> + </scene> </fx:root> diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index f08ea32ad55..00000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.geometry.Insets?> -<?import javafx.scene.control.Label?> -<?import javafx.scene.layout.ColumnConstraints?> -<?import javafx.scene.layout.FlowPane?> -<?import javafx.scene.layout.GridPane?> -<?import javafx.scene.layout.HBox?> -<?import javafx.scene.layout.Region?> -<?import javafx.scene.layout.VBox?> - -<HBox id="cardPane" fx:id="cardPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <GridPane HBox.hgrow="ALWAYS"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10" prefWidth="150" /> - </columnConstraints> - <VBox alignment="CENTER_LEFT" minHeight="105" GridPane.columnIndex="0"> - <padding> - <Insets top="5" right="5" bottom="5" left="15" /> - </padding> - <HBox spacing="5" alignment="CENTER_LEFT"> - <Label fx:id="id" styleClass="cell_big_label"> - <minWidth> - <!-- Ensures that the label text is never truncated --> - <Region fx:constant="USE_PREF_SIZE" /> - </minWidth> - </Label> - <Label fx:id="name" text="\$first" styleClass="cell_big_label" /> - </HBox> - <FlowPane fx:id="tags" /> - <Label fx:id="phone" styleClass="cell_small_label" text="\$phone" /> - <Label fx:id="address" styleClass="cell_small_label" text="\$address" /> - <Label fx:id="email" styleClass="cell_small_label" text="\$email" /> - </VBox> - </GridPane> -</HBox> diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml deleted file mode 100644 index 8836d323cc5..00000000000 --- a/src/main/resources/view/PersonListPanel.fxml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.scene.control.ListView?> -<?import javafx.scene.layout.VBox?> - -<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> - <ListView fx:id="personListView" VBox.vgrow="ALWAYS" /> -</VBox> diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..c43f79e03b5 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -3,7 +3,6 @@ <?import javafx.scene.control.TextArea?> <?import javafx.scene.layout.StackPane?> -<StackPane fx:id="placeHolder" styleClass="pane-with-border" xmlns="http://javafx.com/javafx/8" - xmlns:fx="http://javafx.com/fxml/1"> - <TextArea fx:id="resultDisplay" editable="false" styleClass="result-display"/> +<StackPane fx:id="placeHolder" style="-fx-background-color: #FDF6E3; -fx-background: #FDF6E3; -fx-border-radius: 5; -fx-border-color: black; -fx-border-width: 3;" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> + <TextArea fx:id="resultDisplay" editable="false" styleClass="result-display" /> </StackPane> diff --git a/src/main/resources/view/StatusBarFooter.fxml b/src/main/resources/view/StatusBarFooter.fxml index 149f62bd29c..919c82a59a7 100644 --- a/src/main/resources/view/StatusBarFooter.fxml +++ b/src/main/resources/view/StatusBarFooter.fxml @@ -4,7 +4,7 @@ <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> -<GridPane styleClass="status-bar" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> +<GridPane styleClass="status-bar" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10" /> </columnConstraints> diff --git a/src/main/resources/view/ZoomView.fxml b/src/main/resources/view/ZoomView.fxml new file mode 100644 index 00000000000..b2f93742ce7 --- /dev/null +++ b/src/main/resources/view/ZoomView.fxml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Hyperlink?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ScrollPane?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.FlowPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Pane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> + +<ScrollPane fitToHeight="true" fitToWidth="true" style="-fx-background: #FDF6E3; -fx-padding: 10,0,0,0; -fx-background-color: #FDF6E3;" xmlns="http://javafx.com/javafx/11" xmlns:fx="http://javafx.com/fxml/1"> + <content> + <VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="340.0" prefWidth="570.0" style="-fx-background-color: #F4F6F8; -fx-background-radius: 10; -fx-border-radius: 10; -fx-border-color: black;"> + <children> + <HBox fx:id="zoomed" maxWidth="1.7976931348623157E308"> + <children> + <Label fx:id="viewTitle" minHeight="-Infinity" style="-fx-font-family: 'Britannic Bold'; -fx-font-size: 30; -fx-text-fill: black;" text="Label" translateX="12.0" translateY="15.0" wrapText="true"/> + </children> + </HBox> + <HBox fx:id="authorBox" minHeight="29.0" prefWidth="555.0" maxWidth="1.7976931348623157E308"> + <children> + <Label fx:id="authorView" minHeight="-Infinity" style="-fx-font-family: 'Arial'; -fx-font-size: 15; -fx-text-fill: black;" text="Label" translateX="18.0" translateY="17.0" wrapText="true" /> + </children> + </HBox> + <HBox minHeight="35.0" prefWidth="630.0"> + <children> + <Label fx:id="progressView" minHeight="-Infinity" style="-fx-font-family: 'Arial'; -fx-font-size: 15; -fx-text-fill: black;" text="Label" translateX="18.0" translateY="20" wrapText="true" /> + </children> + </HBox> + <HBox fx:id="hyperBox" minHeight="23.0" prefHeight="23.0" prefWidth="568.0" nodeOrientation="LEFT_TO_RIGHT"> + <children> + <Hyperlink fx:id="urlLink" minHeight="-Infinity" style="-fx-text-fill: Black; -fx-font-size: 15;" text="Hyperlink" translateX="12.0" translateY="4" wrapText="true"> + <font> + <Font size="14.0" /> + </font> + </Hyperlink> + </children> + </HBox> + <HBox minHeight="29.0" prefHeight="29.0" prefWidth="568.0"> + <children> + <Label fx:id="genreView" minHeight="-Infinity" style="-fx-font-family: 'Arial'; -fx-font-size: 15; -fx-text-fill: black;" text="Label" translateX="17.0" translateY="12.0" wrapText="true" /> + </children> + </HBox> + <HBox> + <children> + <Label fx:id="zoomTag" minHeight="-Infinity" maxWidth="47.0" minWidth="47.0" prefWidth="47.0" style="-fx-font-family: 'Arial'; -fx-font-size: 15; -fx-text-fill: black;" text="Tags:" translateX="17.0" translateY="5.0" wrapText="true" /> + <FlowPane fx:id="tagsView" prefHeight="10.0" prefWidth="600.0" translateX="10.0" translateY="2.0" /> + </children> + </HBox> + <HBox prefHeight="100.0" prefWidth="555.0"> + <children> + <Label fx:id="labelHeader" minHeight="-Infinity" style="-fx-font-family: Arial; -fx-font-size: 15;" text="Rating:" translateX="17.0" translateY="10.0" wrapText="true" /> + <ImageView fx:id="ratingStar" fitHeight="20.0" fitWidth="190.0" pickOnBounds="true" preserveRatio="true" translateX="20.0" translateY="9.0" /> + </children> + </HBox> + <Pane prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" /> + <HBox fx:id="center" alignment="CENTER_RIGHT" prefHeight="100.0" prefWidth="150.0" VBox.vgrow="NEVER"> + <children> + <ImageView fx:id="avatar" fitHeight="208.0" fitWidth="163.0" pickOnBounds="true" preserveRatio="true" style="-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0);;" /> + </children> + </HBox> + </children> + </VBox> + </content> +</ScrollPane> diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json deleted file mode 100644 index 6a4d2b7181c..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "persons": [ { - "name": "Valid Person", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - }, { - "name": "Person With Invalid Phone Field", - "phone": "948asdf2424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json deleted file mode 100644 index ccd21f7d1a9..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Person with invalid name field: Ha!ns Mu@ster", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonLibraryStorageTest/invalidAndValidBookmarkLibrary.json b/src/test/data/JsonLibraryStorageTest/invalidAndValidBookmarkLibrary.json new file mode 100644 index 00000000000..5d3fbdd2cae --- /dev/null +++ b/src/test/data/JsonLibraryStorageTest/invalidAndValidBookmarkLibrary.json @@ -0,0 +1,13 @@ +{ + "bookmarks": [ { + "title": "Valid Bookmark", + "phone": "9482424", + "genre": "Action", + "author": "4th street" + }, { + "title": "Bookmark With Invalid Phone Field", + "phone": "948asdf2424", + "genre": "Fantasy", + "author": "4th street" + } ] +} diff --git a/src/test/data/JsonLibraryStorageTest/invalidBookmarkLibrary.json b/src/test/data/JsonLibraryStorageTest/invalidBookmarkLibrary.json new file mode 100644 index 00000000000..9d71d6f85c0 --- /dev/null +++ b/src/test/data/JsonLibraryStorageTest/invalidBookmarkLibrary.json @@ -0,0 +1,8 @@ +{ + "bookmarks": [ { + "title": "Bookmark with invalid name field: Ha!ns Mu@ster", + "phone": "9482424", + "genre": "Action", + "author": "4th street" + } ] +} diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonLibraryStorageTest/notJsonFormatLibrary.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json rename to src/test/data/JsonLibraryStorageTest/notJsonFormatLibrary.json diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json deleted file mode 100644 index 48831cc7674..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "persons": [ { - "name": "Alice Pauline", - "phone": "94351253", - "email": "alice@example.com", - "address": "123, Jurong West Ave 6, #08-111", - "tagged": [ "friends" ] - }, { - "name": "Alice Pauline", - "phone": "94351253", - "email": "pauline@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json deleted file mode 100644 index ad3f135ae42..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Hans Muster", - "phone": "9482424", - "email": "invalid@email!3e", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json deleted file mode 100644 index f10eddee12e..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()", - "persons" : [ { - "name" : "Alice Pauline", - "phone" : "94351253", - "email" : "alice@example.com", - "address" : "123, Jurong West Ave 6, #08-111", - "tagged" : [ "friends" ] - }, { - "name" : "Benson Meier", - "phone" : "98765432", - "email" : "johnd@example.com", - "address" : "311, Clementi Ave 2, #02-25", - "tagged" : [ "owesMoney", "friends" ] - }, { - "name" : "Carl Kurz", - "phone" : "95352563", - "email" : "heinz@example.com", - "address" : "wall street", - "tagged" : [ ] - }, { - "name" : "Daniel Meier", - "phone" : "87652533", - "email" : "cornelia@example.com", - "address" : "10th street", - "tagged" : [ "friends" ] - }, { - "name" : "Elle Meyer", - "phone" : "9482224", - "email" : "werner@example.com", - "address" : "michegan ave", - "tagged" : [ ] - }, { - "name" : "Fiona Kunz", - "phone" : "9482427", - "email" : "lydia@example.com", - "address" : "little tokyo", - "tagged" : [ ] - }, { - "name" : "George Best", - "phone" : "9482442", - "email" : "anna@example.com", - "address" : "4th street", - "tagged" : [ ] - } ] -} diff --git a/src/test/data/JsonSerializableLibraryTest/duplicateBookmarkLibrary.json b/src/test/data/JsonSerializableLibraryTest/duplicateBookmarkLibrary.json new file mode 100644 index 00000000000..c74ffd70680 --- /dev/null +++ b/src/test/data/JsonSerializableLibraryTest/duplicateBookmarkLibrary.json @@ -0,0 +1,26 @@ +{ + "bookmarks": [ { + "title": "Alice Pauline", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Action", + "author": "123, Jurong West Ave 6, #08-111", + "url": "", + "tagged": [ { + "tag": "friends" + } ] + }, { + "title": "Alice Pauline", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Others", + "author": "4th street", + "url": "" + } ] +} diff --git a/src/test/data/JsonSerializableLibraryTest/invalidBookmarkLibrary.json b/src/test/data/JsonSerializableLibraryTest/invalidBookmarkLibrary.json new file mode 100644 index 00000000000..789427ef107 --- /dev/null +++ b/src/test/data/JsonSerializableLibraryTest/invalidBookmarkLibrary.json @@ -0,0 +1,12 @@ +{ + "bookmarks": [ { + "title": "Hans Muster", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": " ", + "author": "4th street" + } ] +} diff --git a/src/test/data/JsonSerializableLibraryTest/typicalBookmarksLibrary.json b/src/test/data/JsonSerializableLibraryTest/typicalBookmarksLibrary.json new file mode 100644 index 00000000000..fb41edbc6d8 --- /dev/null +++ b/src/test/data/JsonSerializableLibraryTest/typicalBookmarksLibrary.json @@ -0,0 +1,89 @@ +{ + "_comment": "Library save file which contains the same Bookmark values as in TypicalBookmarks#getTypicalLibrary()", + "bookmarks": [ { + "title": "Attack on Titans", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Action", + "author": "Hajime Isayama", + "url": "https://aotmanga.com/attack-on-titan/chapter-139/", + "tagged" : [ { + "tag": "MaleProtagonist" + }] + }, { + "title": "Benson Meier", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Adventure", + "author": "311, Clementi Ave 2, #02-25", + "url": "", + "tagged" : [ { + "tag": "owesMoney" + }, { + "tag": "friends" + }] + }, { + "title": "Carl Kurz", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Comedy", + "author": "wall street", + "url": "", + "tagged" : [ ] + }, { + "title": "Daniel Meier", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Drama", + "author": "10th street", + "url": "", + "tagged" : [ { + "tag": "friends" + } ] + }, { + "title": "Elle Meyer", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Fantasy", + "author": "michegan ave", + "url": "", + "tagged" : [ ] + }, { + "title": "Fiona Kunz", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Historical", + "author": "little tokyo", + "url": "", + "tagged" : [ ] + }, { + "title": "George Best", + "progress": { + "volume": "1", + "chapter": "2", + "page": "~" + }, + "genre": "Horror", + "author": "4th street", + "url": "", + "tagged" : [ ] + } ] +} diff --git a/src/test/data/JsonSerializableLibraryTest/typicalTagList.json b/src/test/data/JsonSerializableLibraryTest/typicalTagList.json new file mode 100644 index 00000000000..4e4e3fb5d00 --- /dev/null +++ b/src/test/data/JsonSerializableLibraryTest/typicalTagList.json @@ -0,0 +1,7 @@ +{ + "tagList" : [ { + "tag" : "friends" + }, { + "tag" : "owesMoney" + } ] +} diff --git a/src/test/data/JsonTagsTest/duplicateTags.json b/src/test/data/JsonTagsTest/duplicateTags.json new file mode 100644 index 00000000000..be28144409d --- /dev/null +++ b/src/test/data/JsonTagsTest/duplicateTags.json @@ -0,0 +1,23 @@ +{ + "tagList" : [ { + "tag" : "MaleProtagonist" + }, { + "tag" : "MaleProtagonist" + }, { + "tag" : "Manhua" + }, { + "tag" : "Hunters" + }, { + "tag" : "Cheats" + }, { + "tag" : "System" + }, { + "tag" : "Antihero" + }, { + "tag" : "Literature" + }, { + "tag" : "School" + }, { + "tag" : "Gore" + }] +} diff --git a/src/test/data/JsonTagsTest/invalidTags.json b/src/test/data/JsonTagsTest/invalidTags.json new file mode 100644 index 00000000000..ff36a19e4b0 --- /dev/null +++ b/src/test/data/JsonTagsTest/invalidTags.json @@ -0,0 +1,23 @@ +{ + "tagList" : [ { + "tag" : "MaleProtagonist" + }, { + "tag" : "Manhwa#@" + }, { + "tag" : "Manhua" + }, { + "tag" : "Hunters" + }, { + "tag" : "Cheats" + }, { + "tag" : "System" + }, { + "tag" : "Antihero" + }, { + "tag" : "Literature" + }, { + "tag" : "School" + }, { + "tag" : "Gore" + }] +} diff --git a/src/test/data/JsonTagsTest/notJsonFormatTags.json b/src/test/data/JsonTagsTest/notJsonFormatTags.json new file mode 100644 index 00000000000..a1097343b5d --- /dev/null +++ b/src/test/data/JsonTagsTest/notJsonFormatTags.json @@ -0,0 +1 @@ +not json format! diff --git a/src/test/data/JsonTagsTest/typicalTags.json b/src/test/data/JsonTagsTest/typicalTags.json new file mode 100644 index 00000000000..ea44d5af98c --- /dev/null +++ b/src/test/data/JsonTagsTest/typicalTags.json @@ -0,0 +1,21 @@ +{ + "tagList" : [ { + "tag" : "MaleProtagonist" + }, { + "tag" : "Manhwa" + }, { + "tag" : "Hunters" + }, { + "tag" : "Cheats" + }, { + "tag" : "System" + }, { + "tag" : "Antihero" + }, { + "tag" : "Literature" + }, { + "tag" : "School" + }, { + "tag" : "Gore" + }] +} diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index 1037548a9cd..ea4e0640692 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,5 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.json" + "libraryFilePath" : "library.json" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..c71ce9e223e 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "libraryFilePath" : "library.json" } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java deleted file mode 100644 index cb8714bb055..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) for {@code AddCommand}. - */ -public class AddCommandIntegrationTest { - - private Model model; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - } - - @Test - public void execute_newPerson_success() { - Person validPerson = new PersonBuilder().build(); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.addPerson(validPerson); - - assertCommandSuccess(new AddCommand(validPerson), model, - String.format(AddCommand.MESSAGE_SUCCESS, validPerson), expectedModel); - } - - @Test - public void execute_duplicatePerson_throwsCommandException() { - Person personInList = model.getAddressBook().getPersonList().get(0); - assertCommandFailure(new AddCommand(personInList), model, AddCommand.MESSAGE_DUPLICATE_PERSON); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java deleted file mode 100644 index 5865713d5dd..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.function.Predicate; - -import org.junit.jupiter.api.Test; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandTest { - - @Test - public void constructor_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new AddCommand(null)); - } - - @Test - public void execute_personAcceptedByModel_addSuccessful() throws Exception { - ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); - Person validPerson = new PersonBuilder().build(); - - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); - - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); - assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); - } - - @Test - public void execute_duplicatePerson_throwsCommandException() { - Person validPerson = new PersonBuilder().build(); - AddCommand addCommand = new AddCommand(validPerson); - ModelStub modelStub = new ModelStubWithPerson(validPerson); - - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); - } - - @Test - public void equals() { - Person alice = new PersonBuilder().withName("Alice").build(); - Person bob = new PersonBuilder().withName("Bob").build(); - AddCommand addAliceCommand = new AddCommand(alice); - AddCommand addBobCommand = new AddCommand(bob); - - // same object -> returns true - assertTrue(addAliceCommand.equals(addAliceCommand)); - - // same values -> returns true - AddCommand addAliceCommandCopy = new AddCommand(alice); - assertTrue(addAliceCommand.equals(addAliceCommandCopy)); - - // different types -> returns false - assertFalse(addAliceCommand.equals(1)); - - // null -> returns false - assertFalse(addAliceCommand.equals(null)); - - // different person -> returns false - assertFalse(addAliceCommand.equals(addBobCommand)); - } - - /** - * A default model stub that have all of the methods failing. - */ - private class ModelStub implements Model { - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - throw new AssertionError("This method should not be called."); - } - - @Override - public GuiSettings getGuiSettings() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - throw new AssertionError("This method should not be called."); - } - - @Override - public Path getAddressBookFilePath() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void addPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBook(ReadOnlyAddressBook newData) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - throw new AssertionError("This method should not be called."); - } - - @Override - public boolean hasPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void deletePerson(Person target) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ObservableList<Person> getFilteredPersonList() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void updateFilteredPersonList(Predicate<Person> predicate) { - throw new AssertionError("This method should not be called."); - } - } - - /** - * A Model stub that contains a single person. - */ - private class ModelStubWithPerson extends ModelStub { - private final Person person; - - ModelStubWithPerson(Person person) { - requireNonNull(person); - this.person = person; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return this.person.isSamePerson(person); - } - } - - /** - * A Model stub that always accept the person being added. - */ - private class ModelStubAcceptingPersonAdded extends ModelStub { - final ArrayList<Person> personsAdded = new ArrayList<>(); - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return personsAdded.stream().anyMatch(person::isSamePerson); - } - - @Override - public void addPerson(Person person) { - requireNonNull(person); - personsAdded.add(person); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); - } - } - -} diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java deleted file mode 100644 index 80d9110c03a..00000000000 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -public class ClearCommandTest { - - @Test - public void execute_emptyAddressBook_success() { - Model model = new ModelManager(); - Model expectedModel = new ModelManager(); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_nonEmptyAddressBook_success() { - Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel.setAddressBook(new AddressBook()); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java deleted file mode 100644 index 643a1d08069..00000000000 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ /dev/null @@ -1,128 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.testutil.Assert.assertThrows; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -/** - * Contains helper methods for testing commands. - */ -public class CommandTestUtil { - - public static final String VALID_NAME_AMY = "Amy Bee"; - public static final String VALID_NAME_BOB = "Bob Choo"; - public static final String VALID_PHONE_AMY = "11111111"; - public static final String VALID_PHONE_BOB = "22222222"; - public static final String VALID_EMAIL_AMY = "amy@example.com"; - public static final String VALID_EMAIL_BOB = "bob@example.com"; - public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; - public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; - public static final String VALID_TAG_HUSBAND = "husband"; - public static final String VALID_TAG_FRIEND = "friend"; - - public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; - public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; - public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; - public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; - public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; - public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; - public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY; - public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB; - public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; - public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - - public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names - public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones - public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol - public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses - public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags - - public static final String PREAMBLE_WHITESPACE = "\t \r \n"; - public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; - - public static final EditCommand.EditPersonDescriptor DESC_AMY; - public static final EditCommand.EditPersonDescriptor DESC_BOB; - - static { - DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_FRIEND).build(); - DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - } - - /** - * Executes the given {@code command}, confirms that <br> - * - the returned {@link CommandResult} matches {@code expectedCommandResult} <br> - * - the {@code actualModel} matches {@code expectedModel} - */ - public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, - Model expectedModel) { - try { - CommandResult result = command.execute(actualModel); - assertEquals(expectedCommandResult, result); - assertEquals(expectedModel, actualModel); - } catch (CommandException ce) { - throw new AssertionError("Execution of command should not fail.", ce); - } - } - - /** - * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} - * that takes a string {@code expectedMessage}. - */ - public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, - Model expectedModel) { - CommandResult expectedCommandResult = new CommandResult(expectedMessage); - assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); - } - - /** - * Executes the given {@code command}, confirms that <br> - * - a {@code CommandException} is thrown <br> - * - the CommandException message matches {@code expectedMessage} <br> - * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged - */ - public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { - // we are unable to defensively copy the model for comparison later, so we can - // only do so by copying its components. - AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); - List<Person> expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); - - assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); - assertEquals(expectedAddressBook, actualModel.getAddressBook()); - assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); - } - /** - * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the - * {@code model}'s address book. - */ - public static void showPersonAtIndex(Model model, Index targetIndex) { - assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); - - Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); - final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); - - assertEquals(1, model.getFilteredPersonList().size()); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java deleted file mode 100644 index 45a8c910ba1..00000000000 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; - -/** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code DeleteCommand}. - */ -public class DeleteCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexUnfilteredList_throwsCommandException() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void execute_validIndexFilteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - showNoPerson(expectedModel); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexFilteredList_throwsCommandException() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON); - DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON); - - // same object -> returns true - assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); - - // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); - assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); - - // different types -> returns false - assertFalse(deleteFirstCommand.equals(1)); - - // null -> returns false - assertFalse(deleteFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); - } - - /** - * Updates {@code model}'s filtered list to show no one. - */ - private void showNoPerson(Model model) { - model.updateFilteredPersonList(p -> false); - - assertTrue(model.getFilteredPersonList().isEmpty()); - } -} diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java deleted file mode 100644 index 214c6c2507b..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) and unit tests for EditCommand. - */ -public class EditCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_allFieldsSpecifiedUnfilteredList_success() { - Person editedPerson = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_someFieldsSpecifiedUnfilteredList_success() { - Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size()); - Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased()); - - PersonBuilder personInList = new PersonBuilder(lastPerson); - Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withTags(VALID_TAG_HUSBAND).build(); - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(lastPerson, editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); - Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_filteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_duplicatePersonUnfilteredList_failure() { - Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_duplicatePersonFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder(personInList).build()); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_invalidPersonIndexUnfilteredList_failure() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - /** - * Edit filtered list where index is larger than size of filtered list, - * but smaller than size of address book - */ - @Test - public void execute_invalidPersonIndexFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - - EditCommand editCommand = new EditCommand(outOfBoundIndex, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); - - // same values -> returns true - EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor); - assertTrue(standardCommand.equals(commandWithSameValues)); - - // same object -> returns true - assertTrue(standardCommand.equals(standardCommand)); - - // null -> returns false - assertFalse(standardCommand.equals(null)); - - // different types -> returns false - assertFalse(standardCommand.equals(new ClearCommand())); - - // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); - - // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB))); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java deleted file mode 100644 index e0288792e72..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditPersonDescriptorTest { - - @Test - public void equals() { - // same values -> returns true - EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); - assertTrue(DESC_AMY.equals(descriptorWithSameValues)); - - // same object -> returns true - assertTrue(DESC_AMY.equals(DESC_AMY)); - - // null -> returns false - assertFalse(DESC_AMY.equals(null)); - - // different types -> returns false - assertFalse(DESC_AMY.equals(5)); - - // different values -> returns false - assertFalse(DESC_AMY.equals(DESC_BOB)); - - // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different phone -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different email -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - } -} diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java deleted file mode 100644 index 9b15db28bbb..00000000000 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.CARL; -import static seedu.address.testutil.TypicalPersons.ELLE; -import static seedu.address.testutil.TypicalPersons.FIONA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Contains integration tests (interaction with the Model) for {@code FindCommand}. - */ -public class FindCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void equals() { - NameContainsKeywordsPredicate firstPredicate = - new NameContainsKeywordsPredicate(Collections.singletonList("first")); - NameContainsKeywordsPredicate secondPredicate = - new NameContainsKeywordsPredicate(Collections.singletonList("second")); - - FindCommand findFirstCommand = new FindCommand(firstPredicate); - FindCommand findSecondCommand = new FindCommand(secondPredicate); - - // same object -> returns true - assertTrue(findFirstCommand.equals(findFirstCommand)); - - // same values -> returns true - FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); - assertTrue(findFirstCommand.equals(findFirstCommandCopy)); - - // different types -> returns false - assertFalse(findFirstCommand.equals(1)); - - // null -> returns false - assertFalse(findFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(findFirstCommand.equals(findSecondCommand)); - } - - @Test - public void execute_zeroKeywords_noPersonFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); - NameContainsKeywordsPredicate predicate = preparePredicate(" "); - FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); - assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Collections.emptyList(), model.getFilteredPersonList()); - } - - @Test - public void execute_multipleKeywords_multiplePersonsFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); - NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz"); - FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); - assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList()); - } - - /** - * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}. - */ - private NameContainsKeywordsPredicate preparePredicate(String userInput) { - return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); - } -} diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java deleted file mode 100644 index 435ff1f7275..00000000000 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -/** - * Contains integration tests (interaction with the Model) and unit tests for ListCommand. - */ -public class ListCommandTest { - - private Model model; - private Model expectedModel; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - } - - @Test - public void execute_listIsNotFiltered_showsSameList() { - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java deleted file mode 100644 index 5cf487d7ebb..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); - - @Test - public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); - - // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple names - last name accepted - assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple phones - last phone accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple emails - last email accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple addresses - last address accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple tags - all accepted - Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedPersonMultipleTags)); - } - - @Test - public void parse_optionalFieldsMissing_success() { - // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); - } - - @Test - public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - - // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, - expectedMessage); - - // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, - expectedMessage); - } - - @Test - public void parse_invalidValue_failure() { - // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); - - // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); - - // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); - - // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); - - // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, - Name.MESSAGE_CONSTRAINTS); - - // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java deleted file mode 100644 index 2ff31522486..00000000000 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditCommandParserTest { - - private static final String TAG_EMPTY = " " + PREFIX_TAG; - - private static final String MESSAGE_INVALID_FORMAT = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); - - private EditCommandParser parser = new EditCommandParser(); - - @Test - public void parse_missingParts_failure() { - // no index specified - assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); - - // no field specified - assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); - - // no index and no field specified - assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidPreamble_failure() { - // negative index - assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // zero index - assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // invalid arguments being parsed as preamble - assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); - - // invalid prefix being parsed as preamble - assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidValue_failure() { - assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name - assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email - assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag - - // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); - - // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone - // is tested at {@code parse_invalidValueFollowedByValidValue_success()} - assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); - - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, - // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - - // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, - Name.MESSAGE_CONSTRAINTS); - } - - @Test - public void parse_allFieldsSpecified_success() { - Index targetIndex = INDEX_SECOND_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND - + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_someFieldsSpecified_success() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_oneFieldSpecified_success() { - // name - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // phone - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // email - userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // address - userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // tags - userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; - descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_multipleRepeatedFields_acceptsLast() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_invalidValueFollowedByValidValue_success() { - // no other valid values specified - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // other valid values specified - userInput = targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB - + PHONE_DESC_BOB; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_resetTags_success() { - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + TAG_EMPTY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } -} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java deleted file mode 100644 index 70f4f0e79c4..00000000000 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; - -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -public class FindCommandParserTest { - - private FindCommandParser parser = new FindCommandParser(); - - @Test - public void parse_emptyArg_throwsParseException() { - assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - @Test - public void parse_validArgs_returnsFindCommand() { - // no leading and trailing whitespaces - FindCommand expectedFindCommand = - new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"))); - assertParseSuccess(parser, "Alice Bob", expectedFindCommand); - - // multiple whitespaces between keywords - assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand); - } - -} diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java deleted file mode 100644 index 4256788b1a7..00000000000 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ /dev/null @@ -1,196 +0,0 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -public class ParserUtilTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = "Rachel Walker"; - private static final String VALID_PHONE = "123456"; - private static final String VALID_ADDRESS = "123 Main Street #0505"; - private static final String VALID_EMAIL = "rachel@example.com"; - private static final String VALID_TAG_1 = "friend"; - private static final String VALID_TAG_2 = "neighbour"; - - private static final String WHITESPACE = " \t\r\n"; - - @Test - public void parseIndex_invalidInput_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); - } - - @Test - public void parseIndex_outOfRangeInput_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () - -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); - } - - @Test - public void parseIndex_validInput_success() throws Exception { - // No whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); - - // Leading and trailing whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); - } - - @Test - public void parseName_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); - } - - @Test - public void parseName_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME)); - } - - @Test - public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); - } - - @Test - public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { - String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); - } - - @Test - public void parsePhone_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); - } - - @Test - public void parsePhone_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { - String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace)); - } - - @Test - public void parseAddress_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null)); - } - - @Test - public void parseAddress_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception { - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception { - String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE; - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); - } - - @Test - public void parseEmail_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); - } - - @Test - public void parseEmail_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { - String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); - } - - @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); - } - - @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); - } - - @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); - } - - @Test - public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { - String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); - } - - @Test - public void parseTags_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); - } - - @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); - } - - @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); - } - - @Test - public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { - Set<Tag> actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set<Tag> expectedTagSet = new HashSet<Tag>(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); - - assertEquals(expectedTagSet, actualTagSet); - } -} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java deleted file mode 100644 index 87782528ecd..00000000000 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; - -public class AddressBookTest { - - private final AddressBook addressBook = new AddressBook(); - - @Test - public void constructor() { - assertEquals(Collections.emptyList(), addressBook.getPersonList()); - } - - @Test - public void resetData_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.resetData(null)); - } - - @Test - public void resetData_withValidReadOnlyAddressBook_replacesData() { - AddressBook newData = getTypicalAddressBook(); - addressBook.resetData(newData); - assertEquals(newData, addressBook); - } - - @Test - public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { - // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - List<Person> newPersons = Arrays.asList(ALICE, editedAlice); - AddressBookStub newData = new AddressBookStub(newPersons); - - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - assertTrue(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(addressBook.hasPerson(editedAlice)); - } - - @Test - public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); - } - - /** - * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. - */ - private static class AddressBookStub implements ReadOnlyAddressBook { - private final ObservableList<Person> persons = FXCollections.observableArrayList(); - - AddressBookStub(Collection<Person> persons) { - this.persons.setAll(persons); - } - - @Override - public ObservableList<Person> getPersonList() { - return persons; - } - } - -} diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java deleted file mode 100644 index 2cf1418d116..00000000000 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; - -public class ModelManagerTest { - - private ModelManager modelManager = new ModelManager(); - - @Test - public void constructor() { - assertEquals(new UserPrefs(), modelManager.getUserPrefs()); - assertEquals(new GuiSettings(), modelManager.getGuiSettings()); - assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); - } - - @Test - public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null)); - } - - @Test - public void setUserPrefs_validUserPrefs_copiesUserPrefs() { - UserPrefs userPrefs = new UserPrefs(); - userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); - userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); - modelManager.setUserPrefs(userPrefs); - assertEquals(userPrefs, modelManager.getUserPrefs()); - - // Modifying userPrefs should not modify modelManager's userPrefs - UserPrefs oldUserPrefs = new UserPrefs(userPrefs); - userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path")); - assertEquals(oldUserPrefs, modelManager.getUserPrefs()); - } - - @Test - public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setGuiSettings(null)); - } - - @Test - public void setGuiSettings_validGuiSettings_setsGuiSettings() { - GuiSettings guiSettings = new GuiSettings(1, 2, 3, 4); - modelManager.setGuiSettings(guiSettings); - assertEquals(guiSettings, modelManager.getGuiSettings()); - } - - @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); - } - - @Test - public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { - Path path = Paths.get("address/book/file/path"); - modelManager.setAddressBookFilePath(path); - assertEquals(path, modelManager.getAddressBookFilePath()); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(modelManager.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - modelManager.addPerson(ALICE); - assertTrue(modelManager.hasPerson(ALICE)); - } - - @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); - } - - @Test - public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); - AddressBook differentAddressBook = new AddressBook(); - UserPrefs userPrefs = new UserPrefs(); - - // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); - assertTrue(modelManager.equals(modelManagerCopy)); - - // same object -> returns true - assertTrue(modelManager.equals(modelManager)); - - // null -> returns false - assertFalse(modelManager.equals(null)); - - // different types -> returns false - assertFalse(modelManager.equals(5)); - - // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); - - // different filteredList -> returns false - String[] keywords = ALICE.getName().fullName.split("\\s+"); - modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); - assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); - - // resets modelManager to initial state for upcoming tests - modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - - // different userPrefs -> returns false - UserPrefs differentUserPrefs = new UserPrefs(); - differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); - assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); - } -} diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java deleted file mode 100644 index dcd3be87b3a..00000000000 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class AddressTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Address(null)); - } - - @Test - public void constructor_invalidAddress_throwsIllegalArgumentException() { - String invalidAddress = ""; - assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress)); - } - - @Test - public void isValidAddress() { - // null address - assertThrows(NullPointerException.class, () -> Address.isValidAddress(null)); - - // invalid addresses - assertFalse(Address.isValidAddress("")); // empty string - assertFalse(Address.isValidAddress(" ")); // spaces only - - // valid addresses - assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); - assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address - } -} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java deleted file mode 100644 index bbcc6c8c98e..00000000000 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class EmailTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Email(null)); - } - - @Test - public void constructor_invalidEmail_throwsIllegalArgumentException() { - String invalidEmail = ""; - assertThrows(IllegalArgumentException.class, () -> new Email(invalidEmail)); - } - - @Test - public void isValidEmail() { - // null email - assertThrows(NullPointerException.class, () -> Email.isValidEmail(null)); - - // blank email - assertFalse(Email.isValidEmail("")); // empty string - assertFalse(Email.isValidEmail(" ")); // spaces only - - // missing parts - assertFalse(Email.isValidEmail("@example.com")); // missing local part - assertFalse(Email.isValidEmail("peterjackexample.com")); // missing '@' symbol - assertFalse(Email.isValidEmail("peterjack@")); // missing domain name - - // invalid parts - assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name - assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name - assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part - assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name - assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space - assertFalse(Email.isValidEmail("peterjack@example.com ")); // trailing space - assertFalse(Email.isValidEmail("peterjack@@example.com")); // double '@' symbol - assertFalse(Email.isValidEmail("peter@jack@example.com")); // '@' symbol in local part - assertFalse(Email.isValidEmail("-peterjack@example.com")); // local part starts with a hyphen - assertFalse(Email.isValidEmail("peterjack-@example.com")); // local part ends with a hyphen - assertFalse(Email.isValidEmail("peter..jack@example.com")); // local part has two consecutive periods - assertFalse(Email.isValidEmail("peterjack@example@com")); // '@' symbol in domain name - assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a period - assertFalse(Email.isValidEmail("peterjack@example.com.")); // domain name ends with a period - assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars - - // valid email - assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part - assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part - assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part - assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part - assertTrue(Email.isValidEmail("a@bc")); // minimal - assertTrue(Email.isValidEmail("test@localhost")); // alphabets only - assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name - assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters - assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name - assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part - assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain - } -} diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java deleted file mode 100644 index f136664e017..00000000000 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class NameContainsKeywordsPredicateTest { - - @Test - public void equals() { - List<String> firstPredicateKeywordList = Collections.singletonList("first"); - List<String> secondPredicateKeywordList = Arrays.asList("first", "second"); - - NameContainsKeywordsPredicate firstPredicate = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - NameContainsKeywordsPredicate secondPredicate = new NameContainsKeywordsPredicate(secondPredicateKeywordList); - - // same object -> returns true - assertTrue(firstPredicate.equals(firstPredicate)); - - // same values -> returns true - NameContainsKeywordsPredicate firstPredicateCopy = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - assertTrue(firstPredicate.equals(firstPredicateCopy)); - - // different types -> returns false - assertFalse(firstPredicate.equals(1)); - - // null -> returns false - assertFalse(firstPredicate.equals(null)); - - // different person -> returns false - assertFalse(firstPredicate.equals(secondPredicate)); - } - - @Test - public void test_nameContainsKeywords_returnsTrue() { - // One keyword - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Multiple keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Only one matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build())); - - // Mixed-case keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - } - - @Test - public void test_nameDoesNotContainKeywords_returnsFalse() { - // Zero keywords - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList()); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); - - // Non-matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") - .withEmail("alice@email.com").withAddress("Main Street").build())); - } -} diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java deleted file mode 100644 index c9801392874..00000000000 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class NameTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Name(null)); - } - - @Test - public void constructor_invalidName_throwsIllegalArgumentException() { - String invalidName = ""; - assertThrows(IllegalArgumentException.class, () -> new Name(invalidName)); - } - - @Test - public void isValidName() { - // null name - assertThrows(NullPointerException.class, () -> Name.isValidName(null)); - - // invalid name - assertFalse(Name.isValidName("")); // empty string - assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters - assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters - - // valid name - assertTrue(Name.isValidName("peter jack")); // alphabets only - assertTrue(Name.isValidName("12345")); // numbers only - assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters - assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names - } -} diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java deleted file mode 100644 index b29c097cfd4..00000000000 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class PersonTest { - - @Test - public void asObservableList_modifyList_throwsUnsupportedOperationException() { - Person person = new PersonBuilder().build(); - assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0)); - } - - @Test - public void isSamePerson() { - // same object -> returns true - assertTrue(ALICE.isSamePerson(ALICE)); - - // null -> returns false - assertFalse(ALICE.isSamePerson(null)); - - // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); - assertTrue(ALICE.isSamePerson(editedAlice)); - - // different name, all other attributes same -> returns false - editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_BOB + " "; - editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); - } - - @Test - public void equals() { - // same values -> returns true - Person aliceCopy = new PersonBuilder(ALICE).build(); - assertTrue(ALICE.equals(aliceCopy)); - - // same object -> returns true - assertTrue(ALICE.equals(ALICE)); - - // null -> returns false - assertFalse(ALICE.equals(null)); - - // different type -> returns false - assertFalse(ALICE.equals(5)); - - // different person -> returns false - assertFalse(ALICE.equals(BOB)); - - // different name -> returns false - Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different phone -> returns false - editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different email -> returns false - editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(ALICE.equals(editedAlice)); - } -} diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java deleted file mode 100644 index 8dd52766a5f..00000000000 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class PhoneTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Phone(null)); - } - - @Test - public void constructor_invalidPhone_throwsIllegalArgumentException() { - String invalidPhone = ""; - assertThrows(IllegalArgumentException.class, () -> new Phone(invalidPhone)); - } - - @Test - public void isValidPhone() { - // null phone number - assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null)); - - // invalid phone numbers - assertFalse(Phone.isValidPhone("")); // empty string - assertFalse(Phone.isValidPhone(" ")); // spaces only - assertFalse(Phone.isValidPhone("91")); // less than 3 numbers - assertFalse(Phone.isValidPhone("phone")); // non-numeric - assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits - assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits - - // valid phone numbers - assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers - assertTrue(Phone.isValidPhone("93121534")); - assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers - } -} diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java deleted file mode 100644 index 1cc5fe9e0fe..00000000000 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; - -public class UniquePersonListTest { - - private final UniquePersonList uniquePersonList = new UniquePersonList(); - - @Test - public void contains_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); - } - - @Test - public void contains_personNotInList_returnsFalse() { - assertFalse(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personInList_returnsTrue() { - uniquePersonList.add(ALICE); - assertTrue(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personWithSameIdentityFieldsInList_returnsTrue() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(uniquePersonList.contains(editedAlice)); - } - - @Test - public void add_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); - } - - @Test - public void add_duplicatePerson_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); - } - - @Test - public void setPerson_nullTargetPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); - } - - @Test - public void setPerson_nullEditedPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); - } - - @Test - public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); - } - - @Test - public void setPerson_editedPersonIsSamePerson_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(ALICE); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasSameIdentity_success() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - uniquePersonList.setPerson(ALICE, editedAlice); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(editedAlice); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasDifferentIdentity_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, BOB); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); - } - - @Test - public void remove_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); - } - - @Test - public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); - } - - @Test - public void remove_existingPerson_removesPerson() { - uniquePersonList.add(ALICE); - uniquePersonList.remove(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); - } - - @Test - public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { - uniquePersonList.add(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - uniquePersonList.setPersons(expectedUniquePersonList); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List<Person>) null)); - } - - @Test - public void setPersons_list_replacesOwnListWithProvidedList() { - uniquePersonList.add(ALICE); - List<Person> personList = Collections.singletonList(BOB); - uniquePersonList.setPersons(personList); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { - List<Person> listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); - } - - @Test - public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java deleted file mode 100644 index 83b11331cdb..00000000000 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; - -public class JsonAdaptedPersonTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = BENSON.getName().toString(); - private static final String VALID_PHONE = BENSON.getPhone().toString(); - private static final String VALID_EMAIL = BENSON.getEmail().toString(); - private static final String VALID_ADDRESS = BENSON.getAddress().toString(); - private static final List<JsonAdaptedTag> VALID_TAGS = BENSON.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList()); - - @Test - public void toModelType_validPersonDetails_returnsPerson() throws Exception { - JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON); - assertEquals(BENSON, person.toModelType()); - } - - @Test - public void toModelType_invalidName_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Name.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Phone.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Email.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); - String expectedMessage = Address.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidTags_throwsIllegalValueException() { - List<JsonAdaptedTag> invalidTags = new ArrayList<>(VALID_TAGS); - invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); - assertThrows(IllegalValueException.class, person::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java deleted file mode 100644 index ac3c3af9566..00000000000 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; - -public class JsonAddressBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); - - @TempDir - public Path testFolder; - - @Test - public void readAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> readAddressBook(null)); - } - - private java.util.Optional<ReadOnlyAddressBook> readAddressBook(String filePath) throws Exception { - return new JsonAddressBookStorage(Paths.get(filePath)).readAddressBook(addToTestDataPathIfNotNull(filePath)); - } - - private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { - return prefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) - : null; - } - - @Test - public void read_missingFile_emptyResult() throws Exception { - assertFalse(readAddressBook("NonExistentFile.json").isPresent()); - } - - @Test - public void read_notJsonFormat_exceptionThrown() { - assertThrows(DataConversionException.class, () -> readAddressBook("notJsonFormatAddressBook.json")); - } - - @Test - public void readAddressBook_invalidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidPersonAddressBook.json")); - } - - @Test - public void readAddressBook_invalidAndValidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); - } - - @Test - public void readAndSaveAddressBook_allInOrder_success() throws Exception { - Path filePath = testFolder.resolve("TempAddressBook.json"); - AddressBook original = getTypicalAddressBook(); - JsonAddressBookStorage jsonAddressBookStorage = new JsonAddressBookStorage(filePath); - - // Save in new file and read back - jsonAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Modify data, overwrite exiting file, and read back - original.addPerson(HOON); - original.removePerson(ALICE); - jsonAddressBookStorage.saveAddressBook(original, filePath); - readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Save and read without specifying file path - original.addPerson(IDA); - jsonAddressBookStorage.saveAddressBook(original); // file path not specified - readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified - assertEquals(original, new AddressBook(readBack)); - - } - - @Test - public void saveAddressBook_nullAddressBook_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(null, "SomeFile.json")); - } - - /** - * Saves {@code addressBook} at the specified {@code filePath}. - */ - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) { - try { - new JsonAddressBookStorage(Paths.get(filePath)) - .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); - } catch (IOException ioe) { - throw new AssertionError("There should not be an error writing to the file.", ioe); - } - } - - @Test - public void saveAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(new AddressBook(), null)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java deleted file mode 100644 index 188c9058d20..00000000000 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; - -public class JsonSerializableAddressBookTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); - - @Test - public void toModelType_typicalPersonsFile_success() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, - JsonSerializableAddressBook.class).get(); - AddressBook addressBookFromFile = dataFromFile.toModelType(); - AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook(); - assertEquals(addressBookFromFile, typicalPersonsAddressBook); - } - - @Test - public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, dataFromFile::toModelType); - } - - @Test - public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, - dataFromFile::toModelType); - } - -} diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java deleted file mode 100644 index d53799fd110..00000000000 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class to help with building Addressbook objects. - * Example usage: <br> - * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} - */ -public class AddressBookBuilder { - - private AddressBook addressBook; - - public AddressBookBuilder() { - addressBook = new AddressBook(); - } - - public AddressBookBuilder(AddressBook addressBook) { - this.addressBook = addressBook; - } - - /** - * Adds a new {@code Person} to the {@code AddressBook} that we are building. - */ - public AddressBookBuilder withPerson(Person person) { - addressBook.addPerson(person); - return this; - } - - public AddressBook build() { - return addressBook; - } -} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java deleted file mode 100644 index 4584bd5044e..00000000000 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.testutil; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * A utility class to help with building EditPersonDescriptor objects. - */ -public class EditPersonDescriptorBuilder { - - private EditPersonDescriptor descriptor; - - public EditPersonDescriptorBuilder() { - descriptor = new EditPersonDescriptor(); - } - - public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) { - this.descriptor = new EditPersonDescriptor(descriptor); - } - - /** - * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details - */ - public EditPersonDescriptorBuilder(Person person) { - descriptor = new EditPersonDescriptor(); - descriptor.setName(person.getName()); - descriptor.setPhone(person.getPhone()); - descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); - } - - /** - * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withName(String name) { - descriptor.setName(new Name(name)); - return this; - } - - /** - * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withPhone(String phone) { - descriptor.setPhone(new Phone(phone)); - return this; - } - - /** - * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withEmail(String email) { - descriptor.setEmail(new Email(email)); - return this; - } - - /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set<Tag>} and set it to the {@code EditPersonDescriptor} - * that we are building. - */ - public EditPersonDescriptorBuilder withTags(String... tags) { - Set<Tag> tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); - descriptor.setTags(tagSet); - return this; - } - - public EditPersonDescriptor build() { - return descriptor; - } -} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 6be381d39ba..00000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -package seedu.address.testutil; - -import java.util.HashSet; -import java.util.Set; - -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; - -/** - * A utility class to help with building Person objects. - */ -public class PersonBuilder { - - public static final String DEFAULT_NAME = "Amy Bee"; - public static final String DEFAULT_PHONE = "85355255"; - public static final String DEFAULT_EMAIL = "amy@gmail.com"; - public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; - - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set<Tag> tags; - - /** - * Creates a {@code PersonBuilder} with the default details. - */ - public PersonBuilder() { - name = new Name(DEFAULT_NAME); - phone = new Phone(DEFAULT_PHONE); - email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); - } - - /** - * Initializes the PersonBuilder with the data of {@code personToCopy}. - */ - public PersonBuilder(Person personToCopy) { - name = personToCopy.getName(); - phone = personToCopy.getPhone(); - email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); - } - - /** - * Sets the {@code Name} of the {@code Person} that we are building. - */ - public PersonBuilder withName(String name) { - this.name = new Name(name); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set<Tag>} and set it to the {@code Person} that we are building. - */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); - return this; - } - - /** - * Sets the {@code Address} of the {@code Person} that we are building. - */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); - return this; - } - - /** - * Sets the {@code Phone} of the {@code Person} that we are building. - */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); - return this; - } - - /** - * Sets the {@code Email} of the {@code Person} that we are building. - */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); - return this; - } - - public Person build() { - return new Person(name, phone, email, address, tags); - } - -} diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java deleted file mode 100644 index 90849945183..00000000000 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; - -/** - * A utility class for Person. - */ -public class PersonUtil { - - /** - * Returns an add command string for adding the {@code person}. - */ - public static String getAddCommand(Person person) { - return AddCommand.COMMAND_WORD + " " + getPersonDetails(person); - } - - /** - * Returns the part of command string for the given {@code person}'s details. - */ - public static String getPersonDetails(Person person) { - StringBuilder sb = new StringBuilder(); - sb.append(PREFIX_NAME + person.getName().fullName + " "); - sb.append(PREFIX_PHONE + person.getPhone().value + " "); - sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); - return sb.toString(); - } - - /** - * Returns the part of command string for the given {@code EditPersonDescriptor}'s details. - */ - public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { - StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); - if (descriptor.getTags().isPresent()) { - Set<Tag> tags = descriptor.getTags().get(); - if (tags.isEmpty()) { - sb.append(PREFIX_TAG); - } else { - tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); - } - } - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java deleted file mode 100644 index 1e613937657..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.core.index.Index; - -/** - * A utility class containing a list of {@code Index} objects to be used in tests. - */ -public class TypicalIndexes { - public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); - public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); - public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); -} diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List<Person> getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/seedu/library/AppParametersTest.java similarity index 98% rename from src/test/java/seedu/address/AppParametersTest.java rename to src/test/java/seedu/library/AppParametersTest.java index 61326b2d31a..3d159e6756e 100644 --- a/src/test/java/seedu/address/AppParametersTest.java +++ b/src/test/java/seedu/library/AppParametersTest.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.library; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/seedu/library/commons/core/ConfigTest.java similarity index 71% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/seedu/library/commons/core/ConfigTest.java index 07cd7f73d53..f760de7ae38 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/seedu/library/commons/core/ConfigTest.java @@ -1,6 +1,7 @@ -package seedu.address.commons.core; +package seedu.library.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -23,5 +24,10 @@ public void equalsMethod() { assertTrue(defaultConfig.equals(defaultConfig)); } - + @Test + public void equalsMethodNull() { + Config defaultConfig = new Config(); + assertNotNull(defaultConfig); + assertFalse(defaultConfig.equals(null)); + } } diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/library/commons/core/VersionTest.java similarity index 98% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/library/commons/core/VersionTest.java index 495cd231554..5cf70487afd 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/library/commons/core/VersionTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.core; +package seedu.library.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/seedu/library/commons/core/index/IndexTest.java similarity index 73% rename from src/test/java/seedu/address/commons/core/index/IndexTest.java rename to src/test/java/seedu/library/commons/core/index/IndexTest.java index a3ec6f8e747..1a8bc6c8911 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/seedu/library/commons/core/index/IndexTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.core.index; +package seedu.library.commons.core.index; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -39,22 +39,22 @@ public void createZeroBasedIndex() { @Test public void equals() { - final Index fifthPersonIndex = Index.fromOneBased(5); + final Index fifthBookmarkIndex = Index.fromOneBased(5); // same values -> returns true - assertTrue(fifthPersonIndex.equals(Index.fromOneBased(5))); - assertTrue(fifthPersonIndex.equals(Index.fromZeroBased(4))); + assertTrue(fifthBookmarkIndex.equals(Index.fromOneBased(5))); + assertTrue(fifthBookmarkIndex.equals(Index.fromZeroBased(4))); // same object -> returns true - assertTrue(fifthPersonIndex.equals(fifthPersonIndex)); + assertTrue(fifthBookmarkIndex.equals(fifthBookmarkIndex)); // null -> returns false - assertFalse(fifthPersonIndex.equals(null)); + assertFalse(fifthBookmarkIndex.equals(null)); // different types -> returns false - assertFalse(fifthPersonIndex.equals(5.0f)); + assertFalse(fifthBookmarkIndex.equals(5.0f)); // different index -> returns false - assertFalse(fifthPersonIndex.equals(Index.fromOneBased(1))); + assertFalse(fifthBookmarkIndex.equals(Index.fromOneBased(1))); } } diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/library/commons/util/AppUtilTest.java similarity index 85% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/seedu/library/commons/util/AppUtilTest.java index 594de1e6365..07c2643f04b 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/library/commons/util/AppUtilTest.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -9,7 +9,7 @@ public class AppUtilTest { @Test public void getImage_exitingImage() { - assertNotNull(AppUtil.getImage("/images/address_book_32.png")); + assertNotNull(AppUtil.getImage("/images/library.png")); } @Test diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/seedu/library/commons/util/CollectionUtilTest.java similarity index 96% rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java rename to src/test/java/seedu/library/commons/util/CollectionUtilTest.java index b467a3dc025..36720116389 100644 --- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java +++ b/src/test/java/seedu/library/commons/util/CollectionUtilTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.library.testutil.Assert.assertThrows; import java.util.Arrays; import java.util.Collection; diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/library/commons/util/ConfigUtilTest.java similarity index 94% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/library/commons/util/ConfigUtilTest.java index d2ab2839a52..df4bfdde43b 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/library/commons/util/ConfigUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -13,8 +13,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.library.commons.core.Config; +import seedu.library.commons.exceptions.DataConversionException; public class ConfigUtilTest { diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/library/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/library/commons/util/FileUtilTest.java index 1fe5478c756..fc19de747d2 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/library/commons/util/FileUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/library/commons/util/JsonUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/seedu/library/commons/util/JsonUtilTest.java index d4907539dee..63588590522 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/seedu/library/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; +import seedu.library.testutil.SerializableTestClass; +import seedu.library.testutil.TestUtil; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/library/commons/util/StringUtilTest.java similarity index 98% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/library/commons/util/StringUtilTest.java index c56d407bf3f..342184373bb 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/library/commons/util/StringUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.library.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import java.io.FileNotFoundException; diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/library/logic/LogicManagerTest.java similarity index 61% rename from src/test/java/seedu/address/logic/LogicManagerTest.java rename to src/test/java/seedu/library/logic/LogicManagerTest.java index ad923ac249a..abff47c4c9b 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/library/logic/LogicManagerTest.java @@ -1,14 +1,14 @@ -package seedu.address.logic; +package seedu.library.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX; +import static seedu.library.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.library.logic.commands.CommandTestUtil.AUTHOR_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.GENRE_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.PROGRESS_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.TITLE_DESC_AMY; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.AMY; import java.io.IOException; import java.nio.file.Path; @@ -17,20 +17,21 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; +import seedu.library.logic.commands.AddCommand; +import seedu.library.logic.commands.CommandResult; +import seedu.library.logic.commands.ListCommand; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.UserPrefs; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.storage.JsonLibraryStorage; +import seedu.library.storage.JsonTagsStorage; +import seedu.library.storage.JsonUserPrefsStorage; +import seedu.library.storage.StorageManager; +import seedu.library.testutil.BookmarkBuilder; public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @@ -43,10 +44,11 @@ public class LogicManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); + JsonLibraryStorage libraryStorage = + new JsonLibraryStorage(temporaryFolder.resolve("library.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTagsStorage tagsStorage = new JsonTagsStorage(temporaryFolder.resolve("tags.json")); + StorageManager storage = new StorageManager(libraryStorage, userPrefsStorage, tagsStorage); logic = new LogicManager(model, storage); } @@ -59,7 +61,7 @@ public void execute_invalidCommandFormat_throwsParseException() { @Test public void execute_commandExecutionError_throwsCommandException() { String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandException(deleteCommand, MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); } @Test @@ -70,27 +72,29 @@ public void execute_validCommand_success() throws Exception { @Test public void execute_storageThrowsIoException_throwsCommandException() { - // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); + // Setup LogicManager with JsonLibraryIoExceptionThrowingStub + JsonLibraryStorage libraryStorage = + new JsonLibraryIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionLibrary.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTagsStorage tagsStorage = + new JsonTagsStorage(temporaryFolder.resolve("ioExceptionTags.json")); + StorageManager storage = new StorageManager(libraryStorage, userPrefsStorage, tagsStorage); logic = new LogicManager(model, storage); // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY - + ADDRESS_DESC_AMY; - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); + String addCommand = AddCommand.COMMAND_WORD + TITLE_DESC_AMY + PROGRESS_DESC_AMY + GENRE_DESC_AMY + + AUTHOR_DESC_AMY; + Bookmark expectedBookmark = new BookmarkBuilder(AMY).withTags().build(); ModelManager expectedModel = new ModelManager(); - expectedModel.addPerson(expectedPerson); + expectedModel.addBookmark(expectedBookmark); String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); + public void getFilteredBookmarkList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredBookmarkList().remove(0)); } /** @@ -129,7 +133,7 @@ private void assertCommandException(String inputCommand, String expectedMessage) */ private void assertCommandFailure(String inputCommand, Class<? extends Throwable> expectedException, String expectedMessage) { - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + Model expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), model.getTags()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } @@ -149,13 +153,13 @@ private void assertCommandFailure(String inputCommand, Class<? extends Throwable /** * A stub class to throw an {@code IOException} when the save method is called. */ - private static class JsonAddressBookIoExceptionThrowingStub extends JsonAddressBookStorage { - private JsonAddressBookIoExceptionThrowingStub(Path filePath) { + private static class JsonLibraryIoExceptionThrowingStub extends JsonLibraryStorage { + private JsonLibraryIoExceptionThrowingStub(Path filePath) { super(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveLibrary(ReadOnlyLibrary library, Path filePath) throws IOException { throw DUMMY_IO_EXCEPTION; } } diff --git a/src/test/java/seedu/library/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/library/logic/commands/AddCommandIntegrationTest.java new file mode 100644 index 00000000000..f8fc955d906 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/AddCommandIntegrationTest.java @@ -0,0 +1,46 @@ +package seedu.library.logic.commands; + +import static seedu.library.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.testutil.BookmarkBuilder; + +/** + * Contains integration tests (interaction with the Model) for {@code AddCommand}. + */ +public class AddCommandIntegrationTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + } + + @Test + public void execute_newBookmark_success() { + Bookmark validBookmark = new BookmarkBuilder().build(); + + Model expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + expectedModel.addBookmark(validBookmark); + + assertCommandSuccess(new AddCommand(validBookmark), model, + String.format(AddCommand.MESSAGE_SUCCESS, validBookmark), expectedModel); + } + + @Test + public void execute_duplicateBookmark_throwsCommandException() { + Bookmark bookmarkInList = model.getLibrary().getBookmarkList().get(0); + assertCommandFailure(new AddCommand(bookmarkInList), model, AddCommand.MESSAGE_DUPLICATE_BOOKMARK); + } + +} diff --git a/src/test/java/seedu/library/logic/commands/AddCommandTest.java b/src/test/java/seedu/library/logic/commands/AddCommandTest.java new file mode 100644 index 00000000000..bc7c458d379 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/AddCommandTest.java @@ -0,0 +1,257 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import seedu.library.commons.core.GuiSettings; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.tag.Tag; +import seedu.library.testutil.BookmarkBuilder; + +public class AddCommandTest { + + @Test + public void constructor_nullBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddCommand(null)); + } + + @Test + public void execute_bookmarkAcceptedByModel_addSuccessful() throws Exception { + ModelStubAcceptingBookmarkAdded modelStub = new ModelStubAcceptingBookmarkAdded(); + Bookmark validBookmark = new BookmarkBuilder().build(); + + CommandResult commandResult = new AddCommand(validBookmark).execute(modelStub); + + assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validBookmark), commandResult.getFeedbackToUser()); + assertEquals(Arrays.asList(validBookmark), modelStub.bookmarksAdded); + } + + @Test + public void execute_duplicateBookmark_throwsCommandException() { + Bookmark validBookmark = new BookmarkBuilder().build(); + AddCommand addCommand = new AddCommand(validBookmark); + ModelStub modelStub = new ModelStubWithBookmark(validBookmark); + + assertThrows(CommandException.class, + AddCommand.MESSAGE_DUPLICATE_BOOKMARK, () -> addCommand.execute(modelStub)); + } + + @Test + public void equals() { + Bookmark alice = new BookmarkBuilder().withTitle("Alice").build(); + Bookmark bob = new BookmarkBuilder().withTitle("Bob").build(); + AddCommand addAliceCommand = new AddCommand(alice); + AddCommand addBobCommand = new AddCommand(bob); + + // same object -> returns true + assertTrue(addAliceCommand.equals(addAliceCommand)); + + // same values -> returns true + AddCommand addAliceCommandCopy = new AddCommand(alice); + assertTrue(addAliceCommand.equals(addAliceCommandCopy)); + + // different types -> returns false + assertFalse(addAliceCommand.equals(1)); + + // null -> returns false + assertFalse(addAliceCommand.equals(null)); + + // different bookmark -> returns false + assertFalse(addAliceCommand.equals(addBobCommand)); + } + + /** + * A default model stub that have all of the methods failing. + */ + private class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getLibraryFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setLibraryFilePath(Path libraryFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addBookmark(Bookmark bookmark) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setLibrary(ReadOnlyLibrary newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyLibrary getLibrary() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasBookmark(Bookmark bookmark) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteBookmark(Bookmark target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setBookmark(Bookmark target, Bookmark editedBookmark) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyTags getTags() { + throw new AssertionError("This method should not be called."); + } + + @Override + public String tagListToString() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasTag(Set<Tag> tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasTag(Tag tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addTags(Set<Tag> tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteTag(Tag tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean tagInUse(Tag target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList<Bookmark> getFilteredBookmarkList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredBookmarkList(Predicate<Bookmark> predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSortedBookmarkList(String order) { + throw new AssertionError("This method should not be called."); + } + @Override + public Bookmark getSelectedBookmark() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSelectedBookmark(Bookmark target) { + assert(true); + } + + @Override + public int getSelectedIndex() { + throw new AssertionError("This method should not be called."); + }; + + @Override + public void updateSelectedIndex(int index) { + assert(true); + }; + } + + /** + * A Model stub that contains a single bookmark. + */ + private class ModelStubWithBookmark extends ModelStub { + private final Bookmark bookmark; + + ModelStubWithBookmark(Bookmark bookmark) { + requireNonNull(bookmark); + this.bookmark = bookmark; + } + + @Override + public boolean hasBookmark(Bookmark bookmark) { + requireNonNull(bookmark); + return this.bookmark.isSameBookmark(bookmark); + } + } + + /** + * A Model stub that always accept the bookmark being added. + */ + private class ModelStubAcceptingBookmarkAdded extends ModelStub { + final ArrayList<Bookmark> bookmarksAdded = new ArrayList<>(); + + @Override + public boolean hasBookmark(Bookmark bookmark) { + requireNonNull(bookmark); + return bookmarksAdded.stream().anyMatch(bookmark::isSameBookmark); + } + + @Override + public void addBookmark(Bookmark bookmark) { + requireNonNull(bookmark); + bookmarksAdded.add(bookmark); + } + + @Override + public ReadOnlyLibrary getLibrary() { + return new Library(); + } + } + +} diff --git a/src/test/java/seedu/library/logic/commands/AddTagCommandTest.java b/src/test/java/seedu/library/logic/commands/AddTagCommandTest.java new file mode 100644 index 00000000000..776f2a4da28 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/AddTagCommandTest.java @@ -0,0 +1,257 @@ +package seedu.library.logic.commands; + +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Set; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import seedu.library.commons.core.GuiSettings; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.ReadOnlyUserPrefs; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.tag.Tag; +import seedu.library.testutil.TagsBuilder; + +public class AddTagCommandTest { + + @Test + public void constructor_nullTag_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddTagCommand(null)); + } + + @Test + public void execute_tagAcceptedByModel_addSuccessful() throws Exception { + ModelStubAcceptingTagAdded modelStub = new ModelStubAcceptingTagAdded(); + Set<Tag> validTags = new TagsBuilder().build(); + ArrayList<Tag> checkValidTags = new ArrayList<>(validTags); + + CommandResult commandResult = new AddTagCommand(validTags).execute(modelStub); + + assertEquals(String.format(AddTagCommand.MESSAGE_SUCCESS, validTags), commandResult.getFeedbackToUser()); + assertEquals(checkValidTags, modelStub.tagsAdded); + } + + @Test + public void execute_duplicateTag_throwsCommandException() { + Set<Tag> validTags = new TagsBuilder().build(); + AddTagCommand addTagCommand = new AddTagCommand(validTags); + ModelStub modelStub = new ModelStubWithTag(validTags); + + assertThrows(CommandException.class, + AddTagCommand.MESSAGE_DUPLICATE_TAGS, () -> addTagCommand.execute(modelStub)); + } + + @Test + public void equals() { + Set<Tag> novelPlant = new TagsBuilder().build(); + Set<Tag> novelPlantOcean = new TagsBuilder().addTag("ocean").build(); + AddTagCommand addNovelPlantCommand = new AddTagCommand(novelPlant); + AddTagCommand addNovelPlantOceanCommand = new AddTagCommand(novelPlantOcean); + + // same object -> returns true + assertTrue(addNovelPlantCommand.equals(addNovelPlantCommand)); + + // same values -> returns true + AddTagCommand addNovelPlantCommandCopy = new AddTagCommand(novelPlant); + assertTrue(addNovelPlantCommandCopy.equals(addNovelPlantCommandCopy)); + + // different types -> returns false + assertFalse(addNovelPlantCommand.equals(1)); + + // null -> returns false + assertFalse(addNovelPlantCommand.equals(null)); + + // different bookmark -> returns false + assertFalse(addNovelPlantCommand.equals(addNovelPlantOceanCommand)); + } + + /** + * A default model stub that have all of the methods failing. + */ + private class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getLibraryFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setLibraryFilePath(Path libraryFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addBookmark(Bookmark bookmark) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setLibrary(ReadOnlyLibrary newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyLibrary getLibrary() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasBookmark(Bookmark bookmark) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteBookmark(Bookmark target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setBookmark(Bookmark target, Bookmark editedBookmark) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyTags getTags() { + throw new AssertionError("This method should not be called."); + } + + @Override + public String tagListToString() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasTag(Set<Tag> tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasTag(Tag tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addTags(Set<Tag> tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteTag(Tag tags) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean tagInUse(Tag target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList<Bookmark> getFilteredBookmarkList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredBookmarkList(Predicate<Bookmark> predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSortedBookmarkList(String order) { + throw new AssertionError("This method should not be called."); + } + @Override + public Bookmark getSelectedBookmark() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSelectedBookmark(Bookmark target) { + assert(true); + } + + @Override + public int getSelectedIndex() { + throw new AssertionError("This method should not be called."); + }; + + @Override + public void updateSelectedIndex(int index) { + assert(true); + }; + } + + /** + * A Model stub that contains a single tag. + */ + private class ModelStubWithTag extends ModelStub { + private final Set<Tag> tagsAdded; + + ModelStubWithTag(Set<Tag> tags) { + requireNonNull(tags); + this.tagsAdded = tags; + } + + @Override + public boolean hasTag(Set<Tag> tags) { + requireNonNull(tags); + return tagsAdded.containsAll(tags); + } + } + + /** + * A Model stub that always accept the tag being added. + */ + private class ModelStubAcceptingTagAdded extends ModelStub { + final ArrayList<Tag> tagsAdded = new ArrayList<>(); + + @Override + public boolean hasTag(Set<Tag> tags) { + requireNonNull(tags); + return tagsAdded.containsAll(tags); + } + + @Override + public void addTags(Set<Tag> tags) { + requireNonNull(tags); + tagsAdded.addAll(tags); + } + + @Override + public ReadOnlyLibrary getLibrary() { + return new Library(); + } + } + +} diff --git a/src/test/java/seedu/library/logic/commands/ClearCommandTest.java b/src/test/java/seedu/library/logic/commands/ClearCommandTest.java new file mode 100644 index 00000000000..6596534b78a --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/ClearCommandTest.java @@ -0,0 +1,33 @@ +package seedu.library.logic.commands; + +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import org.junit.jupiter.api.Test; + +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; + +public class ClearCommandTest { + + @Test + public void execute_emptyLibrary_success() { + Model model = new ModelManager(); + Model expectedModel = new ModelManager(); + + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyLibrary_success() { + Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + Model expectedModel = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + expectedModel.setLibrary(new Library()); + + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/library/logic/commands/CommandResultTest.java similarity index 89% rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java rename to src/test/java/seedu/library/logic/commands/CommandResultTest.java index 4f3eb46e9ef..1d71c4092f7 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/library/logic/commands/CommandResultTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -14,7 +14,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +29,10 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false))); + assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true))); + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false))); } @Test @@ -46,9 +46,9 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false).hashCode()); } } diff --git a/src/test/java/seedu/library/logic/commands/CommandTestUtil.java b/src/test/java/seedu/library/logic/commands/CommandTestUtil.java new file mode 100644 index 00000000000..7544b06f30d --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/CommandTestUtil.java @@ -0,0 +1,148 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; +import static seedu.library.testutil.Assert.assertThrows; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.exceptions.CommandException; +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; +import seedu.library.model.util.SampleDataUtil; +import seedu.library.testutil.EditBookmarkDescriptorBuilder; + +/** + * Contains helper methods for testing commands. + */ +public class CommandTestUtil { + + public static final String VALID_TITLE_AMY = "Attack on Titans"; + public static final String VALID_TITLE_BOB = "Blue Lock"; + public static final String VALID_PROGRESS_AMY = "1 32 7"; + public static final String VALID_PROGRESS_BOB = "2 20 12"; + public static final String VALID_GENRE_AMY = "Fantasy"; + public static final String VALID_GENRE_BOB = "Others"; + public static final String VALID_AUTHOR_AMY = "Hajime Isayama"; + public static final String VALID_AUTHOR_BOB = "Muneyuki Kaneshiro"; + public static final String VALID_TAG_HUSBAND = "MaleProtagonist"; + public static final String VALID_TAG_FRIEND = "FemaleProtagonist"; + public static final String VALID_TAG_PLANT = "plant"; + public static final String VALID_TAG_NOVEL = "novel"; + public static final ReadOnlyTags VALID_TAGS = SampleDataUtil.getSampleTagList(); + public static final String VALID_RATING_AMY = "3"; + public static final String VALID_RATING_BOB = "4"; + public static final String VALID_URL_AMY = "http://www.google.com"; + public static final String VALID_URL_BOB = "http://www.youtube.com"; + + public static final String TITLE_DESC_AMY = " " + PREFIX_TITLE + VALID_TITLE_AMY; + public static final String TITLE_DESC_BOB = " " + PREFIX_TITLE + VALID_TITLE_BOB; + public static final String PROGRESS_DESC_AMY = " " + PREFIX_PROGRESS + VALID_PROGRESS_AMY; + public static final String PROGRESS_DESC_BOB = " " + PREFIX_PROGRESS + VALID_PROGRESS_BOB; + public static final String GENRE_DESC_AMY = " " + PREFIX_GENRE + VALID_GENRE_AMY; + public static final String GENRE_DESC_BOB = " " + PREFIX_GENRE + VALID_GENRE_BOB; + public static final String AUTHOR_DESC_AMY = " " + PREFIX_AUTHOR + VALID_AUTHOR_AMY; + public static final String AUTHOR_DESC_BOB = " " + PREFIX_AUTHOR + VALID_AUTHOR_BOB; + public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; + public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; + public static final String TAG_DESC_PLANT = " " + PREFIX_TAG + VALID_TAG_PLANT; + public static final String TAG_DESC_NOVEL = " " + PREFIX_TAG + VALID_TAG_NOVEL; + public static final String RATING_DESC_AMY = " " + PREFIX_RATING + VALID_RATING_AMY; + public static final String URL_DESC_AMY = " " + PREFIX_URL + VALID_URL_AMY; + + public static final String INVALID_TITLE_DESC = " " + PREFIX_TITLE; // '&' not allowed in names + public static final String INVALID_PROGRESS_DESC = " " + PREFIX_PROGRESS + "911*"; // '*' not allowed in progress + public static final String INVALID_GENRE_DESC = " " + PREFIX_GENRE; + public static final String INVALID_AUTHOR_DESC = " " + PREFIX_AUTHOR; // empty string not allowed for author + public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags + public static final String INVALID_RATING_DESC = " " + PREFIX_RATING + "-1"; + public static final String INVALID_URL_DESC = " " + PREFIX_URL + "invalidurl"; + + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; + public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; + + public static final EditCommand.EditBookmarkDescriptor DESC_AMY; + public static final EditCommand.EditBookmarkDescriptor DESC_BOB; + + static { + DESC_AMY = new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_AMY) + .withProgress(VALID_PROGRESS_AMY).withGenre(VALID_GENRE_AMY).withAuthor(VALID_AUTHOR_AMY) + .withTags(VALID_TAG_FRIEND).withRating(VALID_RATING_AMY) + .withUrl(VALID_URL_AMY).build(); + DESC_BOB = new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_BOB) + .withProgress(VALID_PROGRESS_BOB).withGenre(VALID_GENRE_BOB).withAuthor(VALID_AUTHOR_BOB) + .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).withRating(VALID_RATING_BOB) + .withUrl(VALID_URL_BOB).build(); + } + + /** + * Executes the given {@code command}, confirms that <br> + * - the returned {@link CommandResult} matches {@code expectedCommandResult} <br> + * - the {@code actualModel} matches {@code expectedModel} + */ + public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, + Model expectedModel) { + try { + CommandResult result = command.execute(actualModel); + assertEquals(expectedCommandResult, result); + assertEquals(expectedModel, actualModel); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} + * that takes a string {@code expectedMessage}. + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + CommandResult expectedCommandResult = new CommandResult(expectedMessage); + assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); + } + + /** + * Executes the given {@code command}, confirms that <br> + * - a {@code CommandException} is thrown <br> + * - the CommandException message matches {@code expectedMessage} <br> + * - the library, filtered bookmark list and selected bookmark in {@code actualModel} remain unchanged + */ + public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + // we are unable to defensively copy the model for comparison later, so we can + // only do so by copying its components. + Library expectedLibrary = new Library(actualModel.getLibrary()); + List<Bookmark> expectedFilteredList = new ArrayList<>(actualModel.getFilteredBookmarkList()); + + assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); + assertEquals(expectedLibrary, actualModel.getLibrary()); + assertEquals(expectedFilteredList, actualModel.getFilteredBookmarkList()); + } + /** + * Updates {@code model}'s filtered list to show only the bookmark at the given {@code targetIndex} in the + * {@code model}'s library. + */ + public static void showBookmarkAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredBookmarkList().size()); + + Bookmark bookmark = model.getFilteredBookmarkList().get(targetIndex.getZeroBased()); + final String[] splitName = bookmark.getTitle().value.split("\\s+"); + model.updateFilteredBookmarkList(new BookmarkContainsKeywordsPredicate( + Arrays.asList(splitName[0]), null, null, null)); + + assertEquals(1, model.getFilteredBookmarkList().size()); + } + +} diff --git a/src/test/java/seedu/library/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/library/logic/commands/DeleteCommandTest.java new file mode 100644 index 00000000000..872d549f9e7 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/DeleteCommandTest.java @@ -0,0 +1,110 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.logic.commands.CommandTestUtil.showBookmarkAtIndex; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; +import static seedu.library.testutil.TypicalIndexes.INDEX_SECOND_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; +import seedu.library.model.bookmark.Bookmark; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteCommand}. + */ +public class DeleteCommandTest { + + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Bookmark bookmarkToDelete = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_BOOKMARK); + + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_BOOKMARK_SUCCESS, bookmarkToDelete); + + ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + expectedModel.deleteBookmark(bookmarkToDelete); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredBookmarkList().size() + 1); + DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + Bookmark bookmarkToDelete = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_BOOKMARK); + + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_BOOKMARK_SUCCESS, bookmarkToDelete); + + Model expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + expectedModel.deleteBookmark(bookmarkToDelete); + showNoBookmark(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + Index outOfBoundIndex = INDEX_SECOND_BOOKMARK; + // ensures that outOfBoundIndex is still in bounds of library list + assertTrue(outOfBoundIndex.getZeroBased() < model.getLibrary().getBookmarkList().size()); + + DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_BOOKMARK); + DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_BOOKMARK); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_BOOKMARK); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different bookmark -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + /** + * Updates {@code model}'s filtered list to show no one. + */ + private void showNoBookmark(Model model) { + model.updateFilteredBookmarkList(p -> false); + + assertTrue(model.getFilteredBookmarkList().isEmpty()); + } +} diff --git a/src/test/java/seedu/library/logic/commands/DeleteTagCommandTest.java b/src/test/java/seedu/library/logic/commands/DeleteTagCommandTest.java new file mode 100644 index 00000000000..6d58ca00de5 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/DeleteTagCommandTest.java @@ -0,0 +1,61 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import org.junit.jupiter.api.Test; + +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.UserPrefs; +import seedu.library.model.tag.Tag; +import seedu.library.testutil.TagsBuilder; + +public class DeleteTagCommandTest { + + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new TagsBuilder().getTypicalTags()); + + @Test + public void execute_deleteTag_success() { + Tag tagToDelete = new Tag("plant"); + DeleteTagCommand deleteTagCommand = new DeleteTagCommand(tagToDelete); + + String expectedMessage = String.format(DeleteTagCommand.MESSAGE_DELETE_TAG_SUCCESS, tagToDelete); + + ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), model.getTags()); + expectedModel.deleteTag(tagToDelete); + + assertCommandSuccess(deleteTagCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_deleteInvalidTag_throwsCommandException() { + assertThrows(IllegalArgumentException.class, Tag.MESSAGE_CONSTRAINTS, () -> + new DeleteTagCommand(new Tag("abc 123 abc")).execute(model)); + } + + @Test + public void equals() { + DeleteTagCommand deletePlantCommand = new DeleteTagCommand(new Tag("plant")); + DeleteTagCommand deleteNovelCommand = new DeleteTagCommand(new Tag("novel")); + + // same object -> returns true + assertTrue(deletePlantCommand.equals(deletePlantCommand)); + + // same values -> returns true + DeleteTagCommand deletePlantCommandCopy = new DeleteTagCommand(new Tag("plant")); + assertTrue(deletePlantCommand.equals(deletePlantCommandCopy)); + + // different types -> returns false + assertFalse(deletePlantCommand.equals(1)); + + // null -> returns false + assertFalse(deletePlantCommand.equals(null)); + + // different bookmark -> returns false + assertFalse(deletePlantCommand.equals(deleteNovelCommand)); + } +} diff --git a/src/test/java/seedu/library/logic/commands/EditBookmarkDescriptorTest.java b/src/test/java/seedu/library/logic/commands/EditBookmarkDescriptorTest.java new file mode 100644 index 00000000000..a0f45ad2c48 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/EditBookmarkDescriptorTest.java @@ -0,0 +1,69 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_RATING_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_URL_BOB; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.EditCommand.EditBookmarkDescriptor; +import seedu.library.testutil.EditBookmarkDescriptorBuilder; + +public class EditBookmarkDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditCommand.EditBookmarkDescriptor descriptorWithSameValues = new EditBookmarkDescriptor(DESC_AMY); + assertTrue(DESC_AMY.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(DESC_AMY.equals(DESC_AMY)); + + // null -> returns false + assertFalse(DESC_AMY.equals(null)); + + // different types -> returns false + assertFalse(DESC_AMY.equals(5)); + + // different values -> returns false + assertFalse(DESC_AMY.equals(DESC_BOB)); + + // different name -> returns false + EditBookmarkDescriptor editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY) + .withTitle(VALID_TITLE_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different progress -> returns false + editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY).withProgress(VALID_PROGRESS_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different genre -> returns false + editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY).withGenre(VALID_GENRE_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different author -> returns false + editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY).withAuthor(VALID_AUTHOR_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different tags -> returns false + editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different rating -> returns false + editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY).withRating(VALID_RATING_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different url -> returns false + editedAmy = new EditBookmarkDescriptorBuilder(DESC_AMY).withUrl(VALID_URL_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + } +} diff --git a/src/test/java/seedu/library/logic/commands/EditCommandTest.java b/src/test/java/seedu/library/logic/commands/EditCommandTest.java new file mode 100644 index 00000000000..2ccce517e75 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/EditCommandTest.java @@ -0,0 +1,175 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAGS; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.logic.commands.CommandTestUtil.showBookmarkAtIndex; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; +import static seedu.library.testutil.TypicalIndexes.INDEX_SECOND_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.EditCommand.EditBookmarkDescriptor; +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.testutil.BookmarkBuilder; +import seedu.library.testutil.EditBookmarkDescriptorBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditCommand. + */ +public class EditCommandTest { + + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), VALID_TAGS); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Bookmark editedBookmark = new BookmarkBuilder().build(); + EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder(editedBookmark).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_BOOKMARK, descriptor); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_BOOKMARK_SUCCESS, editedBookmark); + + Model expectedModel = new ModelManager(new Library(model.getLibrary()), new UserPrefs(), new Tags()); + expectedModel.setBookmark(model.getFilteredBookmarkList().get(0), editedBookmark); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastBookmark = Index.fromOneBased(model.getFilteredBookmarkList().size()); + Bookmark lastBookmark = model.getFilteredBookmarkList().get(indexLastBookmark.getZeroBased()); + + BookmarkBuilder bookmarkInList = new BookmarkBuilder(lastBookmark); + Bookmark editedBookmark = bookmarkInList.withTitle(VALID_TITLE_BOB).withProgress(VALID_PROGRESS_BOB) + .withTags(VALID_TAG_HUSBAND).build(); + + EditCommand.EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_BOB) + .withProgress(VALID_PROGRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); + EditCommand editCommand = new EditCommand(indexLastBookmark, descriptor); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_BOOKMARK_SUCCESS, editedBookmark); + + Model expectedModel = new ModelManager(new Library(model.getLibrary()), new UserPrefs(), new Tags()); + expectedModel.setBookmark(lastBookmark, editedBookmark); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditCommand editCommand = new EditCommand(INDEX_FIRST_BOOKMARK, new EditCommand.EditBookmarkDescriptor()); + Bookmark editedBookmark = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_BOOKMARK_SUCCESS, editedBookmark); + + Model expectedModel = new ModelManager(new Library(model.getLibrary()), new UserPrefs(), new Tags()); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + Bookmark bookmarkInFilteredList = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + Bookmark editedBookmark = new BookmarkBuilder(bookmarkInFilteredList).withTitle(VALID_TITLE_BOB).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_BOOKMARK, + new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_BOB).build()); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_BOOKMARK_SUCCESS, editedBookmark); + + Model expectedModel = new ModelManager(new Library(model.getLibrary()), new UserPrefs(), VALID_TAGS); + expectedModel.setBookmark(model.getFilteredBookmarkList().get(0), editedBookmark); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_duplicateBookmarkUnfilteredList_failure() { + Bookmark firstBookmark = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + EditCommand.EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder(firstBookmark).build(); + EditCommand editCommand = new EditCommand(INDEX_SECOND_BOOKMARK, descriptor); + + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_BOOKMARK); + } + + @Test + public void execute_duplicateBookmarkFilteredList_failure() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + // edit bookmark in filtered list into a duplicate in address book + Bookmark bookmarkInList = model.getLibrary().getBookmarkList().get(INDEX_SECOND_BOOKMARK.getZeroBased()); + EditCommand editCommand = new EditCommand(INDEX_FIRST_BOOKMARK, + new EditBookmarkDescriptorBuilder(bookmarkInList).build()); + + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_BOOKMARK); + } + + @Test + public void execute_invalidBookmarkIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredBookmarkList().size() + 1); + EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_BOB).build(); + EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of library + */ + @Test + public void execute_invalidBookmarkIndexFilteredList_failure() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + Index outOfBoundIndex = INDEX_SECOND_BOOKMARK; + // ensures that outOfBoundIndex is still in bounds of library list + assertTrue(outOfBoundIndex.getZeroBased() < model.getLibrary().getBookmarkList().size()); + + EditCommand editCommand = new EditCommand(outOfBoundIndex, + new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_BOB).build()); + + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditCommand standardCommand = new EditCommand(INDEX_FIRST_BOOKMARK, DESC_AMY); + + // same values -> returns true + EditCommand.EditBookmarkDescriptor copyDescriptor = new EditBookmarkDescriptor(DESC_AMY); + EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_BOOKMARK, copyDescriptor); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_BOOKMARK, DESC_AMY))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_BOOKMARK, DESC_BOB))); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/library/logic/commands/ExitCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/ExitCommandTest.java rename to src/test/java/seedu/library/logic/commands/ExitCommandTest.java index 9533c473875..e570b94606d 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/library/logic/commands/ExitCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; public class ExitCommandTest { private Model model = new ModelManager(); @@ -14,7 +14,7 @@ public class ExitCommandTest { @Test public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/library/logic/commands/FindCommandTest.java b/src/test/java/seedu/library/logic/commands/FindCommandTest.java new file mode 100644 index 00000000000..1e4fc9cf505 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/FindCommandTest.java @@ -0,0 +1,136 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.commons.core.Messages.MESSAGE_BOOKMARKS_LISTED_OVERVIEW; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.testutil.TypicalBookmarks.CARL; +import static seedu.library.testutil.TypicalBookmarks.ELLE; +import static seedu.library.testutil.TypicalBookmarks.FIONA; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FindCommand}. + */ +public class FindCommandTest { + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + private Model expectedModel = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + + @Test + public void equals() { + BookmarkContainsKeywordsPredicate firstPredicate = + new BookmarkContainsKeywordsPredicate( + Collections.singletonList("first"), null, null, null); + BookmarkContainsKeywordsPredicate secondPredicate = + new BookmarkContainsKeywordsPredicate( + Collections.singletonList("second"), null, null, null); + + FindCommand findFirstCommand = new FindCommand(firstPredicate); + FindCommand findSecondCommand = new FindCommand(secondPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different bookmark -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_oneTitleKeywordsOneAuthorKeywords_noBookmarkFound() { + String expectedMessage = String.format(MESSAGE_BOOKMARKS_LISTED_OVERVIEW, 0); + BookmarkContainsKeywordsPredicate predicate = + preparePredicate("Alice", null, null, "wall street"); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredBookmarkList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredBookmarkList()); + } + + @Test + public void execute_multipleKeywords_noBookmarksFound() { + String expectedMessage = String.format(MESSAGE_BOOKMARKS_LISTED_OVERVIEW, 0); + BookmarkContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz", null, null, null); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredBookmarkList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredBookmarkList()); + } + + @Test + public void execute_oneGenreKeywords_oneBookmarksFound() { + String expectedMessage = String.format(MESSAGE_BOOKMARKS_LISTED_OVERVIEW, 1); + BookmarkContainsKeywordsPredicate predicate = preparePredicate(null, "Comedy", null, null); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredBookmarkList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CARL), model.getFilteredBookmarkList()); + } + + @Test + public void execute_oneTitleKeywords_oneBookmarksFound() { + String expectedMessage = String.format(MESSAGE_BOOKMARKS_LISTED_OVERVIEW, 1); + BookmarkContainsKeywordsPredicate predicate = preparePredicate("Fiona", null, null, null); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredBookmarkList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(FIONA), model.getFilteredBookmarkList()); + } + + @Test + public void execute_oneAuthorKeywords_oneBookmarksFound() { + String expectedMessage = String.format(MESSAGE_BOOKMARKS_LISTED_OVERVIEW, 1); + BookmarkContainsKeywordsPredicate predicate = preparePredicate(null, null, null, "michegan ave"); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredBookmarkList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(ELLE), model.getFilteredBookmarkList()); + } + + /** + * Parses {@code userInput} into a {@code TitleContainsKeywordsPredicate}. + */ + private BookmarkContainsKeywordsPredicate preparePredicate(String titleInput, String genreInput, + String tagInput, String authorInput) { + List<String> titleKeywords = null; + List<String> genreKeywords = null; + List<String> tagKeywords = null; + List<String> authorKeywords = null; + + if (titleInput != null) { + titleKeywords = Arrays.asList(titleInput.split("\\s+")); + } + if (genreInput != null) { + genreKeywords = Arrays.asList(genreInput.split("\\s+")); + } + if (tagInput != null) { + tagKeywords = Arrays.asList(tagInput.split("\\s+")); + } + if (authorInput != null) { + authorKeywords = Arrays.asList(authorInput.split("\\s+")); + } + return new BookmarkContainsKeywordsPredicate(titleKeywords, genreKeywords, tagKeywords, authorKeywords); + } +} diff --git a/src/test/java/seedu/library/logic/commands/GoToCommandTest.java b/src/test/java/seedu/library/logic/commands/GoToCommandTest.java new file mode 100644 index 00000000000..668d303cc9f --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/GoToCommandTest.java @@ -0,0 +1,94 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.library.logic.commands.CommandTestUtil.showBookmarkAtIndex; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; +import static seedu.library.testutil.TypicalIndexes.INDEX_SECOND_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; + + + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code GoToCommand}. + */ +public class GoToCommandTest { + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + + // @Test + // public void execute_validIndexUnfilteredList_success() { + // Bookmark bookmarkToView = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + // GoToCommand goToCommand = new GoToCommand(INDEX_FIRST_BOOKMARK); + // String expectedMessage = String.format(GoToCommand.MESSAGE_GOTO_BOOKMARK_SUCCESS, bookmarkToView); + // ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + // expectedModel.viewBookmark(bookmarkToView); + // + // assertCommandSuccess(goToCommand, model, expectedMessage, expectedModel); + // } + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredBookmarkList().size() + 1); + GoToCommand goToCommand = new GoToCommand(outOfBoundIndex); + + assertCommandFailure(goToCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + // @Test + // public void execute_validIndexFilteredList_success() { + // showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + // Bookmark bookmarkToView = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + // GoToCommand goToCommand = new GoToCommand(INDEX_FIRST_BOOKMARK); + // String expectedMessage = String.format(GoToCommand.MESSAGE_GOTO_BOOKMARK_SUCCESS, bookmarkToView); + // ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + // showBookmarkAtIndex(expectedModel, INDEX_FIRST_BOOKMARK); + // expectedModel.viewBookmark(bookmarkToView); + // assertCommandSuccess(goToCommand, model, expectedMessage, expectedModel); + // } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + Index outOfBoundIndex = INDEX_SECOND_BOOKMARK; + // ensures that outOfBoundIndex is still in bounds of bookmark list + assertTrue(outOfBoundIndex.getZeroBased() < model.getLibrary().getBookmarkList().size()); + + GoToCommand goToCommand = new GoToCommand(outOfBoundIndex); + + assertCommandFailure(goToCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + GoToCommand goToFirstCommand = new GoToCommand(INDEX_FIRST_BOOKMARK); + GoToCommand goToSecondCommand = new GoToCommand(INDEX_SECOND_BOOKMARK); + + // same object -> returns true + assertTrue(goToFirstCommand.equals(goToFirstCommand)); + + // same values -> returns true + GoToCommand goToFirstCommandCopy = new GoToCommand(INDEX_FIRST_BOOKMARK); + assertTrue(goToFirstCommand.equals(goToFirstCommandCopy)); + + // different types -> returns false + assertFalse(goToFirstCommand.equals(1)); + + // null -> returns false + assertFalse(goToFirstCommand.equals(null)); + + // different goTo -> returns false + assertFalse(goToFirstCommand.equals(goToSecondCommand)); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/library/logic/commands/HelpCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/HelpCommandTest.java rename to src/test/java/seedu/library/logic/commands/HelpCommandTest.java index 4904fc4352e..0f09d1b57ba 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/library/logic/commands/HelpCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.library.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; public class HelpCommandTest { private Model model = new ModelManager(); @@ -14,7 +14,7 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/library/logic/commands/ListCommandTest.java b/src/test/java/seedu/library/logic/commands/ListCommandTest.java new file mode 100644 index 00000000000..dd9965b6772 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/ListCommandTest.java @@ -0,0 +1,40 @@ +package seedu.library.logic.commands; + +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.logic.commands.CommandTestUtil.showBookmarkAtIndex; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCommand. + */ +public class ListCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/library/logic/commands/ListTagsCommandTest.java b/src/test/java/seedu/library/logic/commands/ListTagsCommandTest.java new file mode 100644 index 00000000000..80a37509645 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/ListTagsCommandTest.java @@ -0,0 +1,35 @@ +package seedu.library.logic.commands; + +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.library.model.Library; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.UserPrefs; +import seedu.library.testutil.TagsBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTagCommand. + */ + +public class ListTagsCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(new Library(), new UserPrefs(), new TagsBuilder().getTypicalTags()); + expectedModel = new ModelManager(new Library(), new UserPrefs(), model.getTags()); + } + + @Test + public void execute_listTagIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListTagsCommand(), model, + ListTagsCommand.MESSAGE_SUCCESS + model.tagListToString(), expectedModel); + } + +} diff --git a/src/test/java/seedu/library/logic/commands/SortCommandTest.java b/src/test/java/seedu/library/logic/commands/SortCommandTest.java new file mode 100644 index 00000000000..4a892c01d45 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/SortCommandTest.java @@ -0,0 +1,43 @@ +package seedu.library.logic.commands; + +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import org.junit.jupiter.api.Test; + +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.UserPrefs; +import seedu.library.testutil.TagsBuilder; + +public class SortCommandTest { + + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new TagsBuilder().getTypicalTags()); + + @Test + public void execute_sortByDescendingOrder() { + String order = "desc"; + SortCommand sortCommand = new SortCommand(order); + + String expectedMessage = SortCommand.MESSAGE_SUCCESS_DESC; + + ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), model.getTags()); + expectedModel.updateSortedBookmarkList(order); + + assertCommandSuccess(sortCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_sortByAscendingOrder() { + String order = "asc"; + SortCommand sortCommand = new SortCommand(order); + + String expectedMessage = SortCommand.MESSAGE_SUCCESS_ASC; + + ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), model.getTags()); + expectedModel.updateSortedBookmarkList(order); + + assertCommandSuccess(sortCommand, model, expectedMessage, expectedModel); + } + +} diff --git a/src/test/java/seedu/library/logic/commands/ViewCommandTest.java b/src/test/java/seedu/library/logic/commands/ViewCommandTest.java new file mode 100644 index 00000000000..1759a4a62a8 --- /dev/null +++ b/src/test/java/seedu/library/logic/commands/ViewCommandTest.java @@ -0,0 +1,107 @@ +package seedu.library.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.library.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.library.logic.commands.CommandTestUtil.showBookmarkAtIndex; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; +import static seedu.library.testutil.TypicalIndexes.INDEX_SECOND_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.core.Messages; +import seedu.library.commons.core.index.Index; +import seedu.library.model.Model; +import seedu.library.model.ModelManager; +import seedu.library.model.Tags; +import seedu.library.model.UserPrefs; +import seedu.library.model.bookmark.Bookmark; + + + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code ViewCommand}. + */ +public class ViewCommandTest { + + + + private Model model = new ModelManager(getTypicalLibrary(), new UserPrefs(), new Tags()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Bookmark bookmarkToView = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_BOOKMARK); + + String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_VIEWED_SUCCESS, bookmarkToView); + + ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + expectedModel.viewBookmark(bookmarkToView); + + assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredBookmarkList().size() + 1); + ViewCommand viewCommand = new ViewCommand(outOfBoundIndex); + + assertCommandFailure(viewCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + Bookmark bookmarkToView = model.getFilteredBookmarkList().get(INDEX_FIRST_BOOKMARK.getZeroBased()); + ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_BOOKMARK); + + String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_VIEWED_SUCCESS, bookmarkToView); + + + ModelManager expectedModel = new ModelManager(model.getLibrary(), new UserPrefs(), new Tags()); + showBookmarkAtIndex(expectedModel, INDEX_FIRST_BOOKMARK); + expectedModel.viewBookmark(bookmarkToView); + + assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showBookmarkAtIndex(model, INDEX_FIRST_BOOKMARK); + + Index outOfBoundIndex = INDEX_SECOND_BOOKMARK; + // ensures that outOfBoundIndex is still in bounds of bookmark list + assertTrue(outOfBoundIndex.getZeroBased() < model.getLibrary().getBookmarkList().size()); + + ViewCommand viewCommand = new ViewCommand(outOfBoundIndex); + + assertCommandFailure(viewCommand, model, Messages.MESSAGE_INVALID_BOOKMARK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + ViewCommand viewFirstCommand = new ViewCommand(INDEX_FIRST_BOOKMARK); + ViewCommand viewSecondCommand = new ViewCommand(INDEX_SECOND_BOOKMARK); + + // same object -> returns true + assertTrue(viewFirstCommand.equals(viewFirstCommand)); + + // same values -> returns true + ViewCommand viewFirstCommandCopy = new ViewCommand(INDEX_FIRST_BOOKMARK); + assertTrue(viewFirstCommand.equals(viewFirstCommandCopy)); + + // different types -> returns false + assertFalse(viewFirstCommand.equals(1)); + + // null -> returns false + assertFalse(viewFirstCommand.equals(null)); + + // different view -> returns false + assertFalse(viewFirstCommand.equals(viewSecondCommand)); + } + +} diff --git a/src/test/java/seedu/library/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/library/logic/parser/AddCommandParserTest.java new file mode 100644 index 00000000000..8bc3a2d5591 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/AddCommandParserTest.java @@ -0,0 +1,132 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.commands.CommandTestUtil.AUTHOR_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.AUTHOR_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.GENRE_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.GENRE_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_AUTHOR_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_GENRE_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_PROGRESS_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TITLE_DESC; +import static seedu.library.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.library.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.library.logic.commands.CommandTestUtil.PROGRESS_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.PROGRESS_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +import static seedu.library.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.TITLE_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.TITLE_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_BOB; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.library.testutil.TypicalBookmarks.AMY; +import static seedu.library.testutil.TypicalBookmarks.BOB; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.AddCommand; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Title; +import seedu.library.model.tag.Tag; +import seedu.library.testutil.BookmarkBuilder; + +public class AddCommandParserTest { + private AddCommandParser parser = new AddCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Bookmark expectedBookmark = new BookmarkBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + + AUTHOR_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedBookmark)); + + // multiple names - last name accepted + assertParseSuccess(parser, TITLE_DESC_AMY + TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + + AUTHOR_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedBookmark)); + + // multiple progresses - last progress accepted + assertParseSuccess(parser, TITLE_DESC_BOB + PROGRESS_DESC_AMY + PROGRESS_DESC_BOB + GENRE_DESC_BOB + + AUTHOR_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedBookmark)); + + // multiple emails - last email accepted + assertParseSuccess(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_AMY + GENRE_DESC_BOB + + AUTHOR_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedBookmark)); + + // multiple addresses - last address accepted + assertParseSuccess(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + AUTHOR_DESC_AMY + + AUTHOR_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedBookmark)); + + // multiple tags - all accepted + Bookmark expectedBookmarkMultipleTags = new BookmarkBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) + .build(); + assertParseSuccess(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + AUTHOR_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedBookmarkMultipleTags)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + // only title and genre present + Bookmark expectedBookmark = new BookmarkBuilder(AMY).withTags().build(); + assertParseSuccess(parser, TITLE_DESC_AMY + GENRE_DESC_AMY, new AddCommand(expectedBookmark)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + + // missing name prefix + assertParseFailure(parser, VALID_TITLE_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + AUTHOR_DESC_BOB, + expectedMessage); + + // missing genre prefix + assertParseFailure(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + VALID_GENRE_BOB + AUTHOR_DESC_BOB, + expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_TITLE_BOB + VALID_PROGRESS_BOB + VALID_GENRE_BOB + VALID_AUTHOR_BOB, + expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid name + assertParseFailure(parser, INVALID_TITLE_DESC + PROGRESS_DESC_BOB + GENRE_DESC_BOB + AUTHOR_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Title.MESSAGE_CONSTRAINTS); + + // invalid progress + assertParseFailure(parser, TITLE_DESC_BOB + INVALID_PROGRESS_DESC + GENRE_DESC_BOB + AUTHOR_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Progress.MESSAGE_CONSTRAINTS); + + // invalid genre + assertParseFailure(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + INVALID_GENRE_DESC + AUTHOR_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Genre.MESSAGE_CONSTRAINTS); + + // invalid author + assertParseFailure(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + INVALID_AUTHOR_DESC + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Author.MESSAGE_CONSTRAINTS); + + // invalid tag + assertParseFailure(parser, TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + AUTHOR_DESC_BOB + + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_TITLE_DESC + PROGRESS_DESC_BOB + GENRE_DESC_BOB + INVALID_AUTHOR_DESC, + Title.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + TITLE_DESC_BOB + PROGRESS_DESC_BOB + GENRE_DESC_BOB + + AUTHOR_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/library/logic/parser/AddTagCommandParserTest.java b/src/test/java/seedu/library/logic/parser/AddTagCommandParserTest.java new file mode 100644 index 00000000000..250dce94bd4 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/AddTagCommandParserTest.java @@ -0,0 +1,46 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.library.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.library.logic.commands.CommandTestUtil.TAG_DESC_NOVEL; +import static seedu.library.logic.commands.CommandTestUtil.TAG_DESC_PLANT; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_PLANT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.AddTagCommand; +import seedu.library.model.tag.Tag; +import seedu.library.testutil.TagsBuilder; + +public class AddTagCommandParserTest { + private AddTagCommandParser parser = new AddTagCommandParser(); + + @Test + public void parse_tagsPresent_success() { + Set<Tag> expectedTags = new TagsBuilder().build(); + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + TAG_DESC_PLANT + TAG_DESC_NOVEL, + new AddTagCommand(expectedTags)); + } + + @Test + public void parse_wrongFormat_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE); + + // no prefix for tags + assertParseFailure(parser, VALID_TAG_PLANT, expectedMessage); + + // no tags + assertParseFailure(parser, " ", expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/library/logic/parser/ArgumentTokenizerTest.java similarity index 99% rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java rename to src/test/java/seedu/library/logic/parser/ArgumentTokenizerTest.java index c97308935f5..f54d22b35de 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/library/logic/parser/ArgumentTokenizerTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/seedu/library/logic/parser/CliSyntaxTest.java b/src/test/java/seedu/library/logic/parser/CliSyntaxTest.java new file mode 100644 index 00000000000..45057fc3811 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/CliSyntaxTest.java @@ -0,0 +1,49 @@ +package seedu.library.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; + +import org.junit.jupiter.api.Test; + +public class CliSyntaxTest { + @Test + public void prefixAuthor_success() { + assertEquals(new Prefix("a/"), PREFIX_AUTHOR); + } + + @Test + public void prefixGenre_success() { + assertEquals(new Prefix("g/"), PREFIX_GENRE); + } + + @Test + public void prefixProgress_success() { + assertEquals(new Prefix("p/"), PREFIX_PROGRESS); + } + + @Test + public void prefixTag_success() { + assertEquals(new Prefix("t/"), PREFIX_TAG); + } + + @Test + public void prefixTitle_success() { + assertEquals(new Prefix("n/"), PREFIX_TITLE); + } + + @Test + public void prefixRating_success() { + assertEquals(new Prefix("r/"), PREFIX_RATING); + } + + @Test + public void prefixUrl_success() { + assertEquals(new Prefix("u/"), PREFIX_URL); + } +} diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/seedu/library/logic/parser/CommandParserTestUtil.java similarity index 89% rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java rename to src/test/java/seedu/library/logic/parser/CommandParserTestUtil.java index 9bf1ccf1cef..3877318cffb 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/seedu/library/logic/parser/CommandParserTestUtil.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.library.logic.commands.Command; +import seedu.library.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/library/logic/parser/DeleteCommandParserTest.java similarity index 71% rename from src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java rename to src/test/java/seedu/library/logic/parser/DeleteCommandParserTest.java index 27eaec84450..d6a90509899 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/seedu/library/logic/parser/DeleteCommandParserTest.java @@ -1,13 +1,13 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.DeleteCommand; +import seedu.library.logic.commands.DeleteCommand; /** * As we are only doing white-box testing, our test cases do not cover path variations @@ -22,7 +22,7 @@ public class DeleteCommandParserTest { @Test public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); + assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_BOOKMARK)); } @Test diff --git a/src/test/java/seedu/library/logic/parser/DeleteTagCommandParserTest.java b/src/test/java/seedu/library/logic/parser/DeleteTagCommandParserTest.java new file mode 100644 index 00000000000..acdd629749b --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/DeleteTagCommandParserTest.java @@ -0,0 +1,28 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_PLANT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.DeleteTagCommand; +import seedu.library.model.tag.Tag; + +public class DeleteTagCommandParserTest { + private DeleteTagCommandParser parser = new DeleteTagCommandParser(); + private Tag tag = new Tag(VALID_TAG_PLANT); + + @Test + public void parse_validArgs_returnDeleteCommand() { + assertParseSuccess(parser, VALID_TAG_PLANT, new DeleteTagCommand(tag)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, INVALID_TAG_DESC, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/library/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/library/logic/parser/EditCommandParserTest.java new file mode 100644 index 00000000000..f7e18c5c649 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/EditCommandParserTest.java @@ -0,0 +1,243 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.commands.CommandTestUtil.AUTHOR_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.AUTHOR_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.GENRE_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.GENRE_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_AUTHOR_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_GENRE_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_PROGRESS_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_RATING_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TITLE_DESC; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_URL_DESC; +import static seedu.library.logic.commands.CommandTestUtil.PROGRESS_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.PROGRESS_DESC_BOB; +import static seedu.library.logic.commands.CommandTestUtil.RATING_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +import static seedu.library.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.TITLE_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.URL_DESC_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_RATING_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_URL_AMY; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; +import static seedu.library.testutil.TypicalIndexes.INDEX_SECOND_BOOKMARK; +import static seedu.library.testutil.TypicalIndexes.INDEX_THIRD_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.core.index.Index; +import seedu.library.logic.commands.EditCommand; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; +import seedu.library.testutil.EditBookmarkDescriptorBuilder; + +public class EditCommandParserTest { + + private static final String TAG_EMPTY = " " + PREFIX_TAG; + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); + + private EditCommandParser parser = new EditCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_TITLE_AMY, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + TITLE_DESC_AMY, MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + TITLE_DESC_AMY, MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, "1" + INVALID_TITLE_DESC, Title.MESSAGE_CONSTRAINTS); // invalid name + assertParseFailure(parser, "1" + INVALID_PROGRESS_DESC, Progress.MESSAGE_CONSTRAINTS); // invalid progress + assertParseFailure(parser, "1" + INVALID_GENRE_DESC, Genre.MESSAGE_CONSTRAINTS); // invalid email + assertParseFailure(parser, "1" + INVALID_AUTHOR_DESC, Author.MESSAGE_CONSTRAINTS); // invalid address + assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag + assertParseFailure(parser, "1" + INVALID_RATING_DESC, Rating.MESSAGE_CONSTRAINTS); // invalid rating + assertParseFailure(parser, "1" + INVALID_URL_DESC, Url.MESSAGE_CONSTRAINTS); // invalid url + + // invalid progress followed by valid genre + assertParseFailure(parser, "1" + INVALID_PROGRESS_DESC + GENRE_DESC_AMY, Progress.MESSAGE_CONSTRAINTS); + + // valid progress followed by invalid progress. The test case for invalid progress followed by valid progress + // is tested at {@code parse_invalidValueFollowedByValidValue_success()} + assertParseFailure(parser, "1" + PROGRESS_DESC_BOB + INVALID_PROGRESS_DESC, + Progress.MESSAGE_CONSTRAINTS); + + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Bookmark} being edited, + // parsing it together with a valid tag results in error + assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, + Tag.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_TITLE_DESC + INVALID_GENRE_DESC + + VALID_AUTHOR_AMY + VALID_PROGRESS_AMY, + Title.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_SECOND_BOOKMARK; + String userInput = targetIndex.getOneBased() + PROGRESS_DESC_BOB + TAG_DESC_HUSBAND + + GENRE_DESC_AMY + AUTHOR_DESC_AMY + TITLE_DESC_AMY + TAG_DESC_FRIEND + + RATING_DESC_AMY + URL_DESC_AMY; + + EditCommand.EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder().withTitle(VALID_TITLE_AMY) + .withProgress(VALID_PROGRESS_BOB).withGenre(VALID_GENRE_AMY).withAuthor(VALID_AUTHOR_AMY) + .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).withRating(VALID_RATING_AMY) + .withUrl(VALID_URL_AMY).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = INDEX_FIRST_BOOKMARK; + String userInput = targetIndex.getOneBased() + PROGRESS_DESC_BOB + GENRE_DESC_AMY; + + EditCommand.EditBookmarkDescriptor descriptor = + new EditBookmarkDescriptorBuilder().withProgress(VALID_PROGRESS_BOB) + .withGenre(VALID_GENRE_AMY).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // name + Index targetIndex = INDEX_THIRD_BOOKMARK; + String userInput = targetIndex.getOneBased() + TITLE_DESC_AMY; + EditCommand.EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder() + .withTitle(VALID_TITLE_AMY).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // progress + userInput = targetIndex.getOneBased() + PROGRESS_DESC_AMY; + descriptor = new EditBookmarkDescriptorBuilder().withProgress(VALID_PROGRESS_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // email + userInput = targetIndex.getOneBased() + GENRE_DESC_AMY; + descriptor = new EditBookmarkDescriptorBuilder().withGenre(VALID_GENRE_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // author + userInput = targetIndex.getOneBased() + AUTHOR_DESC_AMY; + descriptor = new EditBookmarkDescriptorBuilder().withAuthor(VALID_AUTHOR_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // tags + userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; + descriptor = new EditBookmarkDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // rating + userInput = targetIndex.getOneBased() + RATING_DESC_AMY; + descriptor = new EditBookmarkDescriptorBuilder().withRating(VALID_RATING_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // url + userInput = targetIndex.getOneBased() + URL_DESC_AMY; + descriptor = new EditBookmarkDescriptorBuilder().withUrl(VALID_URL_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + Index targetIndex = INDEX_FIRST_BOOKMARK; + String userInput = targetIndex.getOneBased() + PROGRESS_DESC_AMY + AUTHOR_DESC_AMY + GENRE_DESC_AMY + + TAG_DESC_FRIEND + PROGRESS_DESC_AMY + AUTHOR_DESC_AMY + GENRE_DESC_AMY + TAG_DESC_FRIEND + + PROGRESS_DESC_BOB + AUTHOR_DESC_BOB + GENRE_DESC_BOB + TAG_DESC_HUSBAND; + + EditCommand.EditBookmarkDescriptor descriptor = + new EditBookmarkDescriptorBuilder().withProgress(VALID_PROGRESS_BOB) + .withGenre(VALID_GENRE_BOB).withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) + .build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + Index targetIndex = INDEX_FIRST_BOOKMARK; + String userInput = targetIndex.getOneBased() + INVALID_PROGRESS_DESC + PROGRESS_DESC_BOB; + EditCommand.EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder() + .withProgress(VALID_PROGRESS_BOB).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = targetIndex.getOneBased() + GENRE_DESC_BOB + INVALID_PROGRESS_DESC + AUTHOR_DESC_BOB + + PROGRESS_DESC_BOB; + descriptor = new EditBookmarkDescriptorBuilder().withProgress(VALID_PROGRESS_BOB).withGenre(VALID_GENRE_BOB) + .withAuthor(VALID_AUTHOR_BOB).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_resetTags_success() { + Index targetIndex = INDEX_THIRD_BOOKMARK; + String userInput = targetIndex.getOneBased() + TAG_EMPTY; + + EditCommand.EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder().withTags().build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/library/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/library/logic/parser/FindCommandParserTest.java new file mode 100644 index 00000000000..7787113ad4d --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/FindCommandParserTest.java @@ -0,0 +1,65 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.FindCommand; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; + +public class FindCommandParserTest { + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); + + private FindCommandParser parser = new FindCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_validArgs_returnsFindCommand() { + // no leading and trailing whitespaces for title + FindCommand expectedFindCommand = + new FindCommand(new BookmarkContainsKeywordsPredicate( + Arrays.asList("Alice", "Bob"), null, null, null)); + assertParseSuccess(parser, " n/ Alice Bob", expectedFindCommand); + + // multiple whitespaces between keywords for title + assertParseSuccess(parser, " n/ \n Alice \n \t Bob \t", expectedFindCommand); + + // valid genre + expectedFindCommand = new FindCommand(new BookmarkContainsKeywordsPredicate( + null, Arrays.asList("Fantasy"), null, null)); + assertParseSuccess(parser, " g/ Fantasy", expectedFindCommand); + + // valid tag + expectedFindCommand = new FindCommand(new BookmarkContainsKeywordsPredicate( + null, null, Arrays.asList("Gore"), null)); + assertParseSuccess(parser, " t/ Gore", expectedFindCommand); + + // no leading and trailing whitespaces for title + expectedFindCommand = new FindCommand(new BookmarkContainsKeywordsPredicate( + null, null, null, Arrays.asList("Alice", "Bob"))); + assertParseSuccess(parser, " a/ Alice Bob", expectedFindCommand); + + // multiple whitespaces between keywords for title + assertParseSuccess(parser, " a/ \n Alice \n \t Bob \t", expectedFindCommand); + } + + @Test + public void parse_missingParts_failure() { + // no field specified + assertParseFailure(parser, "Alice", MESSAGE_INVALID_FORMAT); + + // invalid prefix used + assertParseFailure(parser, "u/ Alice", MESSAGE_INVALID_FORMAT); + } + +} diff --git a/src/test/java/seedu/library/logic/parser/GoToCommandParserTest.java b/src/test/java/seedu/library/logic/parser/GoToCommandParserTest.java new file mode 100644 index 00000000000..515f8f7b5e8 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/GoToCommandParserTest.java @@ -0,0 +1,26 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.GoToCommand; + + + +public class GoToCommandParserTest { + private GoToCommandParser parser = new GoToCommandParser(); + + @Test + public void parse_validArgs_returnsGoToCommand() { + assertParseSuccess(parser, "1", new GoToCommand(INDEX_FIRST_BOOKMARK)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, GoToCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/library/logic/parser/LibraryParserTest.java similarity index 50% rename from src/test/java/seedu/address/logic/parser/AddressBookParserTest.java rename to src/test/java/seedu/library/logic/parser/LibraryParserTest.java index d9659205b57..ece8bcac107 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/library/logic/parser/LibraryParserTest.java @@ -1,11 +1,12 @@ -package seedu.address.logic.parser; +package seedu.library.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; import java.util.Arrays; import java.util.List; @@ -13,31 +14,31 @@ import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; -import seedu.address.testutil.PersonUtil; - -public class AddressBookParserTest { - - private final AddressBookParser parser = new AddressBookParser(); +import seedu.library.logic.commands.AddCommand; +import seedu.library.logic.commands.ClearCommand; +import seedu.library.logic.commands.DeleteCommand; +import seedu.library.logic.commands.EditCommand; +import seedu.library.logic.commands.EditCommand.EditBookmarkDescriptor; +import seedu.library.logic.commands.ExitCommand; +import seedu.library.logic.commands.FindCommand; +import seedu.library.logic.commands.HelpCommand; +import seedu.library.logic.commands.ListCommand; +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; +import seedu.library.testutil.BookmarkBuilder; +import seedu.library.testutil.BookmarkUtil; +import seedu.library.testutil.EditBookmarkDescriptorBuilder; + +public class LibraryParserTest { + + private final LibraryParser parser = new LibraryParser(); @Test public void parseCommand_add() throws Exception { - Person person = new PersonBuilder().build(); - AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); - assertEquals(new AddCommand(person), command); + Bookmark bookmark = new BookmarkBuilder().build(); + AddCommand command = (AddCommand) parser.parseCommand(BookmarkUtil.getAddCommand(bookmark)); + assertEquals(new AddCommand(bookmark), command); } @Test @@ -49,17 +50,17 @@ public void parseCommand_clear() throws Exception { @Test public void parseCommand_delete() throws Exception { DeleteCommand command = (DeleteCommand) parser.parseCommand( - DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); - assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); + DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_BOOKMARK.getOneBased()); + assertEquals(new DeleteCommand(INDEX_FIRST_BOOKMARK), command); } @Test public void parseCommand_edit() throws Exception { - Person person = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); + Bookmark bookmark = new BookmarkBuilder().build(); + EditBookmarkDescriptor descriptor = new EditBookmarkDescriptorBuilder(bookmark).build(); EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); - assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); + + INDEX_FIRST_BOOKMARK.getOneBased() + " " + BookmarkUtil.getEditBookmarkDescriptorDetails(descriptor)); + assertEquals(new EditCommand(INDEX_FIRST_BOOKMARK, descriptor), command); } @Test @@ -72,8 +73,10 @@ public void parseCommand_exit() throws Exception { public void parseCommand_find() throws Exception { List<String> keywords = Arrays.asList("foo", "bar", "baz"); FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); + FindCommand.COMMAND_WORD + " " + PREFIX_TITLE + " " + + keywords.stream().collect(Collectors.joining(" "))); + assertEquals(new FindCommand( + new BookmarkContainsKeywordsPredicate(keywords, null, null, null)), command); } @Test diff --git a/src/test/java/seedu/library/logic/parser/ParserUtilTest.java b/src/test/java/seedu/library/logic/parser/ParserUtilTest.java new file mode 100644 index 00000000000..0226850681b --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/ParserUtilTest.java @@ -0,0 +1,206 @@ +package seedu.library.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.parser.exceptions.ParseException; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Title; +import seedu.library.model.tag.Tag; + +public class ParserUtilTest { + public static final String INVALID_TITLE_LENGTH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + private static final String INVALID_TITLE = " "; + private static final String INVALID_PROGRESS = "+651234"; + private static final String INVALID_AUTHOR = " "; + private static final String INVALID_GENRE = " "; + private static final String INVALID_TAG = "#friend"; + + private static final String VALID_TITLE = "Rachel Walker"; + private static final String VALID_PROGRESS_INPUT = "1 57 25"; + private static final String[] VALID_PROGRESS_ARRAY = new String[]{"1", "57", "25"}; + private static final String VALID_AUTHOR = "123 Main Street #0505"; + private static final String VALID_GENRE = "Fantasy"; + private static final String VALID_TAG_1 = "friend"; + private static final String VALID_TAG_2 = "neighbour"; + + private static final String WHITESPACE = " \t\r\n"; + + @Test + public void parseIndex_invalidInput_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); + } + + @Test + public void parseIndex_outOfRangeInput_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () + -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); + } + + @Test + public void parseIndex_validInput_success() throws Exception { + // No whitespaces + assertEquals(INDEX_FIRST_BOOKMARK, ParserUtil.parseIndex("1")); + + // Leading and trailing whitespaces + assertEquals(INDEX_FIRST_BOOKMARK, ParserUtil.parseIndex(" 1 ")); + } + + @Test + public void parseTitle_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseTitle((String) null)); + } + + @Test + public void parseTitle_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseTitle(INVALID_TITLE)); + } + + @Test + public void parseTitle_invalidLength_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseTitle(INVALID_TITLE_LENGTH)); + } + + @Test + public void parseTitle_validValueWithoutWhitespace_returnsTitle() throws Exception { + Title expectedTitle = new Title(VALID_TITLE); + assertEquals(expectedTitle, ParserUtil.parseTitle(VALID_TITLE)); + } + + @Test + public void parseTitle_validValueWithWhitespace_returnsTrimmedTitle() throws Exception { + String titleWithWhitespace = WHITESPACE + VALID_TITLE + WHITESPACE; + Title expectedTitle = new Title(VALID_TITLE); + assertEquals(expectedTitle, ParserUtil.parseTitle(titleWithWhitespace)); + } + + @Test + public void parseProgress_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseProgress((String) null)); + } + + @Test + public void parseProgress_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseProgress(INVALID_PROGRESS)); + } + + @Test + public void parseProgress_validValueWithoutWhitespace_returnsProgress() throws Exception { + Progress expectedProgress = new Progress(VALID_PROGRESS_ARRAY); + assertEquals(expectedProgress, ParserUtil.parseProgress(VALID_PROGRESS_INPUT)); + } + + @Test + public void parseProgress_validValueWithWhitespace_returnsTrimmedProgress() throws Exception { + String progressWithWhitespace = WHITESPACE + VALID_PROGRESS_INPUT + WHITESPACE; + Progress expectedProgress = new Progress(VALID_PROGRESS_ARRAY); + assertEquals(expectedProgress, ParserUtil.parseProgress(progressWithWhitespace)); + } + + @Test + public void parseAuthor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseAuthor((String) null)); + } + + @Test + public void parseAuthor_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseAuthor(INVALID_AUTHOR)); + } + + @Test + public void parseAuthor_validValueWithoutWhitespace_returnsAuthor() throws Exception { + Author expectedAuthor = new Author(VALID_AUTHOR); + assertEquals(expectedAuthor, ParserUtil.parseAuthor(VALID_AUTHOR)); + } + + @Test + public void parseAuthor_validValueWithWhitespace_returnsTrimmedAuthor() throws Exception { + String authorWithWhitespace = WHITESPACE + VALID_AUTHOR + WHITESPACE; + Author expectedAuthor = new Author(VALID_AUTHOR); + assertEquals(expectedAuthor, ParserUtil.parseAuthor(authorWithWhitespace)); + } + + @Test + public void parseGenre_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseGenre((String) null)); + } + + @Test + public void parseGenre_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseGenre(INVALID_GENRE)); + } + + @Test + public void parseGenre_validValueWithoutWhitespace_returnsGenre() throws Exception { + Genre expectedGenre = new Genre(VALID_GENRE); + assertEquals(expectedGenre, ParserUtil.parseGenre(VALID_GENRE)); + } + + @Test + public void parseGenre_validValueWithWhitespace_returnsTrimmedGenre() throws Exception { + String emailWithWhitespace = WHITESPACE + VALID_GENRE + WHITESPACE; + Genre expectedGenre = new Genre(VALID_GENRE); + assertEquals(expectedGenre, ParserUtil.parseGenre(emailWithWhitespace)); + } + + @Test + public void parseTag_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); + } + + @Test + public void parseTag_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); + } + + @Test + public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { + Tag expectedTag = new Tag(VALID_TAG_1); + assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); + } + + @Test + public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { + String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; + Tag expectedTag = new Tag(VALID_TAG_1); + assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); + } + + @Test + public void parseTags_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); + } + + @Test + public void parseTags_collectionWithInvalidTags_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); + } + + @Test + public void parseTags_emptyCollection_returnsEmptySet() throws Exception { + assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); + } + + @Test + public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { + Set<Tag> actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); + Set<Tag> expectedTagSet = new HashSet<Tag>(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); + + assertEquals(expectedTagSet, actualTagSet); + } +} diff --git a/src/test/java/seedu/library/logic/parser/SortCommandParserTest.java b/src/test/java/seedu/library/logic/parser/SortCommandParserTest.java new file mode 100644 index 00000000000..a40124b8a18 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/SortCommandParserTest.java @@ -0,0 +1,26 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.SortCommand; + +public class SortCommandParserTest { + private SortCommandParser parser = new SortCommandParser(); + + @Test + public void parse_validInputs_success() { + assertParseSuccess(parser, "asc", new SortCommand("asc")); + + assertParseSuccess(parser, "desc", new SortCommand("desc")); + } + + @Test + public void parse_invalidInput_failure() { + assertParseFailure(parser, "invalid", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/library/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/library/logic/parser/ViewCommandParserTest.java new file mode 100644 index 00000000000..a698b631dd1 --- /dev/null +++ b/src/test/java/seedu/library/logic/parser/ViewCommandParserTest.java @@ -0,0 +1,26 @@ +package seedu.library.logic.parser; + +import static seedu.library.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.library.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.library.testutil.TypicalIndexes.INDEX_FIRST_BOOKMARK; + +import org.junit.jupiter.api.Test; + +import seedu.library.logic.commands.ViewCommand; + + + +public class ViewCommandParserTest { + private ViewCommandParser parser = new ViewCommandParser(); + + @Test + public void parse_validArgs_returnsGoToCommand() { + assertParseSuccess(parser, "1", new ViewCommand(INDEX_FIRST_BOOKMARK)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/library/model/LibraryTest.java b/src/test/java/seedu/library/model/LibraryTest.java new file mode 100644 index 00000000000..3d348b01adf --- /dev/null +++ b/src/test/java/seedu/library/model/LibraryTest.java @@ -0,0 +1,102 @@ +package seedu.library.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.AOT; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.exceptions.DuplicateBookmarkException; +import seedu.library.testutil.BookmarkBuilder; + +public class LibraryTest { + + private final Library library = new Library(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), library.getBookmarkList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> library.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyLibrary_replacesData() { + Library newData = getTypicalLibrary(); + library.resetData(newData); + assertEquals(newData, library); + } + + @Test + public void resetData_withDuplicateBookmarks_throwsDuplicateBookmarkException() { + // Two Bookmarks with the same identity fields + Bookmark editedAlice = new BookmarkBuilder(AOT).withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_HUSBAND) + .build(); + List<Bookmark> newBookmarks = Arrays.asList(AOT, editedAlice); + LibraryStub newData = new LibraryStub(newBookmarks); + + assertThrows(DuplicateBookmarkException.class, () -> library.resetData(newData)); + } + + @Test + public void hasBookmark_nullBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> library.hasBookmark(null)); + } + + @Test + public void hasBookmark_bookmarkNotInLibrary_returnsFalse() { + assertFalse(library.hasBookmark(AOT)); + } + + @Test + public void hasBookmark_bookmarkInLibrary_returnsTrue() { + library.addBookmark(AOT); + assertTrue(library.hasBookmark(AOT)); + } + + @Test + public void hasBookmark_bookmarkWithSameIdentityFieldsInLibrary_returnsTrue() { + library.addBookmark(AOT); + Bookmark editedAlice = new BookmarkBuilder(AOT).withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_HUSBAND) + .build(); + assertTrue(library.hasBookmark(editedAlice)); + } + + @Test + public void getBookmarkList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> library.getBookmarkList().remove(0)); + } + + /** + * A stub ReadOnlyLibrary whose Bookmarks list can violate interface constraints. + */ + private static class LibraryStub implements ReadOnlyLibrary { + private final ObservableList<Bookmark> bookmarks = FXCollections.observableArrayList(); + + LibraryStub(Collection<Bookmark> bookmarks) { + this.bookmarks.setAll(bookmarks); + } + + @Override + public ObservableList<Bookmark> getBookmarkList() { + return bookmarks; + } + } + +} diff --git a/src/test/java/seedu/library/model/ModelManagerTest.java b/src/test/java/seedu/library/model/ModelManagerTest.java new file mode 100644 index 00000000000..dee6faa61f4 --- /dev/null +++ b/src/test/java/seedu/library/model/ModelManagerTest.java @@ -0,0 +1,153 @@ +package seedu.library.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.model.Model.PREDICATE_SHOW_ALL_BOOKMARKS; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.AOT; +import static seedu.library.testutil.TypicalBookmarks.BENSON; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.core.GuiSettings; +import seedu.library.model.bookmark.BookmarkContainsKeywordsPredicate; +import seedu.library.testutil.LibraryBuilder; + +public class ModelManagerTest { + + private ModelManager modelManager = new ModelManager(); + + @Test + public void constructor() { + assertEquals(new UserPrefs(), modelManager.getUserPrefs()); + assertEquals(new GuiSettings(), modelManager.getGuiSettings()); + assertEquals(new Library(), new Library(modelManager.getLibrary())); + } + + @Test + public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null)); + } + + @Test + public void setUserPrefs_validUserPrefs_copiesUserPrefs() { + UserPrefs userPrefs = new UserPrefs(); + userPrefs.setLibraryFilePath(Paths.get("address/book/file/path")); + userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); + modelManager.setUserPrefs(userPrefs); + assertEquals(userPrefs, modelManager.getUserPrefs()); + + // Modifying userPrefs should not modify modelManager's userPrefs + UserPrefs oldUserPrefs = new UserPrefs(userPrefs); + userPrefs.setLibraryFilePath(Paths.get("new/address/book/file/path")); + assertEquals(oldUserPrefs, modelManager.getUserPrefs()); + } + + @Test + public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setGuiSettings(null)); + } + + @Test + public void setGuiSettings_validGuiSettings_setsGuiSettings() { + GuiSettings guiSettings = new GuiSettings(1, 2, 3, 4); + modelManager.setGuiSettings(guiSettings); + assertEquals(guiSettings, modelManager.getGuiSettings()); + } + + @Test + public void setLibraryFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setLibraryFilePath(null)); + } + + @Test + public void setLibraryFilePath_validPath_setsLibraryFilePath() { + Path path = Paths.get("address/book/file/path"); + modelManager.setLibraryFilePath(path); + assertEquals(path, modelManager.getLibraryFilePath()); + } + + @Test + public void hasBookmark_nullBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.hasBookmark(null)); + } + + @Test + public void hasBookmark_bookmarkNotInLibrary_returnsFalse() { + assertFalse(modelManager.hasBookmark(AOT)); + } + + @Test + public void hasBookmark_bookmarkInLibrary_returnsTrue() { + modelManager.addBookmark(AOT); + assertTrue(modelManager.hasBookmark(AOT)); + } + + @Test + public void getFilteredBookmarkList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredBookmarkList().remove(0)); + } + + @Test + public void equals() { + Library library = new LibraryBuilder().withBookmark(AOT).withBookmark(BENSON).build(); + Library differentLibrary = new Library(); + UserPrefs userPrefs = new UserPrefs(); + Tags tags = new Tags(); + + // same values -> returns true + modelManager = new ModelManager(library, userPrefs, tags); + ModelManager modelManagerCopy = new ModelManager(library, userPrefs, tags); + assertTrue(modelManager.equals(modelManagerCopy)); + + // same object -> returns true + assertTrue(modelManager.equals(modelManager)); + + // null -> returns false + assertFalse(modelManager.equals(null)); + + // different types -> returns false + assertFalse(modelManager.equals(5)); + + // different Library -> returns false + assertFalse(modelManager.equals(new ModelManager(differentLibrary, userPrefs, tags))); + + // different filteredList -> returns false + String[] keywords = AOT.getTitle().value.split("\\s+"); + modelManager.updateFilteredBookmarkList( + new BookmarkContainsKeywordsPredicate(Arrays.asList(keywords), null, null, null)); + assertFalse(modelManager.equals(new ModelManager(library, userPrefs, tags))); + + // resets modelManager to initial state for upcoming tests + modelManager.updateFilteredBookmarkList(PREDICATE_SHOW_ALL_BOOKMARKS); + + // different userPrefs -> returns false + UserPrefs differentUserPrefs = new UserPrefs(); + differentUserPrefs.setLibraryFilePath(Paths.get("differentFilePath")); + assertFalse(modelManager.equals(new ModelManager(library, differentUserPrefs, tags))); + } + + @Test + public void getSelectedBookmark_default_success() { + assertEquals(modelManager.getSelectedBookmark(), null); + } + @Test + public void getSelectedIndex_default_success() { + assertEquals(modelManager.getSelectedIndex(), -1); + } + @Test + public void getUpdatedBookmark_success() { + modelManager.updateSelectedBookmark(AOT); + assertEquals(modelManager.getSelectedBookmark(), AOT); + } + @Test + public void getUpdatedIndex_success() { + modelManager.updateSelectedIndex(1); + assertEquals(modelManager.getSelectedIndex(), 1); + } +} diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/library/model/UserPrefsTest.java similarity index 68% rename from src/test/java/seedu/address/model/UserPrefsTest.java rename to src/test/java/seedu/library/model/UserPrefsTest.java index b1307a70d52..01ee3d63002 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/seedu/library/model/UserPrefsTest.java @@ -1,6 +1,6 @@ -package seedu.address.model; +package seedu.library.model; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -13,9 +13,9 @@ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { + public void setLibraryFilePath_nullPath_throwsNullPointerException() { UserPrefs userPrefs = new UserPrefs(); - assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); + assertThrows(NullPointerException.class, () -> userPrefs.setLibraryFilePath(null)); } } diff --git a/src/test/java/seedu/library/model/bookmark/AuthorTest.java b/src/test/java/seedu/library/model/bookmark/AuthorTest.java new file mode 100644 index 00000000000..7bb463f075a --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/AuthorTest.java @@ -0,0 +1,36 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class AuthorTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Author(null)); + } + + @Test + public void constructor_invalidAuthor_throwsIllegalArgumentException() { + String invalidAuthor = ""; + assertThrows(IllegalArgumentException.class, () -> new Author(invalidAuthor)); + } + + @Test + public void isValidAuthor() { + // null author + assertThrows(NullPointerException.class, () -> Author.isValidAuthor(null)); + + // invalid author + assertFalse(Author.isValidAuthor("")); // empty string + assertFalse(Author.isValidAuthor(" ")); // spaces only + + // valid author + assertTrue(Author.isValidAuthor("Eiichiro Oda")); + assertTrue(Author.isValidAuthor("-")); // one character + assertTrue(Author.isValidAuthor("Ratko Adamović")); // Author name with special character + } +} diff --git a/src/test/java/seedu/library/model/bookmark/BookmarkContainsKeywordsPredicateTest.java b/src/test/java/seedu/library/model/bookmark/BookmarkContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..66aaa433bc5 --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/BookmarkContainsKeywordsPredicateTest.java @@ -0,0 +1,168 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.library.testutil.BookmarkBuilder; + +public class BookmarkContainsKeywordsPredicateTest { + + @Test + public void equals() { + List<String> firstTitleKeywordList = Collections.singletonList("first"); + List<String> secondTitleKeywordList = Arrays.asList("first", "second"); + + BookmarkContainsKeywordsPredicate firstPredicate = + new BookmarkContainsKeywordsPredicate(firstTitleKeywordList, null, null, null); + BookmarkContainsKeywordsPredicate secondPredicate = + new BookmarkContainsKeywordsPredicate(secondTitleKeywordList, null, null, null); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + BookmarkContainsKeywordsPredicate firstPredicateCopy = + new BookmarkContainsKeywordsPredicate(firstTitleKeywordList, null, null, null); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different bookmark -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_titleContainsKeywords_returnsTrue() { + // One keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + Collections.singletonList("Alice"), null, null, null + ); + assertTrue(predicate.test(new BookmarkBuilder().withTitle("Alice Bob").build())); + + // Multiple keywords + predicate = new BookmarkContainsKeywordsPredicate( + Arrays.asList("Alice", "Bob"), null, null, null); + assertTrue(predicate.test(new BookmarkBuilder().withTitle("Alice Bob").build())); + + // Mixed-case keywords + predicate = new BookmarkContainsKeywordsPredicate( + Arrays.asList("aLIce", "bOB"), null, null, null); + assertTrue(predicate.test(new BookmarkBuilder().withTitle("Alice Bob").build())); + } + + @Test + public void test_titleDoesNotContainKeywords_returnsFalse() { + // Non-matching keyword + BookmarkContainsKeywordsPredicate predicate = + new BookmarkContainsKeywordsPredicate(Arrays.asList("Carol"), null, null, null); + assertFalse(predicate.test(new BookmarkBuilder().withTitle("Alice Bob").build())); + + // Only one matching keyword + predicate = new BookmarkContainsKeywordsPredicate( + Arrays.asList("Bob", "Carol"), null, null, null); + assertFalse(predicate.test(new BookmarkBuilder().withTitle("Alice Carol").build())); + + } + + @Test + public void test_genreContainsKeywords_returnsTrue() { + // One keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, Collections.singletonList("Fantasy"), null, null + ); + assertTrue(predicate.test(new BookmarkBuilder().withGenre("Fantasy").build())); + } + + @Test + public void test_genreContainsKeywords_returnsFalse() { + // Non-matching keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, Collections.singletonList("Fantasy"), null, null + ); + assertFalse(predicate.test(new BookmarkBuilder().withGenre("Action").build())); + } + + @Test + public void test_tagContainsKeywords_returnsTrue() { + // One keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, null, Collections.singletonList("Hunters"), null + ); + assertTrue(predicate.test(new BookmarkBuilder().withTags("Hunters").build())); + } + + @Test + public void test_tagContainsKeywords_returnsFalse() { + // Non-matching keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, null, Collections.singletonList("Hunters"), null + ); + assertFalse(predicate.test(new BookmarkBuilder().withTags("Gore").build())); + } + + @Test + public void test_authorContainsKeywords_returnsTrue() { + // One keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, null, null, Collections.singletonList("Carol") + ); + assertTrue(predicate.test(new BookmarkBuilder().withAuthor("Carol Lee").build())); + + // Multiple keyword + predicate = new BookmarkContainsKeywordsPredicate( + null, null, null, Arrays.asList("Mark", "Tan") + ); + assertTrue(predicate.test(new BookmarkBuilder().withAuthor("Mark Tan").build())); + + // Mix case keyword + predicate = new BookmarkContainsKeywordsPredicate( + null, null, null, Collections.singletonList("MaRK") + ); + assertTrue(predicate.test(new BookmarkBuilder().withAuthor("Mark").build())); + } + + @Test + public void test_authorContainsKeywords_returnsFalse() { + // Non-matching keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, null, Collections.singletonList("Carol"), null + ); + assertFalse(predicate.test(new BookmarkBuilder().withTags("Mark").build())); + } + + @Test + public void test_bookmarkHasNoAuthor_returnsFalse() { + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, null, null, Collections.singletonList("Carol") + ); + assertFalse(predicate.test(new BookmarkBuilder().withAuthor(null).build())); + } + + @Test + public void test_multipleFieldsContainKeywords_returnsTrue() { + // Non-matching keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, Collections.singletonList("Fantasy"), Collections.singletonList("Carol"), null + ); + assertTrue(predicate.test(new BookmarkBuilder().withGenre("Fantasy").withTags("Carol").build())); + } + + @Test + public void test_multipleFieldsContainKeywords_returnsFalse() { + // Non-matching keyword + BookmarkContainsKeywordsPredicate predicate = new BookmarkContainsKeywordsPredicate( + null, Collections.singletonList("Fantasy"), Collections.singletonList("Carol"), null + ); + assertFalse(predicate.test(new BookmarkBuilder().withGenre("Fantasy").build())); + } +} diff --git a/src/test/java/seedu/library/model/bookmark/BookmarkTest.java b/src/test/java/seedu/library/model/bookmark/BookmarkTest.java new file mode 100644 index 00000000000..059780fa43a --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/BookmarkTest.java @@ -0,0 +1,87 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_BOB; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.AOT; +import static seedu.library.testutil.TypicalBookmarks.AOTString; +import static seedu.library.testutil.TypicalBookmarks.BOB; + +import org.junit.jupiter.api.Test; + +import seedu.library.testutil.BookmarkBuilder; + +public class BookmarkTest { + + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + Bookmark bookmark = new BookmarkBuilder().build(); + assertThrows(UnsupportedOperationException.class, () -> bookmark.getTags().remove(0)); + } + + @Test + public void isSameBookmark() { + // same object -> returns true + assertTrue(AOT.isSameBookmark(AOT)); + + // null -> returns false + assertFalse(AOT.isSameBookmark(null)); + + // same name, all other attributes different -> returns true + Bookmark editedAlice = new BookmarkBuilder(AOT).withProgress(VALID_PROGRESS_BOB).withGenre(VALID_GENRE_BOB) + .withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_HUSBAND).build(); + assertTrue(AOT.isSameBookmark(editedAlice)); + + // different name, all other attributes same -> returns false + editedAlice = new BookmarkBuilder(AOT).withTitle(VALID_TITLE_BOB).build(); + assertFalse(AOT.isSameBookmark(editedAlice)); + + // name differs in case, all other attributes same -> returns false + Bookmark editedBob = new BookmarkBuilder(BOB).withTitle(VALID_TITLE_BOB.toLowerCase()).build(); + assertFalse(BOB.isSameBookmark(editedBob)); + + // name has trailing spaces, all other attributes same -> returns false + String nameWithTrailingSpaces = VALID_TITLE_BOB + " "; + editedBob = new BookmarkBuilder(BOB).withTitle(nameWithTrailingSpaces).build(); + assertFalse(BOB.isSameBookmark(editedBob)); + } + + @Test + public void equals() { + // same values -> returns true + Bookmark aliceCopy = new BookmarkBuilder(AOT).build(); + assertTrue(AOT.equals(aliceCopy)); + + // same object -> returns true + assertTrue(AOT.equals(AOT)); + + // null -> returns false + assertFalse(AOT.equals(null)); + + // different type -> returns false + assertFalse(AOT.equals(5)); + + // different bookmark -> returns false + assertFalse(AOT.equals(BOB)); + + // different name -> returns false + Bookmark editedAlice = new BookmarkBuilder(AOT).withTitle(VALID_TITLE_BOB).build(); + assertFalse(AOT.equals(editedAlice)); + + // different genre -> returns false + editedAlice = new BookmarkBuilder(AOT).withGenre(VALID_GENRE_BOB).build(); + assertFalse(AOT.equals(editedAlice)); + } + + @Test + public void validToString() { + Bookmark bookmark = new BookmarkBuilder(AOT).build(); + assertEquals(AOTString, bookmark.toString()); + } +} diff --git a/src/test/java/seedu/library/model/bookmark/GenreTest.java b/src/test/java/seedu/library/model/bookmark/GenreTest.java new file mode 100644 index 00000000000..d80ee527820 --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/GenreTest.java @@ -0,0 +1,37 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class GenreTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Genre(null)); + } + + @Test + public void constructor_invalidGenre_throwsIllegalArgumentException() { + String invalidGenre = ""; + assertThrows(IllegalArgumentException.class, () -> new Genre(invalidGenre)); + } + + @Test + public void isValidGenre() { + // null genre + assertThrows(NullPointerException.class, () -> Genre.isValidGenre(null)); + + // blank genre + assertFalse(Genre.isValidGenre("")); // empty string + assertFalse(Genre.isValidGenre(" ")); // spaces only + + // valid genre + assertTrue(Genre.isValidGenre("Action")); + + // invalid genre + assertFalse(Genre.isValidGenre("Research")); + } +} diff --git a/src/test/java/seedu/library/model/bookmark/ProgressTest.java b/src/test/java/seedu/library/model/bookmark/ProgressTest.java new file mode 100644 index 00000000000..23c2d44806e --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/ProgressTest.java @@ -0,0 +1,35 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class ProgressTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Progress(null)); + } + + @Test + public void constructor_invalidProgress_throwsIllegalArgumentException() { + String[] invalidProgress = {"", "a", "!"}; + assertThrows(IllegalArgumentException.class, () -> new Progress(invalidProgress)); + } + + @Test + public void isValidProgress() { + // null progress + assertThrows(NullPointerException.class, () -> Progress.isValidProgress(null)); + + // invalid progress + assertFalse(Progress.isValidProgress(new String[]{" ", " ", " "})); // empty string + assertFalse(Progress.isValidProgress(new String[]{"a", "*", "!"})); // non-numeric + assertFalse(Progress.isValidProgress(new String[]{"~", "~", "~"})); // all ~ + // valid progress + assertTrue(Progress.isValidProgress(new String[]{"1", "65", "24"})); // all numeric + assertTrue(Progress.isValidProgress(new String[]{"~", "32", "~"})); // numerics + ~ + } +} diff --git a/src/test/java/seedu/library/model/bookmark/RatingTest.java b/src/test/java/seedu/library/model/bookmark/RatingTest.java new file mode 100644 index 00000000000..12098e39194 --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/RatingTest.java @@ -0,0 +1,44 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class RatingTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Rating(null)); + } + + @Test + public void constructor_invalidRating_throwsIllegalArgumentException() { + String invalidRating = ""; + assertThrows(IllegalArgumentException.class, () -> new Rating(invalidRating)); + } + + @Test + public void isValidRating() { + // null rating + assertThrows(NullPointerException.class, () -> Rating.isValidRating(null)); + + // invalid rating + assertFalse(Rating.isValidRating("")); // empty string + assertFalse(Rating.isValidRating("-1")); // negative value + assertFalse(Rating.isValidRating("6")); // value above 5 + + // valid rating + assertTrue(Rating.isValidRating("0")); // lowest value possible + assertTrue(Rating.isValidRating("5")); // highest value possible + assertTrue(Rating.isValidRating("3")); // value in valid range + } + + @Test + public void validStringRepresentation() { + Rating rating = new Rating("4"); + assertEquals("4", rating.toString()); + } +} diff --git a/src/test/java/seedu/library/model/bookmark/TitleTest.java b/src/test/java/seedu/library/model/bookmark/TitleTest.java new file mode 100644 index 00000000000..5ec5bc87513 --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/TitleTest.java @@ -0,0 +1,45 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class TitleTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Title(null)); + } + + @Test + public void constructor_invalidTitle_throwsIllegalArgumentException() { + String invalidTitle = ""; + assertThrows(IllegalArgumentException.class, () -> new Title(invalidTitle)); + } + + @Test + public void isValidTitle() { + // null title + assertThrows(NullPointerException.class, () -> Title.isValidTitle(null)); + + // invalid title + assertFalse(Title.isValidTitle("")); // empty string + assertFalse(Title.isValidTitle(" ")); // spaces only + assertFalse(Title.isValidTitle("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")); // above 180 characters + + // valid title + assertTrue(Title.isValidTitle("peter jack")); // alphabets only + assertTrue(Title.isValidTitle("12345")); // numbers only + assertTrue(Title.isValidTitle("peter the 2nd")); // alphanumeric characters + assertTrue(Title.isValidTitle("Capital Tan")); // with capital letters + assertTrue(Title.isValidTitle("David Roger Jackson Ray Jr 2nd")); // long names + assertTrue(Title.isValidTitle("^")); // only non-alphanumeric characters + assertTrue(Title.isValidTitle("peter*")); // contains non-alphanumeric characters + } +} diff --git a/src/test/java/seedu/library/model/bookmark/UniqueBookmarkListTest.java b/src/test/java/seedu/library/model/bookmark/UniqueBookmarkListTest.java new file mode 100644 index 00000000000..7198e61c315 --- /dev/null +++ b/src/test/java/seedu/library/model/bookmark/UniqueBookmarkListTest.java @@ -0,0 +1,171 @@ +package seedu.library.model.bookmark; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.AOT; +import static seedu.library.testutil.TypicalBookmarks.BOB; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.library.model.bookmark.exceptions.BookmarkNotFoundException; +import seedu.library.model.bookmark.exceptions.DuplicateBookmarkException; +import seedu.library.testutil.BookmarkBuilder; + +public class UniqueBookmarkListTest { + + private final UniqueBookmarkList uniqueBookmarkList = new UniqueBookmarkList(); + + @Test + public void contains_nullBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.contains(null)); + } + + @Test + public void contains_bookmarkNotInList_returnsFalse() { + assertFalse(uniqueBookmarkList.contains(AOT)); + } + + @Test + public void contains_bookmarkInList_returnsTrue() { + uniqueBookmarkList.add(AOT); + assertTrue(uniqueBookmarkList.contains(AOT)); + } + + @Test + public void contains_bookmarkWithSameIdentityFieldsInList_returnsTrue() { + uniqueBookmarkList.add(AOT); + Bookmark editedAlice = new BookmarkBuilder(AOT).withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_HUSBAND) + .build(); + assertTrue(uniqueBookmarkList.contains(editedAlice)); + } + + @Test + public void add_nullBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.add(null)); + } + + @Test + public void add_duplicateBookmark_throwsDuplicateBookmarkException() { + uniqueBookmarkList.add(AOT); + assertThrows(DuplicateBookmarkException.class, () -> uniqueBookmarkList.add(AOT)); + } + + @Test + public void setBookmark_nullTargetBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.setBookmark(null, AOT)); + } + + @Test + public void setBookmark_nullEditedBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.setBookmark(AOT, null)); + } + + @Test + public void setBookmark_targetBookmarkNotInList_throwsBookmarkNotFoundException() { + assertThrows(BookmarkNotFoundException.class, () -> uniqueBookmarkList.setBookmark(AOT, AOT)); + } + + @Test + public void setBookmark_editedBookmarkIsSameBookmark_success() { + uniqueBookmarkList.add(AOT); + uniqueBookmarkList.setBookmark(AOT, AOT); + UniqueBookmarkList expectedUniqueBookmarkList = new UniqueBookmarkList(); + expectedUniqueBookmarkList.add(AOT); + assertEquals(expectedUniqueBookmarkList, uniqueBookmarkList); + } + + @Test + public void setBookmark_editedBookmarkHasSameIdentity_success() { + uniqueBookmarkList.add(AOT); + Bookmark editedAlice = new BookmarkBuilder(AOT).withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_HUSBAND) + .build(); + uniqueBookmarkList.setBookmark(AOT, editedAlice); + UniqueBookmarkList expectedUniqueBookmarkList = new UniqueBookmarkList(); + expectedUniqueBookmarkList.add(editedAlice); + assertEquals(expectedUniqueBookmarkList, uniqueBookmarkList); + } + + @Test + public void setBookmark_editedBookmarkHasDifferentIdentity_success() { + uniqueBookmarkList.add(AOT); + uniqueBookmarkList.setBookmark(AOT, BOB); + UniqueBookmarkList expectedUniqueBookmarkList = new UniqueBookmarkList(); + expectedUniqueBookmarkList.add(BOB); + assertEquals(expectedUniqueBookmarkList, uniqueBookmarkList); + } + + @Test + public void setBookmark_editedBookmarkHasNonUniqueIdentity_throwsDuplicateBookmarkException() { + uniqueBookmarkList.add(AOT); + uniqueBookmarkList.add(BOB); + assertThrows(DuplicateBookmarkException.class, () -> uniqueBookmarkList.setBookmark(AOT, BOB)); + } + + @Test + public void remove_nullBookmark_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.remove(null)); + } + + @Test + public void remove_bookmarkDoesNotExist_throwsBookmarkNotFoundException() { + assertThrows(BookmarkNotFoundException.class, () -> uniqueBookmarkList.remove(AOT)); + } + + @Test + public void remove_existingBookmark_removesBookmark() { + uniqueBookmarkList.add(AOT); + uniqueBookmarkList.remove(AOT); + UniqueBookmarkList expectedUniqueBookmarkList = new UniqueBookmarkList(); + assertEquals(expectedUniqueBookmarkList, uniqueBookmarkList); + } + + @Test + public void setBookmarks_nullUniqueBookmarkList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.setBookmarks((UniqueBookmarkList) null)); + } + + @Test + public void setBookmarks_uniqueBookmarkList_replacesOwnListWithProvidedUniqueBookmarkList() { + uniqueBookmarkList.add(AOT); + UniqueBookmarkList expectedUniqueBookmarkList = new UniqueBookmarkList(); + expectedUniqueBookmarkList.add(BOB); + uniqueBookmarkList.setBookmarks(expectedUniqueBookmarkList); + assertEquals(expectedUniqueBookmarkList, uniqueBookmarkList); + } + + @Test + public void setBookmarks_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueBookmarkList.setBookmarks((List<Bookmark>) null)); + } + + @Test + public void setBookmarks_list_replacesOwnListWithProvidedList() { + uniqueBookmarkList.add(AOT); + List<Bookmark> bookmarkList = Collections.singletonList(BOB); + uniqueBookmarkList.setBookmarks(bookmarkList); + UniqueBookmarkList expectedUniqueBookmarkList = new UniqueBookmarkList(); + expectedUniqueBookmarkList.add(BOB); + assertEquals(expectedUniqueBookmarkList, uniqueBookmarkList); + } + + @Test + public void setBookmarks_listWithDuplicateBookmarks_throwsDuplicateBookmarkException() { + List<Bookmark> listWithDuplicateBookmarks = Arrays.asList(AOT, AOT); + assertThrows(DuplicateBookmarkException.class, () -> uniqueBookmarkList + .setBookmarks(listWithDuplicateBookmarks)); + } + + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueBookmarkList.asUnmodifiableObservableList().remove(0)); + } +} diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/library/model/tag/TagTest.java similarity index 86% rename from src/test/java/seedu/address/model/tag/TagTest.java rename to src/test/java/seedu/library/model/tag/TagTest.java index 64d07d79ee2..9eb3e7e2f5f 100644 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ b/src/test/java/seedu/library/model/tag/TagTest.java @@ -1,6 +1,6 @@ -package seedu.address.model.tag; +package seedu.library.model.tag; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/library/storage/JsonAdaptedBookmarkTest.java b/src/test/java/seedu/library/storage/JsonAdaptedBookmarkTest.java new file mode 100644 index 00000000000..e533b0aa638 --- /dev/null +++ b/src/test/java/seedu/library/storage/JsonAdaptedBookmarkTest.java @@ -0,0 +1,132 @@ +package seedu.library.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.library.storage.JsonAdaptedBookmark.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.BENSON; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; + +public class JsonAdaptedBookmarkTest { + private static final String INVALID_TITLE = " "; + private static final JsonAdaptedProgress INVALID_PROGRESS = new JsonAdaptedProgress("a", "b", "c"); + private static final String INVALID_AUTHOR = " "; + private static final String INVALID_GENRE = " "; + private static final String INVALID_TAG = "#friend"; + private static final String INVALID_RATING = "-1"; + + private static final String VALID_TITLE = BENSON.getTitle().toString(); + private static final JsonAdaptedProgress VALID_PROGRESS = new JsonAdaptedProgress(BENSON.getProgress()); + private static final String VALID_GENRE = BENSON.getGenre().toString(); + private static final String VALID_AUTHOR = BENSON.getAuthor().toString(); + private static final String VALID_URL = BENSON.getUrl().toString(); + private static final String VALID_RATING = "5"; + private static final List<JsonAdaptedTag> VALID_TAGS = BENSON.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList()); + + @Test + public void toModelType_validBookmarkDetails_returnsBookmark() throws Exception { + JsonAdaptedBookmark bookmark = new JsonAdaptedBookmark(BENSON); + assertEquals(BENSON, bookmark.toModelType()); + } + + @Test + public void toModelType_invalidTitle_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = + new JsonAdaptedBookmark(INVALID_TITLE, VALID_PROGRESS, + VALID_GENRE, VALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = Title.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + + @Test + public void toModelType_nullTitle_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = new JsonAdaptedBookmark(null, + VALID_PROGRESS, VALID_GENRE, VALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Title.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + + @Test + public void toModelType_invalidProgress_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = + new JsonAdaptedBookmark(VALID_TITLE, INVALID_PROGRESS, + VALID_GENRE, VALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = Progress.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + + // @Test + // public void toModelType_nullProgress_throwsIllegalValueException() { + // JsonAdaptedBookmark bookmark = new JsonAdaptedBookmark(VALID_TITLE, null, + // VALID_GENRE, VALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + // String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Progress.class.getSimpleName()); + // assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + // } + + @Test + public void toModelType_invalidGenre_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = + new JsonAdaptedBookmark(VALID_TITLE, VALID_PROGRESS, + INVALID_GENRE, VALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = Genre.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + + @Test + public void toModelType_nullGenre_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = new JsonAdaptedBookmark(VALID_TITLE, + VALID_PROGRESS, null, VALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Genre.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + + @Test + public void toModelType_invalidAuthor_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = + new JsonAdaptedBookmark(VALID_TITLE, VALID_PROGRESS, + VALID_GENRE, INVALID_AUTHOR, VALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = Author.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + + // @Test + // public void toModelType_nullAuthor_throwsIllegalValueException() { + // JsonAdaptedBookmark bookmark = new JsonAdaptedBookmark(VALID_TITLE, + // VALID_PROGRESS, VALID_GENRE, null, VALID_RATING, VALID_URL, VALID_TAGS); + // String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Author.class.getSimpleName()); + // assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + // } + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List<JsonAdaptedTag> invalidTags = new ArrayList<>(VALID_TAGS); + invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); + JsonAdaptedBookmark bookmark = + new JsonAdaptedBookmark(VALID_TITLE, VALID_PROGRESS, + VALID_GENRE, VALID_AUTHOR, VALID_RATING, VALID_URL, invalidTags); + assertThrows(IllegalValueException.class, bookmark::toModelType); + } + + @Test + public void toModelType_invalidRating_throwsIllegalValueException() { + JsonAdaptedBookmark bookmark = + new JsonAdaptedBookmark(VALID_TITLE, VALID_PROGRESS, + VALID_GENRE, VALID_AUTHOR, INVALID_RATING, VALID_URL, VALID_TAGS); + String expectedMessage = Rating.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, bookmark::toModelType); + } + +} diff --git a/src/test/java/seedu/library/storage/JsonAdaptedTagTest.java b/src/test/java/seedu/library/storage/JsonAdaptedTagTest.java new file mode 100644 index 00000000000..76c989c3cef --- /dev/null +++ b/src/test/java/seedu/library/storage/JsonAdaptedTagTest.java @@ -0,0 +1,27 @@ +package seedu.library.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.library.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_PLANT; +import static seedu.library.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.model.tag.Tag; + + +public class JsonAdaptedTagTest { + @Test + public void toModelType_validTags_returnTags() throws Exception { + Tag validTag = new Tag(VALID_TAG_PLANT); + JsonAdaptedTag tag = new JsonAdaptedTag(validTag); + assertEquals(validTag, tag.toModelType()); + } + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + JsonAdaptedTag tag = new JsonAdaptedTag(INVALID_TAG_DESC); + assertThrows(IllegalValueException.class, tag::toModelType); + } +} diff --git a/src/test/java/seedu/library/storage/JsonLibraryStorageTest.java b/src/test/java/seedu/library/storage/JsonLibraryStorageTest.java new file mode 100644 index 00000000000..f2d532bea17 --- /dev/null +++ b/src/test/java/seedu/library/storage/JsonLibraryStorageTest.java @@ -0,0 +1,110 @@ +package seedu.library.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalBookmarks.AOT; +import static seedu.library.testutil.TypicalBookmarks.HOON; +import static seedu.library.testutil.TypicalBookmarks.IDA; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.Library; +import seedu.library.model.ReadOnlyLibrary; + +public class JsonLibraryStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonLibraryStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readLibrary_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readLibrary(null)); + } + + private java.util.Optional<ReadOnlyLibrary> readLibrary(String filePath) throws Exception { + return new JsonLibraryStorage(Paths.get(filePath)).readLibrary(addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readLibrary("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readLibrary("notJsonFormatLibrary.json")); + } + + @Test + public void readLibrary_invalidBookmarkLibrary_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readLibrary("invalidBookmarkLibrary.json")); + } + + @Test + public void readLibrary_invalidAndValidBookmarkLibrary_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readLibrary("invalidAndValidBookmarkLibrary.json")); + } + + @Test + public void readAndSaveLibrary_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempLibrary.json"); + Library original = getTypicalLibrary(); + JsonLibraryStorage jsonLibraryStorage = new JsonLibraryStorage(filePath); + + // Save in new file and read back + jsonLibraryStorage.saveLibrary(original, filePath); + ReadOnlyLibrary readBack = jsonLibraryStorage.readLibrary(filePath).get(); + assertEquals(original, new Library(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addBookmark(HOON); + original.removeBookmark(AOT); + jsonLibraryStorage.saveLibrary(original, filePath); + readBack = jsonLibraryStorage.readLibrary(filePath).get(); + assertEquals(original, new Library(readBack)); + + // Save and read without specifying file path + original.addBookmark(IDA); + jsonLibraryStorage.saveLibrary(original); // file path not specified + readBack = jsonLibraryStorage.readLibrary().get(); // file path not specified + assertEquals(original, new Library(readBack)); + + } + + @Test + public void saveLibrary_nullLibrary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveLibrary(null, "SomeFile.json")); + } + + /** + * Saves {@code Library} at the specified {@code filePath}. + */ + private void saveLibrary(ReadOnlyLibrary library, String filePath) { + try { + new JsonLibraryStorage(Paths.get(filePath)) + .saveLibrary(library, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveLibrary_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveLibrary(new Library(), null)); + } +} diff --git a/src/test/java/seedu/library/storage/JsonSerializableLibraryTest.java b/src/test/java/seedu/library/storage/JsonSerializableLibraryTest.java new file mode 100644 index 00000000000..33604619c81 --- /dev/null +++ b/src/test/java/seedu/library/storage/JsonSerializableLibraryTest.java @@ -0,0 +1,47 @@ +package seedu.library.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.library.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.commons.util.JsonUtil; +import seedu.library.model.Library; +import seedu.library.testutil.TypicalBookmarks; + +public class JsonSerializableLibraryTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableLibraryTest"); + private static final Path TYPICAL_BOOKMARKS_FILE = TEST_DATA_FOLDER.resolve("typicalBookmarksLibrary.json"); + private static final Path INVALID_BOOKMARK_FILE = TEST_DATA_FOLDER.resolve("invalidBookmarkLibrary.json"); + private static final Path DUPLICATE_BOOKMARK_FILE = TEST_DATA_FOLDER.resolve("duplicateBookmarkLibrary.json"); + + @Test + public void toModelType_typicalBookmarksFile_success() throws Exception { + JsonSerializableLibrary dataFromFile = JsonUtil.readJsonFile(TYPICAL_BOOKMARKS_FILE, + JsonSerializableLibrary.class).get(); + Library libraryFromFile = dataFromFile.toModelType(); + Library typicalBookmarksLibrary = TypicalBookmarks.getTypicalLibrary(); + assertEquals(libraryFromFile, typicalBookmarksLibrary); + } + + @Test + public void toModelType_invalidBookmarkFile_throwsIllegalValueException() throws Exception { + JsonSerializableLibrary dataFromFile = JsonUtil.readJsonFile(INVALID_BOOKMARK_FILE, + JsonSerializableLibrary.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateBookmarks_throwsIllegalValueException() throws Exception { + JsonSerializableLibrary dataFromFile = JsonUtil.readJsonFile(DUPLICATE_BOOKMARK_FILE, + JsonSerializableLibrary.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableLibrary.MESSAGE_DUPLICATE_BOOKMARK, + dataFromFile::toModelType); + } + +} diff --git a/src/test/java/seedu/library/storage/JsonSerializableTagsTest.java b/src/test/java/seedu/library/storage/JsonSerializableTagsTest.java new file mode 100644 index 00000000000..046c9f054d2 --- /dev/null +++ b/src/test/java/seedu/library/storage/JsonSerializableTagsTest.java @@ -0,0 +1,47 @@ +package seedu.library.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.library.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.library.commons.exceptions.IllegalValueException; +import seedu.library.commons.util.JsonUtil; +import seedu.library.model.Tags; +import seedu.library.testutil.TypicalTags; + +public class JsonSerializableTagsTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonTagsTest"); + private static final Path TYPICAL_TAGS_FILE = TEST_DATA_FOLDER.resolve("typicalTags.json"); + private static final Path INVALID_TAGS_FILE = TEST_DATA_FOLDER.resolve("invalidTags.json"); + private static final Path DUPLICATE_TAGS_FILE = TEST_DATA_FOLDER.resolve("duplicateTags.json"); + + @Test + public void toModelType_typicalTagsFile_success() throws Exception { + JsonSerializableTags dataFromFile = JsonUtil.readJsonFile(TYPICAL_TAGS_FILE, + JsonSerializableTags.class).get(); + Tags tagsFromFile = dataFromFile.toModelType(); + Tags typicalTags = TypicalTags.getTypicalTags(); + assertEquals(tagsFromFile, typicalTags); + } + + @Test + public void toModelType_invalidTagsFile_throwsIllegalValueException() throws Exception { + JsonSerializableTags dataFromFile = JsonUtil.readJsonFile(INVALID_TAGS_FILE, + JsonSerializableTags.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateTags_throwsIllegalValueException() throws Exception { + JsonSerializableTags dataFromFile = JsonUtil.readJsonFile(DUPLICATE_TAGS_FILE, + JsonSerializableTags.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableTags.MESSAGE_DUPLICATE_TAGS, + dataFromFile::toModelType); + } + +} diff --git a/src/test/java/seedu/library/storage/JsonTagsStorageTest.java b/src/test/java/seedu/library/storage/JsonTagsStorageTest.java new file mode 100644 index 00000000000..561423d2824 --- /dev/null +++ b/src/test/java/seedu/library/storage/JsonTagsStorageTest.java @@ -0,0 +1,110 @@ +package seedu.library.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.library.testutil.Assert.assertThrows; +import static seedu.library.testutil.TypicalTags.OCEAN; +import static seedu.library.testutil.TypicalTags.PLANT; +import static seedu.library.testutil.TypicalTags.getTypicalTags; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.ReadOnlyTags; +import seedu.library.model.Tags; +import seedu.library.model.tag.Tag; + +public class JsonTagsStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonTagsTest"); + + @TempDir + public Path testFolder; + + @Test + public void readTags_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readTags(null)); + } + + private java.util.Optional<ReadOnlyTags> readTags(String filePath) throws Exception { + return new JsonTagsStorage(Paths.get(filePath)).readTags(addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readTags("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readTags("notJsonFormatTags.json")); + } + + @Test + public void readLibrary_invalidTags_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTags("invalidTags.json")); + } + + @Test + public void readLibrary_invalidAndValidTags_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTags("invalidTags.json")); + } + + @Test + public void readAndSaveTags_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempTags.json"); + Tags original = getTypicalTags(); + JsonTagsStorage jsonTagsStorage = new JsonTagsStorage(filePath); + + // Save in new file and read back + jsonTagsStorage.saveTags(original, filePath); + ReadOnlyTags readBack = jsonTagsStorage.readTags(filePath).get(); + assertEquals(original, new Tags(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addTag(OCEAN); + original.removeTag(new Tag("MaleProtagonist")); + jsonTagsStorage.saveTags(original, filePath); + readBack = jsonTagsStorage.readTags(filePath).get(); + assertEquals(original, new Tags(readBack)); + + // Save and read without specifying file path + original.addTag(PLANT); + jsonTagsStorage.saveTags(original); // file path not specified + readBack = jsonTagsStorage.readTags().get(); // file path not specified + assertEquals(original, new Tags(readBack)); + + } + + @Test + public void saveTags_nullTags_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveTags(null, "SomeFile.json")); + } + + /** + * Saves {@code Tags} at the specified {@code filePath}. + */ + private void saveTags(ReadOnlyTags tags, String filePath) { + try { + new JsonTagsStorage(Paths.get(filePath)) + .saveTags(tags, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveTags_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveTags(new Tags(), null)); + } +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/library/storage/JsonUserPrefsStorageTest.java similarity index 93% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/seedu/library/storage/JsonUserPrefsStorageTest.java index 16f33f4a6bb..7d95cb6a0aa 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/library/storage/JsonUserPrefsStorageTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.library.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +import seedu.library.commons.core.GuiSettings; +import seedu.library.commons.exceptions.DataConversionException; +import seedu.library.model.UserPrefs; public class JsonUserPrefsStorageTest { @@ -73,7 +73,7 @@ public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataConv private UserPrefs getTypicalUserPrefs() { UserPrefs userPrefs = new UserPrefs(); userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); - userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setLibraryFilePath(Paths.get("library.json")); return userPrefs; } diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/library/storage/StorageManagerTest.java similarity index 59% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/library/storage/StorageManagerTest.java index 99a16548970..e4cd60f7ca6 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/library/storage/StorageManagerTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.library.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.library.testutil.TypicalBookmarks.getTypicalLibrary; import java.nio.file.Path; @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; +import seedu.library.commons.core.GuiSettings; +import seedu.library.model.Library; +import seedu.library.model.ReadOnlyLibrary; +import seedu.library.model.UserPrefs; public class StorageManagerTest { @@ -24,9 +24,10 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonLibraryStorage libraryStorage = new JsonLibraryStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTagsStorage tagsStorage = new JsonTagsStorage(getTempFilePath("tags")); + storageManager = new StorageManager(libraryStorage, userPrefsStorage, tagsStorage); } private Path getTempFilePath(String fileName) { @@ -48,21 +49,21 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { + public void libraryReadSave() throws Exception { /* * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonAddressBookStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. + * {@link JsonLibraryStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonLibraryStorageTest} class. */ - AddressBook original = getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); + Library original = getTypicalLibrary(); + storageManager.saveLibrary(original); + ReadOnlyLibrary retrieved = storageManager.readLibrary().get(); + assertEquals(original, new Library(retrieved)); } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getLibraryFilePath() { + assertNotNull(storageManager.getLibraryFilePath()); } } diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/seedu/library/testutil/Assert.java similarity index 97% rename from src/test/java/seedu/address/testutil/Assert.java rename to src/test/java/seedu/library/testutil/Assert.java index 9863093bd6e..8e326d7ef1f 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/seedu/library/testutil/Assert.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.library.testutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.function.Executable; diff --git a/src/test/java/seedu/library/testutil/BookmarkBuilder.java b/src/test/java/seedu/library/testutil/BookmarkBuilder.java new file mode 100644 index 00000000000..e0a34cd6c3d --- /dev/null +++ b/src/test/java/seedu/library/testutil/BookmarkBuilder.java @@ -0,0 +1,121 @@ +package seedu.library.testutil; + +import java.util.HashSet; +import java.util.Set; + +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; +import seedu.library.model.util.SampleDataUtil; + +/** + * A utility class to help with building Bookmark objects. + */ +public class BookmarkBuilder { + + public static final String DEFAULT_TITLE = "Chainsaw Man"; + public static final String[] DEFAULT_PROGRESS = {"1", "50", "~"}; + public static final String DEFAULT_GENRE = "Fantasy"; + public static final String DEFAULT_AUTHOR = "Tatsuki Fujimoto"; + public static final String DEFAULT_URL = "https://www.abc.com"; + + private Title title; + private Progress progress; + private Genre genre; + private Author author; + private Rating rating; + private Url url; + private Set<Tag> tags; + + /** + * Creates a {@code BookmarkBuilder} with the default details. + */ + public BookmarkBuilder() { + title = new Title(DEFAULT_TITLE); + progress = new Progress(DEFAULT_PROGRESS); + genre = new Genre(DEFAULT_GENRE); + author = new Author(DEFAULT_AUTHOR); + url = new Url(DEFAULT_URL); + tags = new HashSet<>(); + rating = Rating.DEFAULT_RATING; + } + + /** + * Initializes the BookmarkBuilder with the data of {@code bookmarkToCopy}. + */ + public BookmarkBuilder(Bookmark bookmarkToCopy) { + title = bookmarkToCopy.getTitle(); + progress = bookmarkToCopy.getProgress(); + genre = bookmarkToCopy.getGenre(); + author = bookmarkToCopy.getAuthor(); + url = bookmarkToCopy.getUrl(); + rating = bookmarkToCopy.getRating(); + tags = new HashSet<>(bookmarkToCopy.getTags()); + } + + /** + * Sets the {@code Title} of the {@code Bookmark} that we are building. + */ + public BookmarkBuilder withTitle(String title) { + this.title = new Title(title); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set<Tag>} and set it to the {@code Bookmark} that we are building. + */ + public BookmarkBuilder withTags(String ... tags) { + this.tags = SampleDataUtil.getTagSet(tags); + return this; + } + + /** + * Sets the {@code Author} of the {@code Bookmark} that we are building. + */ + public BookmarkBuilder withAuthor(String author) { + if (author == null) { + this.author = null; + } else { + this.author = new Author(author); + } + return this; + } + + /** + * Sets the {@code Progress} of the {@code Bookmark} that we are building. + */ + public BookmarkBuilder withProgress(String progress) { + if (progress == null) { + this.progress = null; + } else { + String[] splitProgress = progress.split(" "); + this.progress = new Progress(splitProgress); + } + return this; + } + + /** + * Sets the {@code Genre} of the {@code Bookmark} that we are building. + */ + public BookmarkBuilder withGenre(String genre) { + this.genre = new Genre(genre); + return this; + } + /** + * Sets the {@code Url} of the {@code Bookmark} that we are building. + */ + public BookmarkBuilder withUrl(String url) { + this.url = new Url(url); + return this; + } + + public Bookmark build() { + return new Bookmark(title, progress, genre, author, rating, url, tags); + } + +} diff --git a/src/test/java/seedu/library/testutil/BookmarkUtil.java b/src/test/java/seedu/library/testutil/BookmarkUtil.java new file mode 100644 index 00000000000..05daf95c04f --- /dev/null +++ b/src/test/java/seedu/library/testutil/BookmarkUtil.java @@ -0,0 +1,71 @@ +package seedu.library.testutil; + +import static seedu.library.logic.parser.CliSyntax.PREFIX_AUTHOR; +import static seedu.library.logic.parser.CliSyntax.PREFIX_GENRE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_PROGRESS; +import static seedu.library.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.library.logic.parser.CliSyntax.PREFIX_TITLE; +import static seedu.library.logic.parser.CliSyntax.PREFIX_URL; + +import java.util.Set; + +import seedu.library.logic.commands.AddCommand; +import seedu.library.logic.commands.EditCommand.EditBookmarkDescriptor; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.tag.Tag; + +/** + * A utility class for Bookmark. + */ +public class BookmarkUtil { + + /** + * Returns an add command string for adding the {@code bookmark}. + */ + public static String getAddCommand(Bookmark bookmark) { + return AddCommand.COMMAND_WORD + " " + getBookmarkDetails(bookmark); + } + + /** + * Returns the part of command string for the given {@code bookmark}'s details. + */ + public static String getBookmarkDetails(Bookmark bookmark) { + StringBuilder sb = new StringBuilder(); + Progress progress = bookmark.getProgress(); + sb.append(PREFIX_TITLE + bookmark.getTitle().value + " "); + sb.append(PREFIX_PROGRESS + progress.getDetails() + " "); + sb.append(PREFIX_GENRE + bookmark.getGenre().value + " "); + sb.append(PREFIX_AUTHOR + bookmark.getAuthor().value + " "); + sb.append(PREFIX_RATING + bookmark.getRating().value + " "); + sb.append(PREFIX_URL + bookmark.getUrl().value + " "); + bookmark.getTags().stream().forEach( + s -> sb.append(PREFIX_TAG + s.tagName + " ") + ); + return sb.toString(); + } + + /** + * Returns the part of command string for the given {@code EditBookmarkDescriptor}'s details. + */ + public static String getEditBookmarkDescriptorDetails(EditBookmarkDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getTitle().ifPresent(title -> sb.append(PREFIX_TITLE).append(title.value).append(" ")); + descriptor.getProgress().ifPresent(progress -> sb.append(PREFIX_PROGRESS).append(progress.getDetails()) + .append(" ")); + descriptor.getGenre().ifPresent(genre -> sb.append(PREFIX_GENRE).append(genre.value).append(" ")); + descriptor.getAuthor().ifPresent(author -> sb.append(PREFIX_AUTHOR).append(author.value).append(" ")); + descriptor.getRating().ifPresent(rating -> sb.append(PREFIX_RATING).append(rating.value).append(" ")); + descriptor.getUrl().ifPresent(url -> sb.append(PREFIX_URL).append(url.value).append(" ")); + if (descriptor.getTags().isPresent()) { + Set<Tag> tags = descriptor.getTags().get(); + if (tags.isEmpty()) { + sb.append(PREFIX_TAG); + } else { + tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); + } + } + return sb.toString(); + } +} diff --git a/src/test/java/seedu/library/testutil/EditBookmarkDescriptorBuilder.java b/src/test/java/seedu/library/testutil/EditBookmarkDescriptorBuilder.java new file mode 100644 index 00000000000..7f0a08eef72 --- /dev/null +++ b/src/test/java/seedu/library/testutil/EditBookmarkDescriptorBuilder.java @@ -0,0 +1,108 @@ +package seedu.library.testutil; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import seedu.library.logic.commands.EditCommand.EditBookmarkDescriptor; +import seedu.library.model.bookmark.Author; +import seedu.library.model.bookmark.Bookmark; +import seedu.library.model.bookmark.Genre; +import seedu.library.model.bookmark.Progress; +import seedu.library.model.bookmark.Rating; +import seedu.library.model.bookmark.Title; +import seedu.library.model.bookmark.Url; +import seedu.library.model.tag.Tag; + +/** + * A utility class to help with building EditBookmarkDescriptor objects. + */ +public class EditBookmarkDescriptorBuilder { + + private EditBookmarkDescriptor descriptor; + + public EditBookmarkDescriptorBuilder() { + descriptor = new EditBookmarkDescriptor(); + } + + public EditBookmarkDescriptorBuilder(EditBookmarkDescriptor descriptor) { + this.descriptor = new EditBookmarkDescriptor(descriptor); + } + + /** + * Returns an {@code EditBookmarkDescriptor} with fields containing {@code bookmark}'s details + */ + public EditBookmarkDescriptorBuilder(Bookmark bookmark) { + descriptor = new EditBookmarkDescriptor(); + descriptor.setTitle(bookmark.getTitle()); + descriptor.setProgress(bookmark.getProgress()); + descriptor.setGenre(bookmark.getGenre()); + descriptor.setAuthor(bookmark.getAuthor()); + descriptor.setTags(bookmark.getTags()); + descriptor.setRating(bookmark.getRating()); + descriptor.setUrl(bookmark.getUrl()); + } + + /** + * Sets the {@code Title} of the {@code EditBookmarkDescriptor} that we are building. + */ + public EditBookmarkDescriptorBuilder withTitle(String title) { + descriptor.setTitle(new Title(title)); + return this; + } + + /** + * Sets the {@code Progress} of the {@code EditBookmarkDescriptor} that we are building. + */ + public EditBookmarkDescriptorBuilder withProgress(String progress) { + String[] splitProgress = progress.split(" "); + descriptor.setProgress(new Progress(splitProgress)); + return this; + } + + /** + * Sets the {@code Genre} of the {@code EditBookmarkDescriptor} that we are building. + */ + public EditBookmarkDescriptorBuilder withGenre(String genre) { + descriptor.setGenre(new Genre(genre)); + return this; + } + + /** + * Sets the {@code Author} of the {@code EditBookmarkDescriptor} that we are building. + */ + public EditBookmarkDescriptorBuilder withAuthor(String author) { + descriptor.setAuthor(new Author(author)); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set<Tag>} and set it to the {@code EditBookmarkDescriptor} + * that we are building. + */ + public EditBookmarkDescriptorBuilder withTags(String... tags) { + Set<Tag> tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); + descriptor.setTags(tagSet); + return this; + } + + /** + * Sets the {@code Rating} of the {@code EditBookmarkDescriptor} that we are building. + */ + public EditBookmarkDescriptorBuilder withRating(String rating) { + descriptor.setRating(new Rating(rating)); + return this; + } + + /** + * Sets the {@code Url} of the {@code EditBookmarkDescriptor} that we are building. + */ + public EditBookmarkDescriptorBuilder withUrl(String url) { + descriptor.setUrl(new Url(url)); + return this; + } + + public EditBookmarkDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/library/testutil/LibraryBuilder.java b/src/test/java/seedu/library/testutil/LibraryBuilder.java new file mode 100644 index 00000000000..37799e5977a --- /dev/null +++ b/src/test/java/seedu/library/testutil/LibraryBuilder.java @@ -0,0 +1,34 @@ +package seedu.library.testutil; + +import seedu.library.model.Library; +import seedu.library.model.bookmark.Bookmark; + +/** + * A utility class to help with building Library objects. + * Example usage: <br> + * {@code Library ab = new LibraryBuilder().withBookmark("John", "Doe").build();} + */ +public class LibraryBuilder { + + private Library library; + + public LibraryBuilder() { + library = new Library(); + } + + public LibraryBuilder(Library library) { + this.library = library; + } + + /** + * Adds a new {@code Bookmark} to the {@code Library} that we are building. + */ + public LibraryBuilder withBookmark(Bookmark bookmark) { + library.addBookmark(bookmark); + return this; + } + + public Library build() { + return library; + } +} diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/seedu/library/testutil/SerializableTestClass.java similarity index 98% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/seedu/library/testutil/SerializableTestClass.java index f5a66340489..7619bc12c08 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/seedu/library/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.library.testutil; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/test/java/seedu/library/testutil/TagsBuilder.java b/src/test/java/seedu/library/testutil/TagsBuilder.java new file mode 100644 index 00000000000..5847f7369d2 --- /dev/null +++ b/src/test/java/seedu/library/testutil/TagsBuilder.java @@ -0,0 +1,50 @@ +package seedu.library.testutil; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.library.model.Tags; +import seedu.library.model.tag.Tag; + +/** + * A utility class to help with building Tags objects. + */ +public class TagsBuilder { + private final Set<Tag> tags = new HashSet<>(); + + /** + * Builds a set of tags with 2 default values. + */ + public TagsBuilder() { + tags.add(new Tag("novel")); + tags.add(new Tag("plant")); + } + + /** + * Adds a {@code tagName} of the {@code Tag} that we are building. + */ + public TagsBuilder addTag(String tagName) { + tags.add(new Tag(tagName)); + return this; + } + + /** + * Builds a default tag list. + * @return Set of tag list. + */ + public Set<Tag> build() { + return tags; + } + + public List<Tag> asList() { + return new ArrayList<>(tags); + } + + public Tags getTypicalTags() { + Tags tags = new Tags(); + tags.setTags(asList()); + return tags; + } +} diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/library/testutil/TestUtil.java similarity index 58% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/seedu/library/testutil/TestUtil.java index 896d103eb0b..b9aa41fa24a 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/library/testutil/TestUtil.java @@ -1,13 +1,13 @@ -package seedu.address.testutil; +package seedu.library.testutil; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.library.commons.core.index.Index; +import seedu.library.model.Model; +import seedu.library.model.bookmark.Bookmark; /** * A utility class for test cases. @@ -33,23 +33,23 @@ public static Path getFilePathInSandboxFolder(String fileName) { } /** - * Returns the middle index of the person in the {@code model}'s person list. + * Returns the middle index of the bookmark in the {@code model}'s bookmark list. */ public static Index getMidIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size() / 2); + return Index.fromOneBased(model.getFilteredBookmarkList().size() / 2); } /** - * Returns the last index of the person in the {@code model}'s person list. + * Returns the last index of the bookmark in the {@code model}'s bookmark list. */ public static Index getLastIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size()); + return Index.fromOneBased(model.getFilteredBookmarkList().size()); } /** - * Returns the person in the {@code model}'s person list at {@code index}. + * Returns the bookmark in the {@code model}'s bookmark list at {@code index}. */ - public static Person getPerson(Model model, Index index) { - return model.getFilteredPersonList().get(index.getZeroBased()); + public static Bookmark getBookmark(Model model, Index index) { + return model.getFilteredBookmarkList().get(index.getZeroBased()); } } diff --git a/src/test/java/seedu/library/testutil/TypicalBookmarks.java b/src/test/java/seedu/library/testutil/TypicalBookmarks.java new file mode 100644 index 00000000000..0f2673bd791 --- /dev/null +++ b/src/test/java/seedu/library/testutil/TypicalBookmarks.java @@ -0,0 +1,91 @@ +package seedu.library.testutil; + +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_AUTHOR_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_GENRE_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_PROGRESS_BOB; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_AMY; +import static seedu.library.logic.commands.CommandTestUtil.VALID_TITLE_BOB; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.library.model.Library; +import seedu.library.model.bookmark.Bookmark; + +/** + * A utility class containing a list of {@code Bookmark} objects to be used in tests. + */ +public class TypicalBookmarks { + + public static final Bookmark AOT = new BookmarkBuilder().withTitle("Attack on Titans") + .withAuthor("Hajime Isayama").withGenre("Action") + .withProgress("1 2 ~").withUrl("https://aotmanga.com/attack-on-titan/chapter-139/") + .withTags("MaleProtagonist").build(); + + public static final String AOTString = "Attack on Titans" + + "; Progress: Vol. 1 Ch. 2" + + "; Genre: Action; Author: Hajime Isayama" + + "; Rating: 0" + + "; Url: https://aotmanga.com/attack-on-titan/chapter-139/" + + "; Tags: [MaleProtagonist]"; + + public static final Bookmark BENSON = new BookmarkBuilder().withTitle("Benson Meier") + .withAuthor("311, Clementi Ave 2, #02-25") + .withGenre("Adventure").withProgress("1 2 ~").withUrl("https://www.def.com") + .withTags("MaleProtagonist", "FemaleProtagonist").build(); + public static final Bookmark CARL = new BookmarkBuilder().withTitle("Carl Kurz") + .withProgress("1 2 ~") + .withGenre("Comedy").withAuthor("wall street").build(); + public static final Bookmark DANIEL = new BookmarkBuilder().withTitle("Daniel Meier") + .withProgress("1 2 ~") + .withGenre("Drama").withAuthor("10th street").withTags("friends").build(); + public static final Bookmark ELLE = new BookmarkBuilder().withTitle("Elle Meyer") + .withProgress("1 2 ~") + .withGenre("Fantasy").withAuthor("michegan ave").build(); + public static final Bookmark FIONA = new BookmarkBuilder().withTitle("Fiona Kunz") + .withProgress("1 2 ~") + .withGenre("Historical").withAuthor("little tokyo").build(); + public static final Bookmark GEORGE = new BookmarkBuilder().withTitle("George Best") + .withProgress("1 2 ~") + .withGenre("Horror").withAuthor("4th street").build(); + + // Manually added + public static final Bookmark HOON = new BookmarkBuilder().withTitle("Hoon Meier") + .withProgress("1 2 ~") + .withGenre("Martial Arts").withAuthor("little india").build(); + public static final Bookmark IDA = new BookmarkBuilder().withTitle("Ida Mueller") + .withProgress("1 2 ~") + .withGenre("Others").withAuthor("chicago ave").build(); + + // Manually added - Bookmark's details found in {@code CommandTestUtil} + public static final Bookmark AMY = new BookmarkBuilder().withTitle(VALID_TITLE_AMY).withProgress(VALID_PROGRESS_AMY) + .withGenre(VALID_GENRE_AMY).withAuthor(VALID_AUTHOR_AMY).withTags(VALID_TAG_FRIEND).build(); + public static final Bookmark BOB = new BookmarkBuilder().withTitle(VALID_TITLE_BOB).withProgress(VALID_PROGRESS_BOB) + .withGenre(VALID_GENRE_BOB).withAuthor(VALID_AUTHOR_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) + .build(); + + public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER + + private TypicalBookmarks() {} // prevents instantiation + + /** + * Returns an {@code Library} with all the typical Bookmarks. + */ + public static Library getTypicalLibrary() { + Library ab = new Library(); + for (Bookmark bookmark : getTypicalBookmarks()) { + ab.addBookmark(bookmark); + } + return ab; + } + + public static List<Bookmark> getTypicalBookmarks() { + return new ArrayList<>(Arrays.asList(AOT, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); + } +} diff --git a/src/test/java/seedu/library/testutil/TypicalIndexes.java b/src/test/java/seedu/library/testutil/TypicalIndexes.java new file mode 100644 index 00000000000..b0271a58b56 --- /dev/null +++ b/src/test/java/seedu/library/testutil/TypicalIndexes.java @@ -0,0 +1,12 @@ +package seedu.library.testutil; + +import seedu.library.commons.core.index.Index; + +/** + * A utility class containing a list of {@code Index} objects to be used in tests. + */ +public class TypicalIndexes { + public static final Index INDEX_FIRST_BOOKMARK = Index.fromOneBased(1); + public static final Index INDEX_SECOND_BOOKMARK = Index.fromOneBased(2); + public static final Index INDEX_THIRD_BOOKMARK = Index.fromOneBased(3); +} diff --git a/src/test/java/seedu/library/testutil/TypicalTags.java b/src/test/java/seedu/library/testutil/TypicalTags.java new file mode 100644 index 00000000000..fd670911b15 --- /dev/null +++ b/src/test/java/seedu/library/testutil/TypicalTags.java @@ -0,0 +1,48 @@ +package seedu.library.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.library.model.Tags; +import seedu.library.model.tag.Tag; + +/** + * A utility class containing a list of {@code Tag} objects to be used in tests. + */ +public class TypicalTags { + public static final Tag MALE_PROTAGONIST = new Tag("MaleProtagonist"); + + public static final Tag MANHWA = new Tag("Manhwa"); + + public static final Tag HUNTERS = new Tag("Hunters"); + + public static final Tag CHEATS = new Tag("Cheats"); + + public static final Tag SYSTEM = new Tag("System"); + + public static final Tag ANTIHERO = new Tag("Antihero"); + + public static final Tag LITERATURE = new Tag("Literature"); + + public static final Tag SCHOOL = new Tag("School"); + + public static final Tag GORE = new Tag("Gore"); + + public static final Tag OCEAN = new Tag("Ocean"); + + public static final Tag PLANT = new Tag("Plant"); + + public static Tags getTypicalTags() { + Tags tags = new Tags(); + for (Tag tag : getTypicalTag()) { + tags.addTag(tag); + } + return tags; + } + + public static List<Tag> getTypicalTag() { + return new ArrayList<>(Arrays.asList(MALE_PROTAGONIST, MANHWA, HUNTERS, CHEATS, SYSTEM, ANTIHERO, + LITERATURE, SCHOOL, GORE)); + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/seedu/library/ui/TestFxmlObject.java similarity index 96% rename from src/test/java/seedu/address/ui/TestFxmlObject.java rename to src/test/java/seedu/library/ui/TestFxmlObject.java index 5ecd82656f2..670fa7d3a0c 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/seedu/library/ui/TestFxmlObject.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.library.ui; import javafx.beans.DefaultProperty; diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/seedu/library/ui/UiPartTest.java similarity index 97% rename from src/test/java/seedu/address/ui/UiPartTest.java rename to src/test/java/seedu/library/ui/UiPartTest.java index 33d82d911b8..97948e9d907 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/seedu/library/ui/UiPartTest.java @@ -1,8 +1,8 @@ -package seedu.address.ui; +package seedu.library.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.library.testutil.Assert.assertThrows; import java.net.URL; import java.nio.file.Path; @@ -11,7 +11,7 @@ import org.junit.jupiter.api.io.TempDir; import javafx.fxml.FXML; -import seedu.address.MainApp; +import seedu.library.MainApp; public class UiPartTest { diff --git a/src/test/resources/view/UiPartTest/validFile.fxml b/src/test/resources/view/UiPartTest/validFile.fxml index bab836af0db..02825db5350 100644 --- a/src/test/resources/view/UiPartTest/validFile.fxml +++ b/src/test/resources/view/UiPartTest/validFile.fxml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<?import seedu.address.ui.TestFxmlObject?> +<?import seedu.library.ui.TestFxmlObject?> <TestFxmlObject xmlns:fx="http://javafx.com/fxml/1" fx:id="validFileRoot">Hello World!</TestFxmlObject> diff --git a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml index 151e09ce926..6180392e530 100644 --- a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml +++ b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<fx:root type="seedu.address.ui.TestFxmlObject" xmlns="http://javafx.com/javafx" - xmlns:fx="http://javafx.com/fxml"> +<fx:root type="seedu.library.ui.TestFxmlObject" xmlns="http://javafx.com/javafx" + xmlns:fx="http://javafx.com/fxml"> <text>Hello World!</text> </fx:root>