diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index e6f54ae0a31..467829baa75 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -245,37 +245,96 @@ prefixes like `t>` and `d>` ** Cons: Difficult to implement. // end::deadline[] + //@@author LeowWB -// tag::flashcardexport[] -=== Exporting of FlashCards -==== Implementation +// tag::flashcardexportimport[] + +=== Exporting/Importing of FlashCards +==== Implementation -The `FlashCard` exporting mechanism is primarily facilitated by the following classes: +Our application currently supports the exporting of `FlashCards` to two file formats (`.docx` and `.json`) and importing of `FlashCards` from one +file format (`.json`). These mechanisms are primarily facilitated by the following classes: -* `ExportUtil` -- Handles the actual exporting of `FlashCards` * `ExportCommand` -- Embodies an `export` command by the user; carries information about which `FlashCards` are to be exported, and to where * `ExportCommandParser` -- Parses user input and uses it to construct an `ExportCommand` instance +* `ImportCommand` -- Embodies an `import` command by the user; carries information about where `FlashCards` are to be imported from +* `ImportCommandParser` -- Parses user input and uses it to construct an `ImportCommand` instance +* `ExportPath` -- Represents the path to a specific file - either absolute or relative to the application directory +* `ExportPathFactory` -- Parses the user-provided file path and creates instances of `ExportPath` + +NOTE: The "export" in `ExportPath` is to be taken as a noun, not a verb. An `ExportPath`, therefore, is not the path that we export to, but the +path of an export. `ExportPaths` are used in both exporting and importing of files. + +`ExportPath` is an abstract class that follows the factory pattern. Each subclass of `ExportPath` represents the path to a specific file of a +specific extension (e.g. an instance of `DocumentPath` represents the path to a specific document). Instances of these subclasses are created by +`ExportPathFactory#getExportPath(String)`, which determines the appropriate subclass to create based on the extension of the provided file path String. +Once created, an `ExportPath` will expose the following relevant methods: + +* `getPath()` -- Returns a Java `Path` object that represents this `ExportPath` +* `export(List list)` -- Exports the given `List` of `FlashCards` to the file path embodied by this `ExportPath` +* `importFrom()` -- Attempts to import `FlashCards` from the file path represented by this `ExportPath` + +CAUTION: Not all `ExportPath` subclasses will implement the `importFrom()` method. `DocumentPath`, for example, does not - this is because documents are +relatively unstructured and impractical to import from, and there are other reasons for exporting to a document (e.g. to use as cheat sheet). + +Because `ExportPath` follows the factory pattern, any class that deals with `ExportPath` or its subclasses need not know which particular subclass it is +dealing with exactly. Each `ExportPath` subclass will implement its own `export` and `import` methods, which, when called, will perform the required operations +without any further hassle. Of course, due to the Separation of Concerns principle, the `ExportPath` subclasses will not handle these directly. +Instead, they will delegate the work to other utility classes, which, in turn, interface with the external libraries necessary to complete the task. + +TIP: The exporting/importing functionality is extremely easy to extend - support for new formats can be added simply through the creation of new subclasses of `ExportPath`. + +NOTE: All relevant classes can be found in the `seedu.address.model.export` package. The only exceptions are `ExportCommand`, `ImportCommand`, `ExportCommandParser`, and `ImportCommandParser`, which can be found in the `seedu.address.logic` package. + +The following table shows the classes and methods that you may have to deal with when exporting to or importing from each format: + +[width="59%",cols="20%,35%,35%",options="header",] +|==== -The mechanism is also supported by the package `seedu.address.model.export`, consisting of the following classes: +|**File format** |Document |JSON -* `DocumentPath` -- Represents the path to a specific document - either absolute or relative to the application directory -* `DocumentFilePath` -- Represents the path to a specific document, relative to its immediate parent directory -* `DirectoryPath` -- Represents the path to a specific directory - either absolute or relative to the application directory +|**File extension** |`.docx` |`.json` -Of particular note is `ExportUtil`, as it is arguably the class responsible for doing the most tangible work. -Similar to the other classes in the `util` package, it consists entirely of `static` methods, and does not keep track of any variables. -It exposes a single method, `ExportUtil#exportFlashCards(List, DocumentPath)`, which prints a `List` of `FlashCards` to a `.docx` -document located at the given `DocumentPath`. This is done by interfacing with the Apache POI library (in particular, the package -`org.apache.poi.xwpf.usermodel`). +|**`ExportPath` subclass** |`DocumentPath` |`JsonExportPath` -The following sequence diagram shows how the export operation works: +a|**Export utility class and method** +a|`DocumentExportUtil#exportFlashCardsToDocument( List, DocumentPath)` +a|`JsonExportUtil#exportFlashCardsToJson( List, JsonExportPath)` + +a|**Import utility class and method** +a|_None - importing not supported_ +a|`JsonImportUtil#importFlashCardsFromJson( JsonExportPath)` + +|**External library used** |Apache POI |Jackson +|==== + +The number of classes supporting the import/export feature is rather large. The following class diagram will help to clarify the associations +between these classes: + +image::ExportClassDiagram.png[height=700] + +_Figure 1: Class diagram showing the classes directly relevant to importing and exporting_ + +The following sequence diagram shows how the export operation works when the user tries to export to a document (`.docx`) file: image::ExportSequenceDiagram.png[] +_Figure 2: Sequence diagram showing the process of exporting to a document file_ + +NOTE: Due to a limitation of PlantUML, object lifelines in the diagram extend beyond the destroy markers. This, of course, should be ignored. + The following activity diagram summarizes what happens when a user executes an export command: -image::ExportActivityDiagram.png[width=320,height=480] +image::ExportActivityDiagram.png[width=500,height=540] + +_Figure 3: Activity diagram of when the user executes an export command_ + +The following activity diagram summarizes what happens when a user executes an import command: + +image::ImportActivityDiagram.png[width=500,height=580] + +_Figure 4: Activity diagram of when the user executes an import command_ ==== Design Considerations @@ -313,9 +372,9 @@ NOTE: The original AddressBook application, from which KeyboardFlashCards was mo * **Alternative 1:** Read file path as-is, using the existing AddressBook parser ** Pros: Does not require further code changes ** Cons: Means that errors may occur for certain file paths -* **Alternative 2:** Disallow user from selecting file path - instead, always export to a specific directory +* **Alternative 2:** Disallow user from selecting file path - instead, always export to and import from a specific directory ** Pros: Is somewhat easy to implement -** Cons: Requires user to navigate to the specified directory to obtain the file; requires a means of finding alternatives if the default directory does not exist +** Cons: Requires user to navigate to the specified directory; requires a means of finding alternatives if the default directory does not exist * **Alternative 3:** Ask user to replace slashes in file path with another character ** Pros: Is very easy to implement ** Cons: Greatly inconveniences the user; extra work must be done to restore the input to the original file path @@ -325,7 +384,7 @@ NOTE: The original AddressBook application, from which KeyboardFlashCards was mo NOTE: **Alternative 4** was preferred as it provides users with the greatest overall convenience. -// end::flashcardexport[] +// end::flashcardexportimport[] //@@author keiteo // tag::dataencryption[] diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 77224b7b8d4..c492699c1f4 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -46,6 +46,12 @@ This section contains the features and their respective commands * Items with `…`​ after them can be inserted zero or more times e.g. `[c>CATEGORY]...` can be used as `{nbsp}` (i.e. 0 times), `c>math`, `c>math c>school` etc. * Parameters can be in any order e.g. if the command specifies `q>QUESTION a>ANSWER`, `a>ANSWER q>QUESTION` is also acceptable. * Currently, our application does not support emoji. + +Please also note the following icons, which will indicate points of interest throughout this document. + +NOTE: A point of information that you may wish to note +TIP: A tip or suggestion +CAUTION: A cautionary word of advice ==== // tag::flashcard[] @@ -278,6 +284,9 @@ This command rates the flashcard, depending on how well you answered the questio You can stop the test any time simply by typing `end`. //@@author LeowWB + +// tag::flashcardexport[] + === Export flashcards to a file: `export c>CATEGORY p>FILE_PATH` You can use this command to export all your FlashCards in a particular category, to an external file. @@ -290,10 +299,16 @@ Suppose you have a category named `CS2105`, and you wanted to export the FlashCa (so you can share them with your friend). Here's how you would go about this: . Type the example command given above into the command box (`export c>CS2105 p>C:\Documents\cs2105.json`), as shown below. ++ +image::ExportDemo1.png[width="600"] . Press **Enter** to execute the command. The results box will display a success message similar to the one shown in the screenshot below. ++ +image::ExportDemo2.png[width="600"] . Navigate to the directory that you specified in the command (in this case, it would be 'C:\Documents'). Sure enough, your exported file is there! ++ +image::ExportDemo3.png[width="600"] NOTE: Only the questions and answers of FlashCards will be exported. @@ -305,22 +320,33 @@ CAUTION: Do note that some directories may be protected by your operating system TIP: Use this to export your flashcards into an easily-printable cheat sheet! Use them for your assessments or self-learning. +// end::flashcardexport[] + +// tag::flashcardimport[] + === Import flashcards from a JSON file: `import p>FILE_PATH` You can use this command to import FlashCards from a file that you or someone else had exported to earlier. We currently only support importing from JSON (.json) files. + Example: `import p>C:\Downloads\cs2105.json` -Suppose your friend has kindly exported some of his FlashCards for you to use. You've download the `.json` file that he sent you; -now your next step is to get those FlashCards into your copy of KFC. Here's how you'd go about this: +Suppose your friend has kindly exported some of his FlashCards for you to use. You have download the `.json` file that he sent you, +and it's currently located at the following path: `C:\Downloads\cs2105.json`. +Your next step is to get those FlashCards into your copy of KFC. Here's how you'd go about this: . Type the example command given above into the command box (`import p>C:\Downloads\cs2105.json`), as shown below. ++ +image::ImportDemo1.png[width="600"] . Press **Enter** to execute the command. The results box will display a success message similar to the one shown in the screenshot below. -Furthermore, the category panel on the left will now display the category of the newly-imported FlashCards! +Furthermore, the category list on the left will now display the category of the newly-imported FlashCards! ++ +image::ImportDemo2.png[width="600"] NOTE: Duplicate FlashCards will not be imported. You will be notified when we detect duplicate FlashCards in the file you provide. +// end::flashcardimport[] + === Get the full list of commands: `help` This command allows you to view all the commands available in the application. + @@ -424,9 +450,6 @@ To exit: *Q*: Will I be able to resume the test from where I left off after I have stopped? + *A*: No. The program does not support that. -*Q*: Can I undo the commands that I have entered? + -*A*: No. However, a confirmation will be displayed before any potentially-hard-to-reverse actions are done. - *Q*: Can I change the category of a flashcard? + *A*: Yes, it will be one of the fields you can change when you edit the flashcard. Do note that after you change the category, you will have to refer to it using its new category and ID. @@ -434,10 +457,7 @@ To exit: *A*: Yes, but only to a very small extent, like closing the window. This application is targeted primarily at keyboard users. *Q*: What if I forget the format of a command’s arguments? + -*A*: Don’t worry! You will be shown the expected format once you have keyed in the whole command. - -*Q*: Is there an autocomplete functionality? + -*A*: Yes, you can press Tab to use this feature. +*A*: Don’t worry! Just try your best. If the format you've provided is wrong, you will be prompted with the correct format. == Command Summary diff --git a/docs/diagrams/ExportActivityDiagram.puml b/docs/diagrams/ExportActivityDiagram.puml index 43e85192f5f..9ba57595e98 100644 --- a/docs/diagrams/ExportActivityDiagram.puml +++ b/docs/diagrams/ExportActivityDiagram.puml @@ -1,22 +1,27 @@ @startuml start -:User executes export command; +:User executes export command, with a specified category and file path; 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([file path is valid]) - :Obtain the desired FlashCards; - :Create a new document; - if () then ([else]) +if () then ([file path is valid and represents\na document or JSON file]) + if () then ([Category exists and is non-empty]) + :Obtain all FlashCards from category; + if () then ([file path is for document file]) + :Create a new document; + else ([file path is for a JSON file]) + :Create a new JSON file; + endif repeat - :Write FlashCard to document; + :Write FlashCard to file; repeat while () is ([more FlashCards\nto write]) ->[all FlashCards written]; - else ([no FlashCards to be written]) + :Save file to given file path; + :Display success message; + else ([else]) + :Display error message; endif - :Save document to given file path; - :Display success message; else ([else]) :Display error message; endif diff --git a/docs/diagrams/ExportClassDiagram.puml b/docs/diagrams/ExportClassDiagram.puml new file mode 100644 index 00000000000..f4733c4bbfb --- /dev/null +++ b/docs/diagrams/ExportClassDiagram.puml @@ -0,0 +1,65 @@ +@startuml +!include style.puml + +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR + + + +Package Logic <>{ + Class ExportCommand LOGIC_COLOR + Class ImportCommand LOGIC_COLOR + Class ExportCommandParser LOGIC_COLOR + Class ImportCommandParser LOGIC_COLOR + Abstract Class Command LOGIC_COLOR + Class Parser <> LOGIC_COLOR +} + +Command <|- ExportCommand LOGIC_COLOR +Command <|- ImportCommand LOGIC_COLOR +Parser <|.. ExportCommandParser LOGIC_COLOR +Parser <|.. ImportCommandParser LOGIC_COLOR + +Parser .. Command LOGIC_COLOR +ExportCommandParser .. ExportCommand LOGIC_COLOR +ImportCommandParser .. ImportCommand LOGIC_COLOR + + + +Package seedu.address.model.export <>{ + Class DirectoryPath + Class DocumentExportUtil + Class DocumentFilePath + Class DocumentPath + Abstract Class ExportPath + Class ExportPathFactory + Class JsonExportFilePath + Class JsonExportPath + Class JsonExportUtil + Class JsonImportUtil +} + +ExportPath <|- DocumentPath +ExportPath <|- JsonExportPath + +DocumentPath *-- DocumentFilePath +DocumentPath *-- DirectoryPath + +JsonExportPath *-- JsonExportFilePath +JsonExportPath *-- DirectoryPath + +ExportPathFactory .up. ExportPath + +DocumentExportUtil .. DocumentPath +JsonExportUtil .. JsonExportPath +JsonImportUtil .. JsonExportPath + + + +ExportCommand "0..1" ------> "1" ExportPath LOGIC_COLOR +ImportCommand "0..1" --> "1" ExportPath LOGIC_COLOR + +ExportCommandParser .. ExportPath LOGIC_COLOR +ImportCommandParser .. ExportPath LOGIC_COLOR +@enduml diff --git a/docs/diagrams/ExportSequenceDiagram.puml b/docs/diagrams/ExportSequenceDiagram.puml index d335c91f384..bbdc0330ef6 100644 --- a/docs/diagrams/ExportSequenceDiagram.puml +++ b/docs/diagrams/ExportSequenceDiagram.puml @@ -2,15 +2,13 @@ !include style.puml box Logic LOGIC_COLOR_T1 -participant ":ExportCommand" as ExportCommand LOGIC_COLOR +participant ":ExportCommand" as ExportCommand LOGIC_COLOR_T4 end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR -end box - -box Commons COMMONS_COLOR_T1 -participant "<>\n:ExportUtil" as ExportUtil COMMONS_COLOR +participant ":DocumentPath" as DocumentPath MODEL_COLOR_T3 +participant "<>\n:DocumentExportUtil" as DocumentExportUtil MODEL_COLOR_T4 end box box org.apache.poi.xwpf.usermodel EXTERNAL_LIBRARY_COLOR_T1 @@ -30,33 +28,47 @@ activate Model ExportCommand <-- Model : list deactivate Model -ExportCommand -> ExportUtil : exportFlashCards(list, documentPath) -activate ExportUtil +ExportCommand -> ExportCommand : wipeTransientData(list) +activate ExportCommand +ExportCommand <-- ExportCommand : list +deactivate ExportCommand + +ExportCommand -> DocumentPath : export(list) +activate DocumentPath + +DocumentPath -> DocumentExportUtil : exportFlashCardsToDocument(list, documentPath) +activate DocumentExportUtil create XWPFDocument -ExportUtil -> XWPFDocument : XWPFDocument() +DocumentExportUtil -> XWPFDocument : XWPFDocument() activate XWPFDocument -ExportUtil <-- XWPFDocument : document +DocumentExportUtil <-- XWPFDocument : document deactivate XWPFDocument loop while there are still FlashCards to write - ExportUtil -> XWPFDocument : createParagraph() + DocumentExportUtil -> XWPFDocument : createParagraph() activate XWPFDocument - ExportUtil <-- XWPFDocument + DocumentExportUtil <-- XWPFDocument deactivate XWPFDocument end -ExportUtil -> ExportUtil : writeDocumentToFile(document, documentPath) -activate ExportUtil +DocumentExportUtil -> DocumentExportUtil : writeDocumentToFile(document, documentPath) +activate DocumentExportUtil -ExportUtil <-- ExportUtil -deactivate ExportUtil +DocumentExportUtil <-- DocumentExportUtil +deactivate DocumentExportUtil -ExportCommand <-- ExportUtil -deactivate ExportUtil +DocumentPath <-- DocumentExportUtil +deactivate DocumentExportUtil +destroy XWPFDocument -[<--ExportCommand +ExportCommand <-- DocumentPath +deactivate DocumentPath + +[<-- ExportCommand : commandResult deactivate ExportCommand +destroy DocumentPath + @enduml diff --git a/docs/diagrams/ImportActivityDiagram.puml b/docs/diagrams/ImportActivityDiagram.puml new file mode 100644 index 00000000000..c5f614324a6 --- /dev/null +++ b/docs/diagrams/ImportActivityDiagram.puml @@ -0,0 +1,27 @@ +@startuml +start +:User executes import command, with a specified file path; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([file path is valid and represents a JSON file]) + :Open JSON file; + if () then ([JSON file is valid\nand non-empty]) + repeat + :Read next FlashCard from file; + if () then ([User already has a\ncopy of this FlashCard]) + else ([else]) + :Copy FlashCard into user's data; + endif + repeat while () is ([there are more\nFlashCards to read]) + ->[else]; + :Display success message; + else ([else]) + :Display error message; + endif +else ([else]) + :Display error message; +endif +stop +@enduml diff --git a/docs/images/ExportActivityDiagram.png b/docs/images/ExportActivityDiagram.png index 17f2297dbdd..a919840d407 100644 Binary files a/docs/images/ExportActivityDiagram.png and b/docs/images/ExportActivityDiagram.png differ diff --git a/docs/images/ExportClassDiagram.png b/docs/images/ExportClassDiagram.png new file mode 100644 index 00000000000..1ea16fb1cca Binary files /dev/null and b/docs/images/ExportClassDiagram.png differ diff --git a/docs/images/ExportDemo1.png b/docs/images/ExportDemo1.png new file mode 100644 index 00000000000..48aaca7428d Binary files /dev/null and b/docs/images/ExportDemo1.png differ diff --git a/docs/images/ExportDemo2.png b/docs/images/ExportDemo2.png new file mode 100644 index 00000000000..a46fde27184 Binary files /dev/null and b/docs/images/ExportDemo2.png differ diff --git a/docs/images/ExportDemo3.png b/docs/images/ExportDemo3.png new file mode 100644 index 00000000000..64adab75a89 Binary files /dev/null and b/docs/images/ExportDemo3.png differ diff --git a/docs/images/ExportSequenceDiagram.png b/docs/images/ExportSequenceDiagram.png index 7506c4e086b..649858bac97 100644 Binary files a/docs/images/ExportSequenceDiagram.png and b/docs/images/ExportSequenceDiagram.png differ diff --git a/docs/images/ImportActivityDiagram.png b/docs/images/ImportActivityDiagram.png new file mode 100644 index 00000000000..5fde0defd5f Binary files /dev/null and b/docs/images/ImportActivityDiagram.png differ diff --git a/docs/images/ImportDemo1.png b/docs/images/ImportDemo1.png new file mode 100644 index 00000000000..a5c1680fd47 Binary files /dev/null and b/docs/images/ImportDemo1.png differ diff --git a/docs/images/ImportDemo2.png b/docs/images/ImportDemo2.png new file mode 100644 index 00000000000..0a5d4cb928d Binary files /dev/null and b/docs/images/ImportDemo2.png differ diff --git a/docs/team/leowwb.adoc b/docs/team/leowwb.adoc index 59220f09401..c335b1a9612 100644 --- a/docs/team/leowwb.adoc +++ b/docs/team/leowwb.adoc @@ -13,19 +13,23 @@ means that the primary means of user input to the product must be through the ty This project was the result of six weeks of hard work by my team, which comprises two final-year Computer Engineering students and three sophomore Computer Science students (including myself). All are students in the National University of Singapore (NUS). -Our final product is _KeyboardFlashCards_ - a flashcard manager application targeted at computing students (in particular, NUS School of Computing students), -who will be able to use it to enhance their learning experience. It does this by simulating a real deck of flashcards, complete with features including creation, editing, -deletion, revision, and sharing of flashcards. This application is written in the Java programming language, and has a GUI (graphical user interface) created with the JavaFX library. -It has roughly 17 KLoC, up from the original addressbook application's 10 KLoC. +Our final product is _KeyboardFlashCards_ - a desktop flashcard manager application targeted at +computing students (in particular, NUS School of Computing students), who will be able to use it to +enhance their learning experience. It does this by simulating a real deck of flashcards on the user's +computer, complete with features including creation, editing, deletion, revision, and sharing of +flashcards. The application is written in the Java programming language, and has a GUI (graphical +user interface) created with the JavaFX library. It has roughly 17 KLoC, up from the original +addressbook application's 10 KLoC. Here is a screenshot of our project: -image::Ui.png[KeyboardFlashCards] +image::annotatedUi.png[KeyboardFlashCards] _Figure 1. The graphical user interface of **KeyboardFlashCards**_ Please note the following symbols and formatting, which will appear throughout the rest of the document: -TODO - +NOTE: A point of information to take note of +TIP: A tip or suggestion +CAUTION: A cautionary piece of advice == Summary of contributions @@ -34,18 +38,29 @@ TODO |=== * *Major enhancement #1*: I added *the ability to export flashcards to various file formats*. -** What it does: It allows the user to save a subset of their flashcards to a specified external file. The file format can be either '.docx' or '.json', and is inferred from the file extension specified by the user. -** Justification: This feature greatly improves the application, as the ability to export flashcards would mean that the user can easily generate cheat sheets or share flashcards with others. -** Highlights: This enhancement was designed in such a way as to maximize extensibility in the future (if a developer wishes to add support for more file formats). It required an in-depth analysis of design alternatives. The implementation was challenging due to the dependence on an external library, as well as the tendency of certain file paths to break our parser. -** Credits: All writing of flashcards to `.docx` format is done with the use of Apache POI. - -* *Major enhancement #2: I added *the ability to import flashcards*. -** What it does: It allows the user to import flashcards from JSON format (files that end in '.json'). -** Justification: A user could previously import '.json' files that were previously exported, by copying and pasting the file over their existing save file. This feature allows users to do the same from the convenience of the application's CLI, Furthermore, importing flashcards in this manner will not result in any loss to their original data. -** Highlights: TODO -** Credits: TODO - -//* *Minor enhancement*: ??? +** What it does: It allows the user to save their flashcards from the application into a specified external file. + The file format can be either '.docx' or '.json', and is inferred from the file extension specified by the user. + To avoid clutter, any data that would only be relevant in the context of the application (e.g. a user-given rating) + is not exported with the flashcards. +** Justification: This feature greatly improves the application. The ability to export flashcards + means that the user can easily generate cheat sheets (by exporting to a document file), + or share flashcards with others (by exporting to a JSON file) - all through the convenience of a single command. +** Highlights: This enhancement was designed in such a way as to maximize extensibility in the future + (if a future developer ever wishes to add support for more file formats). It required an in-depth analysis of design alternatives. + The implementation was challenging due to the dependence on an external library, + as well as the tendency of certain file paths to break our user input parser. +** Credits: All writing of flashcards to the '.docx' and '.json' formats is done with the use of two external libraries; + these are Apache POI and Jackson, respectively. + +* *Major enhancement #2: I added *the ability to import flashcards from an external file*. +** What it does: It allows the user to import flashcards from a JSON file (a file that ends in '.json'). + The imported flashcards are added to the application, and the user will be able to use them as if they were their own. +** Justification: This feature, when used in combination with the `export` feature, allows users to share their flashcards with + one another. +** Highlights: This feature does not import flashcards which the user already has. This is because + there's no good reason for the user to have two copies of the same flashcard. + The user will be notified if a duplicate is detected in a file while importing. +** Credits: All reading of flashcards from the `.json` format is done with the use of Jackson, an external library. * *Code contributed*: Please click on these links to see a sample of my code: [https://github.com[Functional code]] [https://github.com[Test code]] _{give links to collated code files}_ (reposense link??) @@ -53,31 +68,39 @@ TODO * *Other contributions* ** Project management -*** Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub -** Enhancements to existing features -*** Updated the GUI color scheme (Pull requests https://github.com[#33], https://github.com[#34]) -*** Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests https://github.com[#36], https://github.com[#38]) +*** Managed releases `v1.1` - `v1.2` (2 releases) on GitHub +*** Updated the project website with the team's details ( + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/19[#19], + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/24[#24] + ) ** Documentation -*** Wrote the User Guide and Developer Guide content pertaining to the exporting and importing of flashcards https://github.com[#14] -*** TODO wrote the qna? what else ah https://github.com[#14] -*** Did cosmetic tweaks to existing contents of the User Guide: https://github.com[#14] +*** Created the general framework that would be used by the User Guide ( + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/19[#19] + ) +*** Wrote the FAQ and an early version of the Command Summary section for the User Guide ( + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/19[#19] + ) +*** Wrote the User Guide and Developer Guide content pertaining to the exporting and importing of flashcards ( + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/137[#137], + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/141[#141], + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/151[#151], + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/221[#221], + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/224[#224] + ) ** Community -*** PRs reviewed (with non-trivial review comments): +*** Reviewed the following PRs (with non-trivial review comments): https://github.com/AY1920S1-CS2103T-T12-4/main/pull/98[#98], https://github.com/AY1920S1-CS2103T-T12-4/main/pull/154[#154] -*** Contributed to forum discussions (examples: https://github.com[1], https://github.com[2], https://github.com[3], https://github.com[4]) *** Reported bugs and suggestions for other teams in the class (examples: https://github.com/nus-cs2103-AY1920S1/addressbook-level3/pull/53[1], - https://github.com/nus-cs2103-AY1920S1/addressbook-level3/pull/23[2], - TODO the one reviewed for mock PE? + https://github.com/nus-cs2103-AY1920S1/addressbook-level3/pull/23[2] ) -*** Some parts of the history feature I added was adopted by several other class mates (https://github.com[1], https://github.com[2]) ** Tools -*** Integrated a third party library (Apache POI) to the project (https://github.com/AY1920S1-CS2103T-T12-4/main/pull/44[#44]) +*** Integrated a third party library (Apache POI) to the project ( + https://github.com/AY1920S1-CS2103T-T12-4/main/pull/44[#44] + ) *** Integrated new Github plugins (Travis CI, Appveyor, Coveralls) to the team repository -_{you can add/remove categories in the list above}_ - == Contributions to the User Guide |=== @@ -88,7 +111,7 @@ _(start of extract from User Guide)_ include::../UserGuide.adoc[tag=flashcardexport] -include::../UserGuide.adoc[tag=dataencryption] +include::../UserGuide.adoc[tag=flashcardimport] _(end of extract from User Guide)_ @@ -98,16 +121,8 @@ _(end of extract from User Guide)_ |_The following are sections that I have written for the Developer Guide. They showcase my ability to write technical documentation targeting developers, as well as the technical depth of my contributions to the project._ |=== -_(start of extract from User Guide)_ - -include::../DeveloperGuide.adoc[tag=flashcardexport] - -include::../DeveloperGuide.adoc[tag=dataencryption] - -_(end of extract from User Guide)_ - -== PROJECT: PowerPointLabs +_(start of extract from Developer Guide)_ ---- +include::../DeveloperGuide.adoc[tag=flashcardexportimport] -_{Optionally, you may include other projects in your portfolio.}_ +_(end of extract from Developer Guide)_ diff --git a/src/main/java/seedu/address/logic/commands/ExportCommand.java b/src/main/java/seedu/address/logic/commands/ExportCommand.java index 82c8ac1b858..58ed4a2dae2 100644 --- a/src/main/java/seedu/address/logic/commands/ExportCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExportCommand.java @@ -3,6 +3,7 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import static seedu.address.logic.parser.CliSyntax.PREFIX_CATEGORY; import static seedu.address.logic.parser.CliSyntax.PREFIX_EXPORT_PATH; @@ -96,6 +97,8 @@ public boolean equals(Object other) { * @return ObservableList of the FlashCards in the model that match the Category. */ public static ObservableList getFlashCardsByCategory(Model model, Category category) { + requireAllNonNull(model, category); + return model.getFilteredFlashCardListNoCommit( categoryToPredicate( category @@ -110,6 +113,8 @@ public static ObservableList getFlashCardsByCategory(Model model, Cat * @return CategoryContainsAnyKeywordsPredicate representing the Category. */ public static CategoryContainsAnyKeywordsPredicate categoryToPredicate(Category category) { + requireNonNull(category); + return new CategoryContainsAnyKeywordsPredicate( categoryToKeywordList( category @@ -124,6 +129,8 @@ public static CategoryContainsAnyKeywordsPredicate categoryToPredicate(Category * @return Singleton List containing the given Category. */ public static List categoryToKeywordList(Category category) { + requireNonNull(category); + String categoryName = category.categoryName; List categoryKeywordList = Collections.singletonList(categoryName); return categoryKeywordList; @@ -131,7 +138,7 @@ public static List categoryToKeywordList(Category category) { private static void verifyNonEmptyFlashCardList(List flashCardList, String message) throws CommandException { - if (flashCardList.size() == 0) { + if (flashCardList == null || flashCardList.size() == 0) { throw new CommandException(message); } } @@ -147,6 +154,9 @@ private static void verifyNonEmptyFlashCardList(List flashCardList, S * @return A List of FlashCards with the transient data wiped */ private static List wipeTransientData(List flashCardList, Category category) { + assert flashCardList.size() > 0; + requireAllNonNull(flashCardList, category); + return flashCardList.stream().map( flashCard -> new FlashCard( flashCard.getQuestion(), diff --git a/src/main/java/seedu/address/logic/commands/ImportCommand.java b/src/main/java/seedu/address/logic/commands/ImportCommand.java index 24d8bb77531..e75660941ce 100644 --- a/src/main/java/seedu/address/logic/commands/ImportCommand.java +++ b/src/main/java/seedu/address/logic/commands/ImportCommand.java @@ -83,8 +83,8 @@ public boolean equals(Object other) { * @throws CommandException If the Optional List turns out to be empty. */ private CommandResult applyImport(Model model, Optional> optionalList) throws CommandException { - verifyOptionalFlashCardListPresent(optionalList, MESSAGE_IMPORT_EMPTY_OPTIONAL); + requireNonNull(model); int successCount = 0; int duplicateCount = 0; @@ -112,7 +112,7 @@ private CommandResult applyImport(Model model, Optional> optiona */ private void verifyOptionalFlashCardListPresent(Optional> optionalList, String message) throws CommandException { - if (optionalList.isEmpty()) { + if (optionalList == null || optionalList.isEmpty()) { throw new CommandException( message ); @@ -127,6 +127,9 @@ private void verifyOptionalFlashCardListPresent(Optional> option * @return String representing the formatted success message */ private String formatCommandResultString(int successCount, int duplicateCount) { + assert successCount >= 0; + assert duplicateCount >= 0; + if (successCount == 0) { return MESSAGE_IMPORT_ALL_DUPLICATES; } diff --git a/src/main/java/seedu/address/model/export/DirectoryPath.java b/src/main/java/seedu/address/model/export/DirectoryPath.java index edb275f21fb..dbe7fffd8fe 100644 --- a/src/main/java/seedu/address/model/export/DirectoryPath.java +++ b/src/main/java/seedu/address/model/export/DirectoryPath.java @@ -2,6 +2,7 @@ package seedu.address.model.export; +import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; import java.io.IOException; @@ -49,6 +50,7 @@ public DirectoryPath(Path directoryPath) { * Returns true if a given string is a valid DirectoryPath. */ public static boolean isValid(String test) { + requireNonNull(test); return test.matches(VALIDATION_REGEX); } diff --git a/src/main/java/seedu/address/model/export/DocumentExportUtil.java b/src/main/java/seedu/address/model/export/DocumentExportUtil.java index ca446afb3d4..a60cf223c7d 100644 --- a/src/main/java/seedu/address/model/export/DocumentExportUtil.java +++ b/src/main/java/seedu/address/model/export/DocumentExportUtil.java @@ -2,6 +2,9 @@ package seedu.address.model.export; +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + import java.io.FileOutputStream; import java.io.IOException; import java.util.List; @@ -27,7 +30,9 @@ public class DocumentExportUtil { * @throws IOException If an error arises in writing to the File. */ public static void exportFlashCardsToDocument(List cards, DocumentPath documentPath) throws IOException { + requireAllNonNull(cards, documentPath); assert DocumentPath.isValid(documentPath.toString()); + assert cards.size() > 0; XWPFDocument doc = new XWPFDocument(); @@ -50,6 +55,8 @@ public static void exportFlashCardsToDocument(List cards, DocumentPat * @param doc The XWPFDocument to which the FlashCard will be added. */ private static void addFlashCardToDocument(FlashCard card, XWPFDocument doc) { + requireAllNonNull(card, doc); + Question question = card.getQuestion(); Answer answer = card.getAnswer(); XWPFParagraph paragraph = doc.createParagraph(); @@ -59,13 +66,29 @@ private static void addFlashCardToDocument(FlashCard card, XWPFDocument doc) { addRun(paragraph, answer.toString(), false); } + /** + * Adds a run to the given XWPFParagraph, containing the specified text and with the specified boldface. + * + * @param paragraph Paragraph that we will add a run to. + * @param text Text to add in paragraph + * @param isBold Whether the run should be bold + */ private static void addRun(XWPFParagraph paragraph, String text, boolean isBold) { + requireAllNonNull(paragraph, text); + XWPFRun run = paragraph.createRun(); run.setText(text); run.setBold(isBold); } + /** + * Adds a line break to the given XWPFParagraph. + * + * @param paragraph XWPFParagraph to add line break to. + */ private static void addLineBreak(XWPFParagraph paragraph) { + requireNonNull(paragraph); + XWPFRun run = paragraph.createRun(); run.addCarriageReturn(); } @@ -78,6 +101,9 @@ private static void addLineBreak(XWPFParagraph paragraph) { * @throws IOException If there is an error in writing to the File. */ private static void writeDocumentToFile(XWPFDocument doc, DocumentPath documentPath) throws IOException { + requireAllNonNull(doc, documentPath); + assert DocumentPath.isValid(documentPath.toString()); + try { FileOutputStream out = new FileOutputStream(documentPath.toString()); doc.write(out); diff --git a/src/main/java/seedu/address/model/export/DocumentFilePath.java b/src/main/java/seedu/address/model/export/DocumentFilePath.java index e5c80d9a346..b1763f3f25d 100644 --- a/src/main/java/seedu/address/model/export/DocumentFilePath.java +++ b/src/main/java/seedu/address/model/export/DocumentFilePath.java @@ -38,6 +38,7 @@ public DocumentFilePath(String documentFilePath) { * Returns true if a given string is a valid document file path. */ public static boolean isValid(String test) { + requireNonNull(test); return test.matches(VALIDATION_REGEX); } diff --git a/src/main/java/seedu/address/model/export/DocumentPath.java b/src/main/java/seedu/address/model/export/DocumentPath.java index 1c285227c44..ef2f4a865a3 100644 --- a/src/main/java/seedu/address/model/export/DocumentPath.java +++ b/src/main/java/seedu/address/model/export/DocumentPath.java @@ -8,7 +8,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.Optional; @@ -52,6 +51,7 @@ public DocumentPath(String documentPath) { * Returns true if a given string is a valid document path. */ public static boolean isValid(String test) { + requireNonNull(test); return test.matches(VALIDATION_REGEX); } @@ -62,13 +62,12 @@ public static boolean isValid(String test) { * @return DocumentFilePath representing the path of the document, relative to its immediate parent directory */ private static DocumentFilePath extractDocumentFilePath(String documentFilePathString) { - Path fullPath = Paths.get(documentFilePathString); - int nameCount = fullPath.getNameCount(); + requireNonNull(documentFilePathString); return new DocumentFilePath( - fullPath - .subpath(nameCount - 1, nameCount) - .toString() + ExportPath.extractFilePathNoDirectoryString( + documentFilePathString + ) ); } diff --git a/src/main/java/seedu/address/model/export/ExportPath.java b/src/main/java/seedu/address/model/export/ExportPath.java index 967b59caffe..f9fe30c905d 100644 --- a/src/main/java/seedu/address/model/export/ExportPath.java +++ b/src/main/java/seedu/address/model/export/ExportPath.java @@ -2,6 +2,8 @@ package seedu.address.model.export; +import static java.util.Objects.requireNonNull; + import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -32,6 +34,8 @@ public abstract Optional> importFrom() * @return DirectoryPath representing the path of the most nested directory within the given String */ static DirectoryPath extractDirectoryPath(String exportPathString) { + requireNonNull(exportPathString); + return new DirectoryPath( Paths.get( exportPathString @@ -53,4 +57,23 @@ public String toAbsolutePathString() { .normalize() .toString(); } + + /** + * Given a String representing a file path, extract the portion of the String that corresponds to the path + * to the file from its immediate parent directory. + * e.g. {@code extractFilePathNoDirectoryString("folder/directory/file.ext")} will return {@code "file.ext"}. + * + * @param fullPathString String representing the full file path + * @return String representing path to the file from its immediate parent directory + */ + static String extractFilePathNoDirectoryString(String fullPathString) { + requireNonNull(fullPathString); + + Path fullPath = Paths.get(fullPathString); + int nameCount = fullPath.getNameCount(); + + return fullPath + .subpath(nameCount - 1, nameCount) + .toString(); + } } diff --git a/src/main/java/seedu/address/model/export/ExportPathFactory.java b/src/main/java/seedu/address/model/export/ExportPathFactory.java index 8b26cac707d..8ad7089c0d3 100644 --- a/src/main/java/seedu/address/model/export/ExportPathFactory.java +++ b/src/main/java/seedu/address/model/export/ExportPathFactory.java @@ -2,6 +2,8 @@ package seedu.address.model.export; +import static java.util.Objects.requireNonNull; + /** * Class used for creating of {@code ExportPath}s. Follows the factory pattern. */ @@ -21,6 +23,8 @@ public class ExportPathFactory { * @throws IllegalArgumentException if the given String does not match the format of any of the ExportPaths */ public static ExportPath getExportPath(String exportPath) throws IllegalArgumentException { + requireNonNull(exportPath); + if (DocumentPath.isValid(exportPath)) { return new DocumentPath(exportPath); } else if (JsonExportPath.isValid(exportPath)) { diff --git a/src/main/java/seedu/address/model/export/JsonExportFilePath.java b/src/main/java/seedu/address/model/export/JsonExportFilePath.java index 8d51c8a2fea..6a78622aeb9 100644 --- a/src/main/java/seedu/address/model/export/JsonExportFilePath.java +++ b/src/main/java/seedu/address/model/export/JsonExportFilePath.java @@ -38,6 +38,7 @@ public JsonExportFilePath(String jsonExportFilePath) { * Returns true if a given string is a valid jsonExport file path. */ public static boolean isValid(String test) { + requireNonNull(test); return test.matches(VALIDATION_REGEX); } diff --git a/src/main/java/seedu/address/model/export/JsonExportPath.java b/src/main/java/seedu/address/model/export/JsonExportPath.java index 232384c438e..d181233fa55 100644 --- a/src/main/java/seedu/address/model/export/JsonExportPath.java +++ b/src/main/java/seedu/address/model/export/JsonExportPath.java @@ -8,7 +8,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.Optional; @@ -53,6 +52,7 @@ public JsonExportPath(String jsonExportPath) { * Returns true if a given string is a valid json export path. */ public static boolean isValid(String test) { + requireNonNull(test); return test.matches(VALIDATION_REGEX); } @@ -64,13 +64,10 @@ public static boolean isValid(String test) { * relative to its immediate parent directory */ private static JsonExportFilePath extractJsonExportFilePath(String jsonExportPathString) { - Path fullPath = Paths.get(jsonExportPathString); - int nameCount = fullPath.getNameCount(); + requireNonNull(jsonExportPathString); return new JsonExportFilePath( - fullPath - .subpath(nameCount - 1, nameCount) - .toString() + ExportPath.extractFilePathNoDirectoryString(jsonExportPathString) ); } @@ -89,6 +86,8 @@ public String toString() { @Override public void export(List list) throws IOException { + requireNonNull(list); + try { directoryPath.createIfNotPresent(); JsonExportUtil.exportFlashCardsToJson(list, this); diff --git a/src/main/java/seedu/address/model/export/JsonExportUtil.java b/src/main/java/seedu/address/model/export/JsonExportUtil.java index 91c1b6356ae..60da2ca27ab 100644 --- a/src/main/java/seedu/address/model/export/JsonExportUtil.java +++ b/src/main/java/seedu/address/model/export/JsonExportUtil.java @@ -2,6 +2,8 @@ package seedu.address.model.export; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + import java.io.IOException; import java.util.List; @@ -22,6 +24,8 @@ public class JsonExportUtil { * @throws IOException If an error arises in writing to the File. */ public static void exportFlashCardsToJson(List cards, JsonExportPath jsonExportPath) throws IOException { + requireAllNonNull(cards, jsonExportPath); + assert cards.size() > 0; assert JsonExportPath.isValid(jsonExportPath.toString()); KeyboardFlashCards exportKfc = new KeyboardFlashCards(); diff --git a/src/main/java/seedu/address/model/export/JsonImportUtil.java b/src/main/java/seedu/address/model/export/JsonImportUtil.java index c3e3273adfb..57a89698708 100644 --- a/src/main/java/seedu/address/model/export/JsonImportUtil.java +++ b/src/main/java/seedu/address/model/export/JsonImportUtil.java @@ -2,6 +2,8 @@ package seedu.address.model.export; +import static java.util.Objects.requireNonNull; + import java.io.IOException; import java.util.List; import java.util.Optional; @@ -26,6 +28,8 @@ public class JsonImportUtil { */ public static Optional> importFlashCardsFromJson(JsonExportPath jsonExportPath) throws DataConversionException { + + requireNonNull(jsonExportPath); assert JsonExportPath.isValid(jsonExportPath.toString()); return readFromOptionalKfc( @@ -37,6 +41,9 @@ public static Optional> importFlashCardsFromJson(JsonExportPath private static Optional getOptionalKfc(JsonExportPath jsonExportPath) throws DataConversionException { + + requireNonNull(jsonExportPath); + JsonKeyboardFlashCardsStorage jsonStorage = new JsonKeyboardFlashCardsStorage( jsonExportPath.getPath() ); @@ -54,6 +61,8 @@ private static Optional getOptionalKfc(JsonExportPat * {@code KeyboardFlashCards} Optional. */ private static Optional> readFromOptionalKfc(Optional optionalKfc) { + requireNonNull(optionalKfc); + if (!optionalKfc.isPresent()) { return Optional.empty(); } diff --git a/src/test/java/seedu/address/model/export/DocumentExportUtilTest.java b/src/test/java/seedu/address/model/export/DocumentExportUtilTest.java index 483d203ee0e..20c1500ef19 100644 --- a/src/test/java/seedu/address/model/export/DocumentExportUtilTest.java +++ b/src/test/java/seedu/address/model/export/DocumentExportUtilTest.java @@ -23,8 +23,7 @@ public class DocumentExportUtilTest { public void exportFlashCardsToDocument_valid_success() { List> listsToTest = Arrays.asList( TypicalFlashCards.getTypicalFlashCards(), - TypicalFlashCards.getSingletonFlashCardList(), - TypicalFlashCards.getEmptyFlashCardList() + TypicalFlashCards.getSingletonFlashCardList() ); for (List list : listsToTest) { diff --git a/src/test/java/seedu/address/model/export/DocumentPathTest.java b/src/test/java/seedu/address/model/export/DocumentPathTest.java index e499c60eec3..0813c88f083 100644 --- a/src/test/java/seedu/address/model/export/DocumentPathTest.java +++ b/src/test/java/seedu/address/model/export/DocumentPathTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.ExportTestUtil.isRunningOnWindows; import org.junit.jupiter.api.Test; @@ -49,7 +50,20 @@ public void documentPath_validPath_success() { try { new DocumentPath(validDocumentPathString); } catch (IllegalArgumentException e) { - fail("Valid document path was not recognized as being valid"); + fail("Valid document path was not recognized as being valid: " + validDocumentPathString); + } + } + } + + @Test + public void documentPath_validWindowsPath_success() { + String pathString = "C:\\Users\\User\\Desktop\\[CS2105] Midterm Cheat Sheet (v2).docx"; + + if (isRunningOnWindows()) { + try { + new DocumentPath(pathString); + } catch (IllegalArgumentException e) { + fail("Valid document path was not recognized as being valid: " + pathString); } } } diff --git a/src/test/java/seedu/address/model/export/ExportPathFactoryTest.java b/src/test/java/seedu/address/model/export/ExportPathFactoryTest.java index 99031f93950..0d4bc0f3a36 100644 --- a/src/test/java/seedu/address/model/export/ExportPathFactoryTest.java +++ b/src/test/java/seedu/address/model/export/ExportPathFactoryTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.ExportTestUtil.isRunningOnWindows; import org.junit.jupiter.api.Test; @@ -41,7 +42,7 @@ public void getExportPath_valid_success() { String filePath = filePathNoExtension + validExtension; ExportPath exportPath = ExportPathFactory.getExportPath(filePath); - switch(validExtension.toLowerCase()) { + switch (validExtension.toLowerCase()) { case ".docx": assertTrue( exportPath instanceof DocumentPath @@ -53,7 +54,7 @@ public void getExportPath_valid_success() { ); break; default: - fail("Valid export path not recognized as valid."); + fail("Valid export path not recognized as valid: " + filePath); } } } @@ -71,4 +72,31 @@ public void getExportPath_invalidPath_exceptionThrown() { } } } + + @Test + public void getExportPath_validWindowsPath_success() { + if (isRunningOnWindows()) { + String filePathNoExtension = "C:\\Users\\User\\Desktop\\[CS2105] Midterm Cheat Sheet (v2)"; + + for (String validExtension : validExtensions) { + String filePath = filePathNoExtension + validExtension; + ExportPath exportPath = ExportPathFactory.getExportPath(filePath); + + switch (validExtension.toLowerCase()) { + case ".docx": + assertTrue( + exportPath instanceof DocumentPath + ); + break; + case ".json": + assertTrue( + exportPath instanceof JsonExportPath + ); + break; + default: + fail("Valid export path not recognized as valid: " + filePath); + } + } + } + } } diff --git a/src/test/java/seedu/address/model/export/JsonExportPathTest.java b/src/test/java/seedu/address/model/export/JsonExportPathTest.java index 3e22eccd5c5..195d77f6418 100644 --- a/src/test/java/seedu/address/model/export/JsonExportPathTest.java +++ b/src/test/java/seedu/address/model/export/JsonExportPathTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.ExportTestUtil.isRunningOnWindows; import org.junit.jupiter.api.Test; @@ -50,8 +51,22 @@ public void jsonExportPath_validPath_success() { try { new JsonExportPath(validJsonExportPathString); } catch (IllegalArgumentException e) { - fail("Valid JSON export path was not recognized as being valid"); + fail("Valid JSON export path was not recognized as being valid: " + validJsonExportPathString); } } } + + @Test + public void jsonExportPath_validWindowsPath_success() { + String pathString = "C:\\Users\\User\\Desktop\\[CS2105] Midterm Cheat Sheet (v2).json"; + + if (isRunningOnWindows()) { + try { + new JsonExportPath(pathString); + } catch (IllegalArgumentException e) { + fail("Valid JSON export path was not recognized as being valid: " + pathString); + } + } + } + } diff --git a/src/test/java/seedu/address/model/export/JsonExportUtilTest.java b/src/test/java/seedu/address/model/export/JsonExportUtilTest.java index a4431a3711f..1146c8135fa 100644 --- a/src/test/java/seedu/address/model/export/JsonExportUtilTest.java +++ b/src/test/java/seedu/address/model/export/JsonExportUtilTest.java @@ -23,8 +23,7 @@ public class JsonExportUtilTest { public void exportFlashCardsToJson_valid_success() { List> listsToTest = Arrays.asList( TypicalFlashCards.getTypicalFlashCards(), - TypicalFlashCards.getSingletonFlashCardList(), - TypicalFlashCards.getEmptyFlashCardList() + TypicalFlashCards.getSingletonFlashCardList() ); for (List list : listsToTest) { diff --git a/src/test/java/seedu/address/model/export/JsonImportUtilTest.java b/src/test/java/seedu/address/model/export/JsonImportUtilTest.java index e43e466fac6..131aa0c9777 100644 --- a/src/test/java/seedu/address/model/export/JsonImportUtilTest.java +++ b/src/test/java/seedu/address/model/export/JsonImportUtilTest.java @@ -25,7 +25,6 @@ public class JsonImportUtilTest { @Test public void importFlashCardsFromJson_valid_success() { List> listsToTest = Arrays.asList( - TypicalFlashCards.getEmptyFlashCardList(), TypicalFlashCards.getSingletonFlashCardList(), TypicalFlashCards.getTypicalFlashCards() ); diff --git a/src/test/java/seedu/address/testutil/ExportTestUtil.java b/src/test/java/seedu/address/testutil/ExportTestUtil.java index b1c4f06fee9..39d065c1cfb 100644 --- a/src/test/java/seedu/address/testutil/ExportTestUtil.java +++ b/src/test/java/seedu/address/testutil/ExportTestUtil.java @@ -45,4 +45,17 @@ public static void deleteFileIfExists(ExportPath exportPath) { deleteFile(exportPath); } } + + /** + * Checks the current operating system. Returns true if it is Windows, false otherwise. + * Rationale: Certain tests pertaining to ExportPaths will behave differently on Windows and Unix, due to both + * having different definitions of what makes a legal file path. + * + * @return true if the current OS is Windows, false otherwise + */ + public static boolean isRunningOnWindows() { + return System + .getProperty("os.name") + .contains("Windows"); + } }