diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 00000000..391c46b4 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,34 @@ +name: Java CI + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + + steps: + - name: Set up repository + uses: actions/checkout@master + + - name: Set up repository + uses: actions/checkout@master + with: + ref: master + + - name: Merge to master + run: git checkout --progress --force ${{ github.sha }} + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Setup JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + java-package: jdk+fx + + - name: Build and check with Gradle + run: ./gradlew check diff --git a/.gitignore b/.gitignore index f69985ef..ef409edb 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +src/main/java/META-INF/MANIFEST.MF diff --git a/README.md b/README.md index 9d95025b..da61b786 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# duke project template This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. @@ -15,7 +15,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. Click `Open or Import`. 1. Select the project directory, and click `OK` 1. If there are any further prompts, accept the defaults. -1. After the importing is complete, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below: +1. After the importing is complete, locate the `src/main/java/duke.java` file, right-click it, and choose `Run duke.main()`. If the setup is correct, you should see something like the below: ``` Hello from ____ _ diff --git a/Test/duke/ParserTest.java b/Test/duke/ParserTest.java new file mode 100644 index 00000000..6f9c7f15 --- /dev/null +++ b/Test/duke/ParserTest.java @@ -0,0 +1,47 @@ +package duke; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ParserTest { + String cmdTodo="todo read book"; + String cmdDeadline="deadline watch movie /by 2020-09-14"; + String cmdEvent="event meeting with John /at 2020-09-20"; + String cmdList = "list"; + String cmdDelete = "delete 1"; + String cmdDone = "done 1"; + String cmdUnknown = "what is this?"; + + @Test + void getCommand() { + Command todo = new TodoCommand("read book"); + Command deadline = new DeadlineCommand("watch movie","2020-09-14"); + Command event = new EventCommand("meeting with John","2020-09-20"); + Command list = new ListCommand(); + Command delete = new DeleteCommand("1"); + Command done = new DoneCommand("1"); + Command unknown = new OtherCommand("what"); + + try { + + assertEquals(todo.getCMDType(), Parser.getCommand(cmdTodo).getCMDType()); + assertEquals(deadline.getCMDType(),Parser.getCommand(cmdDeadline).getCMDType()); + assertEquals(event.getCMDType(),Parser.getCommand(cmdEvent).getCMDType()); + assertEquals(list.getCMDType(),Parser.getCommand(cmdList).getCMDType()); + assertEquals(delete.getCMDType(),Parser.getCommand(cmdDelete).getCMDType()); + assertEquals(done.getCMDType(),Parser.getCommand(cmdDone).getCMDType()); + assertEquals(unknown.getCMDType(),Parser.getCommand(cmdUnknown).getCMDType()); + + assertEquals(todo.getCMDContent(), Parser.getCommand(cmdTodo).getCMDContent()); + assertEquals(deadline.getCMDContent(),Parser.getCommand(cmdDeadline).getCMDContent()); + assertEquals(event.getCMDContent(),Parser.getCommand(cmdEvent).getCMDContent()); + assertEquals(delete.getCMDContent(),Parser.getCommand(cmdDelete).getCMDContent()); + assertEquals(done.getCMDContent(),Parser.getCommand(cmdDone).getCMDContent()); + + }catch(Exception e){ + e.getMessage(); + } + + } +} \ No newline at end of file diff --git a/Test/duke/TaskListTest.java b/Test/duke/TaskListTest.java new file mode 100644 index 00000000..4b80af8b --- /dev/null +++ b/Test/duke/TaskListTest.java @@ -0,0 +1,38 @@ +package duke; + +import duke.tasks.Deadlines; +import duke.tasks.Events; +import duke.tasks.Task; +import duke.tasks.ToDos; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.*; + +class TaskListTest { + + ToDos todo1 = new ToDos("read book"); + ToDos todo2 = new ToDos("read two book"); + Deadlines deadline1 = new Deadlines("watch movies", "2020-09-14"); + Events event1 = new Events("book a room", "2020-09-15"); + + @Test + void findKeywordList() { + ArrayList a = new ArrayList<>(); + TaskList original = new TaskList(); + String keyword = "book"; + + a.add(todo1); + a.add(todo2); + a.add(event1); + + original.add(todo1); + original.add(todo2); + original.add(deadline1); + original.add(event1); + + assertEquals(a, original.findKeywordList(keyword)); + + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..e502aa9a --- /dev/null +++ b/build.gradle @@ -0,0 +1,50 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +group 'org.example' +version '1.0' + +sourceCompatibility = JavaVersion.VERSION_11 +targetCompatibility = JavaVersion.VERSION_11 + + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + + //JavaFX dependencies + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' +} + +checkstyle { + toolVersion = '8.29' +} + +application { + mainClassName = "Launcher" +} + +shadowJar { + archiveBaseName = "Duke" + archiveClassifier = null +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..7951f3a6 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 00000000..135ea49e --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 00000000..db1eed32 --- /dev/null +++ b/data/duke.txt @@ -0,0 +1,4 @@ +T|1|read a book|# this is test /div/ +T|0|watch a movie +T|0|read more books +T|0|watch more movies diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md new file mode 100644 index 00000000..e563de08 --- /dev/null +++ b/docs/DeveloperGuide.md @@ -0,0 +1,142 @@ +# Developer Guide + +## Overall Look + +![Image of OverallLookDiagram](https://github.com/ZhengShijieNUS/ip/blob/master/docs/Ui.png?raw=true) + +## Design + +### Architechture + +The Architecture Diagram given below is a quick overview of each component. + +![Image of ArchitectureDiagram](https://github.com/ZhengShijieNUS/ip/blob/master/docs/architectureDiagram.png?raw=true) + +The rest of the App consists of few components. + +GUI: The UI of the App. + +Duke: The main logic handler of the App. + +Command: The control commands accepted by the App. + +Storage: Reads data from, and writes data to, the hard disk. + +Parser: Parse the input and convert the format which could be accepted by the app. + +TaskList: Define the structure of each tasks. + + +### GUI component + +The GUI componenet is the handler of the user input and output result to the user. It includes MainWindow Class which will deal with user input, +and DialogBox Class which will handle the conversation between user and app. + +Both MainWindow and DialogBox are initialized by the Launcher. + +### Duke component + +The Duke component includes logic handle command execution. It is used to provide an organised output and passed it to the GUI component. +Duke initializes the Storage and the Tasklist. + +### Storage Component + +The Storage component loads a list of tasks from a text file when the program starts up. It makes use of the Parser class to create a task object from a String. +It also saves tasks to file upon program exit. It saves to format that is readable by the Parser class. + + +### TaskList component + +The TaskList component is the container to save the data of tasks during execution of program. It consist an ArrayList to store all the tasks. +The Task class is the class of tasks as what we defined. It contains the task name, status, type, tags and description. + +The TaskList component: +* Able to interact with Storage component and load existing tasks +* Able to add new Todo/Deadline/Event tasks and store it in the instance of Task class +* Able to delete existing tasks that stored in the instance of TaskList class +* Able to get tasks that stored in the instance of TaskList class +* Able to get the whole TaskList itself +* Able to set/get all details of any specified task +* Able to add a tag to an existing task +* Able to find a task by its keyword + +### Command component and Parser component + +The Command component are those classes used by the parser class. + +The parser class is mainly in charge of parse user's input command and convert them to a workable format to the program. + + +## Implementation + +Tag Implementation + +The ```tag``` feature gives a tag to an existing task. It is facilitated by the Tag Class and inherited by Task Class which comprises of the following implementation: + +1. ```tag /t #content``` - give a tag content by the task index + + +### Value proposition +Manage tasks faster and allows the user to better manage the tasks. + +## User Stories + +|Priority| As a … | I want to … | So that I can… | +|--------|----------|---------------|------------------| +|* * * |New user|add tasks into the system|track my tasks| +|* * * |New user|view an task that is based on the id|see its content.| +|* * * |New user|delete a task|remove tasks that are completed or no longer required| +|* * * |New user|list all tasks currently in the task list|find all of the recorded tasks currently in the list.| +|* * |New user|I would like a given creating task format|so that I can know a structured way of using the app| +|...|...|...|...| + + +## Non-Functional Requirements + +* Should work on any mainstream OS as long as it has Java 11 or above installed. +* Should be able to hold up to 1000 tasks without a noticeable sluggishness in performance for typical usage. +* 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 +* Should load from and save to from a text files + + +## Glossary + +* *Mainstream OS:* Windows, Linux, Unix, OS-X. + +## Instructions for manual testing + +### Launch and Shutdown + +1. Initial launch + 1. Download the jar file and copy into an empty folder + 2. Use Command Prompt to run jar file with `java -jar` command. + 3. type bye command to exit from the program. + +2. Re-launch the app by running `java -jar` command with Command Prompt. + +### Adding a todo/deadline/event task +1. Add the task through following command: + 1. ```todo ``` + 2. ```deadline /by ``` + 3. ```event /at ``` + +### tag a task +1. Tag the task through following command: + 1. ```tag /t ``` + +### Listing all tasks in the system +1. Enter the command + 1. ```list``` +2. List of all tasks in the system will be listed in the order they were created. + +### Find a particular task in the system +1. Enter the command + 1. ```find ``` +2. The tasks with the specified keyword will be shown on screen in details. + +### Delete a particular task in the system +1. Enter the command + 1. ```Delete ```. Eg:delete 1 +2. The second Defect will be deleted. + + diff --git a/docs/README.md b/docs/README.md index fd440695..95fc55a6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,20 +1,7 @@ -# User Guide +# Duke Task Management System -## Features +Duke Task Manament System is a task management system with friendly GUI that allows user to perform a tasks management work by applying few certain commands. -### Feature 1 -Description of feature. - -## Usage - -### `Keyword` - Describe action - -Describe action and its outcome. - -Example of usage: - -`keyword (optional arguments)` - -Expected outcome: - -`outcome` +Useful links: +* [User Guide](UserGuide.md) +* [Developer Guide](DeveloperGuide.md) diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 00000000..7406a96e Binary files /dev/null and b/docs/Ui.png differ diff --git a/docs/UserGuide.md b/docs/UserGuide.md new file mode 100644 index 00000000..c7a681ed --- /dev/null +++ b/docs/UserGuide.md @@ -0,0 +1,185 @@ +# User Guide + +## Features + +The duke applet is a task management applet. It supports the following features + +### Feature 1 +_To **add a Todo task**. A Todo task is a basic type of task which is without time restriction._ + +### Feature 2 +_To **add a Deadline task**. A Deadline task is the task with a expiration date._ + +### Feature 3 +_To **add a Event task**. A Event task is the task with a specific time that the event occurs._ + +### Feature 4 +_**List down** all task exist._ + +### Feature 5 +_**Set task** status to be **done**._ + +### Feature 6 +_**Delete a task** from the list._ + +### Feature 7 +_**Find specific tasks** with **keyword** entered._ + +### Feature 8 +_**Auto saving** and **Auto loading** existing task file._ + +### Feature 9 +_**Add multiple tags** to a single task._ + +### Feature 10 +_Supporting friendly **GUI**._ + + +## Usage + +### `todo ` - add a todo task in the list + +This command will add a todo task in the list with specific content + +Example of usage: +``` +todo read a book +``` + +Expected outcome: +``` +Got it. I've added this task: +[T][✗] read a book +Now you have 1 tasks in the list. +``` + +### `deadline /by ` - add a deadline task in the list + +This command will add a deadline task in the list with specific content and deadline time. The time is supposed to be in format "yyyy-MM-dd". + +Example of usage: +``` +deadline read a book /by 2019-09-20 +``` + +Expected outcome: +``` +Got it. I've added this task: +[D][✗] read a book (by: Sep 20 2019) +Now you have 2 tasks in the list. +``` + +### `event /at ` - add a event task in the list + +This command will add a event task in the list with specific content and occuring time. The time is supposed to be in format "yyyy-MM-dd". + +Example of usage: +``` +event read a book /at 2019-09-20 +``` + +Expected outcome: +``` +Got it. I've added this task: +[E][✗] read a book (at: Sep 20 2019) +Now you have 3 tasks in the list. +``` + + + +### `list` - list down all tasks in the list + +This command will list down all tasks in the list. + +Example of usage: +``` +list +``` + +Expected outcome: +``` +Here are the tasks in your list: +1.[T][✗] read a book +2.[D][✗] read a book (by: Sep 20 2019) +3.[E][✗] read a book (at: Sep 20 2019) +``` + + + +### `done ` - set the status of the specific task to be done. + +This command will set the task to be done. + +Example of usage: +``` +done 2 +``` + +Expected outcome: +``` +Nice! I've marked this task as done: +[D][✓] read a book (by: Sep 20 2019) +``` + + +### `delete ` - delete the task with the specific index. + +This command will delete the task with the specific index in the list. + +Example of usage: +``` +delete 2 +``` + +Expected outcome: +``` +Noted. I've removed this task: +[D][✓] read a book (by: Sep 20 2019) +Now you have 2 tasks in the list. +``` + + +### `find ` - find the tasks with the keyword. + +This command will find all the tasks with the keyword specified in the list. + +Example of usage: +``` +find book +``` + +Expected outcome: +``` +Here are the matching tasks in your list: +1.[T][✗] read a book +2.[E][✗] read a book (at: Sep 20 2019) +``` + + +### `tag /t ` - add a tag to the task at the index. + +This command will allow user to add a tag to a single task in the list. + +Example of usage: +``` +tag 1 /t testing tag +``` + +Expected outcome: +``` +Adding tag testing tag to task 1 +``` +If `list`: +``` +Here are the matching tasks in your list: +1.[T][✗] read a book +# testing tag + +2.[E][✗] read a book (at: Sep 20 2019) +``` + + +## Acknowledgements +### 3rd party Libraries +* JavaFx: For graphics used for GUI + diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/docs/architectureDiagram.png b/docs/architectureDiagram.png new file mode 100644 index 00000000..815ce0db Binary files /dev/null and b/docs/architectureDiagram.png differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..87b738cb Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..dff5b12d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Jan 16 12:30:57 CST 2021 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..af6708ff --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..6d57edc7 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/apiguardian-api-1.0.0.jar b/lib/apiguardian-api-1.0.0.jar new file mode 100644 index 00000000..6cbff70f Binary files /dev/null and b/lib/apiguardian-api-1.0.0.jar differ diff --git a/lib/junit-jupiter-5.4.2.jar b/lib/junit-jupiter-5.4.2.jar new file mode 100644 index 00000000..b3bf6976 Binary files /dev/null and b/lib/junit-jupiter-5.4.2.jar differ diff --git a/lib/junit-jupiter-api-5.4.2.jar b/lib/junit-jupiter-api-5.4.2.jar new file mode 100644 index 00000000..40828b7a Binary files /dev/null and b/lib/junit-jupiter-api-5.4.2.jar differ diff --git a/lib/junit-jupiter-engine-5.4.2.jar b/lib/junit-jupiter-engine-5.4.2.jar new file mode 100644 index 00000000..3444a980 Binary files /dev/null and b/lib/junit-jupiter-engine-5.4.2.jar differ diff --git a/lib/junit-jupiter-params-5.4.2.jar b/lib/junit-jupiter-params-5.4.2.jar new file mode 100644 index 00000000..ee565041 Binary files /dev/null and b/lib/junit-jupiter-params-5.4.2.jar differ diff --git a/lib/junit-platform-commons-1.4.2.jar b/lib/junit-platform-commons-1.4.2.jar new file mode 100644 index 00000000..27055251 Binary files /dev/null and b/lib/junit-platform-commons-1.4.2.jar differ diff --git a/lib/junit-platform-engine-1.4.2.jar b/lib/junit-platform-engine-1.4.2.jar new file mode 100644 index 00000000..2c46ae92 Binary files /dev/null and b/lib/junit-platform-engine-1.4.2.jar differ diff --git a/lib/opentest4j-1.1.1.jar b/lib/opentest4j-1.1.1.jar new file mode 100644 index 00000000..3f355292 Binary files /dev/null and b/lib/opentest4j-1.1.1.jar differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..f67afec9 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'Duke' + diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java new file mode 100644 index 00000000..b0e0b4dd --- /dev/null +++ b/src/main/java/DialogBox.java @@ -0,0 +1,59 @@ +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + DialogBox db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334..2b343bc8 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,178 @@ +import java.io.IOException; + +import duke.Parser; +import duke.Storage; +import duke.TaskList; +import duke.Ui; +import duke.command.Command; +import duke.command.DeadlineCommand; +import duke.command.DeleteCommand; +import duke.command.DoneCommand; +import duke.command.EventCommand; +import duke.command.OtherCommand; +import duke.command.TagCommand; +import duke.exceptions.DukeException; +import duke.tasks.Deadlines; +import duke.tasks.Events; +import duke.tasks.Task; +import duke.tasks.ToDos; + public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + + static final String HELLO = " Hello! I'm Duke\n" + " What can I do for you?"; + + static final String BYE = " Bye. Hope to see you again soon!"; + + static final String path = "/data/duke.txt"; + private Ui ui; + private Storage storage; + private TaskList tasks; + + /** + * constructor + */ + public Duke() { + this.ui = new Ui(); + this.storage = new Storage(path); + this.tasks = new TaskList(); } + + String loadFile() { + try { + storage.load(tasks); + return "Load data successfully."; + } catch (DukeException | IOException e) { + return "Load data fails. Reason: " + e.getMessage(); + } + } + + private static String processCommand(Command cmd, TaskList tasks, Ui ui) { + StringBuffer s = new StringBuffer(); + + switch (cmd.getCmdType()) { + case "list": + s.append("Here are the tasks in your list:" + System.lineSeparator()); + listTasks(tasks, s); + return s.toString(); + case "find": + s.append("Here are the matching tasks in your list:" + System.lineSeparator()); + findKeywordTasks(tasks, cmd, s); + return s.toString(); + case "done": + setTaskDone(tasks, cmd, s); + return s.toString(); + case "delete": + deleteTask(tasks, cmd, s); + return s.toString(); + case "tag": + tagTask(tasks, cmd, s); + return s.toString(); + case "todo": + Task t = addTodoTask(tasks, cmd); + return ui.printTask(t, tasks); + case "deadline": + Task d = addDeadlineTask(tasks, cmd); + return ui.printTask(d, tasks); + case "event": + Task e = addEventTask(tasks, cmd); + return ui.printTask(e, tasks); + default: + return "Nothing done bro."; + } + } + + private static Task addEventTask(TaskList tasks, Command cmd) { + Events e = new Events(cmd.getCmdContent(), ((EventCommand) cmd).getTime()); + tasks.add(e); + return e; + } + + private static Task addDeadlineTask(TaskList tasks, Command cmd) { + Deadlines d = new Deadlines(cmd.getCmdContent(), ((DeadlineCommand) cmd).getTime()); + tasks.add(d); + return d; + } + + private static Task addTodoTask(TaskList tasks, Command cmd) { + ToDos t = new ToDos(cmd.getCmdContent()); + tasks.add(t); + return t; + } + + private static void tagTask(TaskList tasks, Command cmd, StringBuffer s) { + int index = ((TagCommand) cmd).getIndex(); + tasks.get(index - 1).addTag(cmd.getCmdContent()); + s.append("Adding tag " + cmd.getCmdContent() + " to task: " + index + System.lineSeparator()); + } + + private static void deleteTask(TaskList tasks, Command cmd, StringBuffer s) { + int index = ((DeleteCommand) cmd).getIndex(); + s.append("Noted. I've removed this task: " + System.lineSeparator()); + s.append(tasks.get(index - 1).toString() + System.lineSeparator()); + tasks.remove(index - 1); + s.append("Now you have " + tasks.size() + " tasks in the list."); + } + + private static void setTaskDone(TaskList tasks, Command cmd, StringBuffer s) { + int index = ((DoneCommand) cmd).getIndex(); + tasks.get(index - 1).setDone(true); + s.append("Nice! I've marked this task as done: " + System.lineSeparator()); + s.append(tasks.get(index - 1).toString() + System.lineSeparator()); + } + + private static void findKeywordTasks(TaskList tasks, Command cmd, StringBuffer s) { + int i = 1; + for (Task t : tasks.findKeywordList(cmd.getCmdContent())) { + s.append(i + "." + t.toString() + System.lineSeparator()); + i++; + } + } + + private static void listTasks(TaskList tasks, StringBuffer s) { + int i = 1; + for (Task t : tasks.getWholeList()) { + s.append(i + "." + t.toString() + System.lineSeparator()); + i++; + } + } + + /* + * You should have your own function to generate a response to user input. + * Replace this stub with your completed method. + */ + + String getResponse(String input) { + Command cmd = null; + String result = null; + + try { + cmd = Parser.getCommand(input); + } catch (DukeException e) { + return e.getMessage(); + } catch (Exception e) { + return "\u2639 OOPS!!! Unknown internal error occurs."; + } + + if (cmd instanceof OtherCommand && cmd.getCmdType().equalsIgnoreCase("bye")) { + return BYE; + } else if (cmd instanceof OtherCommand) { + return new DukeException("\u2639 OOPS!!! I'm sorry, but I don't know what that means :-(").getMessage(); + } + + try { + result = processCommand(cmd, tasks, ui); + } catch (Exception e) { + result = new DukeException("\u2639 OOPS!!! Unknown error occurs when process command.").getMessage(); + } + + try { + storage.save(tasks); + } catch (IOException e) { + return "\u2639 OOPS!!!Updating file is fail."; + } + + return result; + + } + } diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java new file mode 100644 index 00000000..1e3546aa --- /dev/null +++ b/src/main/java/Launcher.java @@ -0,0 +1,9 @@ +import javafx.application.Application; + +public class Launcher { + + public static void main(String[] args) { + Application.launch(Main.class, args); + } + +} diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 00000000..41b0e526 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,31 @@ +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke(); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke); + duke.loadFile(); + stage.setTitle("Duke_Zheng Shijie"); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/MainWindow.java b/src/main/java/MainWindow.java new file mode 100644 index 00000000..0e27b247 --- /dev/null +++ b/src/main/java/MainWindow.java @@ -0,0 +1,82 @@ +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; + +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + /** + * Initialize the required parameters by MainWindow + */ + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + introMessage(); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Prints the intro message. + */ + private void introMessage() { + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog("Welcome!", dukeImage) + ); + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + + if (response.equalsIgnoreCase(Duke.BYE)) { + Platform.exit(); + } + + userInput.clear(); + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void loadStoragedFile() { + String response = duke.loadFile(); + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java new file mode 100644 index 00000000..3f28d04c --- /dev/null +++ b/src/main/java/duke/Parser.java @@ -0,0 +1,103 @@ +package duke; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import duke.command.Command; +import duke.command.DeadlineCommand; +import duke.command.DeleteCommand; +import duke.command.DoneCommand; +import duke.command.EventCommand; +import duke.command.FindCommand; +import duke.command.ListCommand; +import duke.command.OtherCommand; +import duke.command.TagCommand; +import duke.command.TodoCommand; +import duke.exceptions.DukeException; + +public class Parser { + + private static String[] commandSplit(String s, String splitBy) { + return s.split(splitBy); + } + + public static Command getCommand(String userInput) throws DukeException { + String[] cmd = Parser.commandSplit(userInput, "\\s+"); + switch(cmd[0]) { + case "list": + return new ListCommand(); + case "done": + try { + return new DoneCommand(userInput.split(" ")[1]); + } catch (IndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The task does not exist."); + } catch (NumberFormatException e) { + throw new DukeException("\u2639 OOPS!!! Please specify the task need to be done."); + } + case "delete": + try { + return new DeleteCommand(userInput.split(" ")[1]); + } catch (IndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The task does not exist."); + } catch (NumberFormatException e) { + throw new DukeException("\u2639 OOPS!!! Please specify the number of the task need to be delete."); + } + case "todo": + try { + return new TodoCommand(userInput.substring(5)); + } catch (StringIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The description of a todo cannot be empty."); + } + case "deadline": + try { + String deadlineSpliter = "/by"; + String[] deadlineContent = userInput.split(deadlineSpliter); + LocalDate.parse(deadlineContent[1].trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd")); + return new DeadlineCommand( + deadlineContent[0].substring(9, deadlineContent[0].length() - 1), + deadlineContent[1].trim()); + } catch (StringIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The description of a deadline cannot be empty."); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The keyword /by is missing."); + } catch (java.time.format.DateTimeParseException e) { + throw new DukeException("\u2639 OOPS!!! The time format is " + + "illegal.Format should be in \"yyyy-MM-dd\" "); + } + case "event": + try { + String eventSpliter = "/at"; + String[] eventContent = userInput.split(eventSpliter); + LocalDate.parse(eventContent[1].trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd")); + return new EventCommand( + eventContent[0].substring(6, eventContent[0].length() - 1), + eventContent[1].trim()); + } catch (StringIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The description of a event cannot be empty."); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The keyword /at is missing."); + } catch (java.time.format.DateTimeParseException e) { + throw new DukeException("\u2639 OOPS!!! The time format is illegal.Format should be in \"yyyy-MM-dd\""); + } + case "find": + return new FindCommand(userInput.substring(5)); + case "tag": + //valid command format: tag 1 /t xxxxxxx + try { + String tagSpliter = "/t"; + String[] tagCommandContent = userInput.split(tagSpliter); + int index = Integer.parseInt(tagCommandContent[0].split(" ")[1]); + return new TagCommand(index, tagCommandContent[1]); + } catch (StringIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The description of the tag cannot be empty."); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DukeException("\u2639 OOPS!!! The keyword /t is missing."); + } catch (NumberFormatException e) { + throw new DukeException("\u2639 OOPS!!! Please specify the number of the task need to be tagged."); + } + default: + return new OtherCommand(cmd[0]); + } + } + +} diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java new file mode 100644 index 00000000..8244ff23 --- /dev/null +++ b/src/main/java/duke/Storage.java @@ -0,0 +1,153 @@ +package duke; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Iterator; +import java.util.Scanner; + +import duke.exceptions.DukeException; +import duke.tasks.Deadlines; +import duke.tasks.Events; +import duke.tasks.Tag; +import duke.tasks.Task; +import duke.tasks.ToDos; + +public class Storage { + static final String CURRENTWORKINGDIR = System.getProperty("user.dir"); + private String path; + private File f; + + /** + * @param path constructor for storage class + */ + public Storage(String path) { + this.path = CURRENTWORKINGDIR + path; + this.f = new File(this.path); + } + + /** + * @param tasks the current stored task list + * @throws IOException any exception related to IO + * @throws DukeException any defined exception + */ + public void load(TaskList tasks) throws IOException, DukeException { + if (f.exists()) { + Scanner s = new Scanner(f); + + while (s.hasNext()) { + String line = s.nextLine(); + String[] contents = line.split("\\|"); + + String taskDate = "unknown date"; + String taskType = contents[0]; + String[] tags = null; + boolean taskDoneStatus = checkDoneStatus(contents[1]); + String taskDescription = contents[2]; + if (contents.length == 4) { + if (contents[3].contains(Tag.getTagDiv())) { + tags = contents[3].split(Tag.getTagDiv()); + } else { + taskDate = contents[3]; + } + } else if (contents.length == 5) { + taskDate = contents[3]; + tags = contents[4].split(Tag.getTagDiv()); + } + + try { + switch (taskType) { + case "T": + Task t = createToDos(taskDescription, taskDoneStatus); + if (tags != null) { + addTags(t, tags); + } + tasks.add(t); + break; + case "D": + Task d = createDeadlines(taskDescription, taskDate, taskDoneStatus); + if (tags != null) { + addTags(d, tags); + } + tasks.add(d); + break; + case "E": + Task e = createEvents(taskDescription, taskDate, taskDoneStatus); + if (tags != null) { + addTags(e, tags); + } + tasks.add(e); + break; + default: + throw new DukeException("\u2639 OOPS!!!There's unknown tasks type in the file."); + } + } catch (Exception e) { + throw new DukeException("\u2639 OOPS!!!There is error in the file,please check the format."); + } + } + } else { + createDukeTxt(path); + } + } + + private void addTags(Task t, String[] tags) { + for (String tag : tags) { + t.addTag(tag); + } + } + + /** + * @param tasks the current task list + * @throws IOException any exception related to IO + */ + public void save(TaskList tasks) throws IOException { + FileWriter fw = new FileWriter(path); + + Iterator i = tasks.iterator(); + while (i.hasNext()) { + Task t = i.next(); + if (t.existTags()) { + fw.write(t.toSavingStringWithTag()); + } else { + fw.write(t.toSavingString()); + } + + fw.write("\n"); + } + + fw.close(); + } + + private static void createDukeTxt(String path) throws IOException { + String folderPath = CURRENTWORKINGDIR + "/data"; + File folder = new File(folderPath); + if (!folder.exists() && !folder.isDirectory()) { + folder.mkdirs(); + } + FileWriter fw = new FileWriter(path); + fw.close(); + } + + private static Events createEvents(String taskDescription, String taskDate, boolean taskDoneStatus) { + Events e = new Events(taskDescription, taskDate); + e.setDone(taskDoneStatus); + return e; + } + + private static Deadlines createDeadlines(String taskDescription, String taskDate, boolean taskDoneStatus) { + Deadlines d = new Deadlines(taskDescription, taskDate); + d.setDone(taskDoneStatus); + return d; + } + + private static ToDos createToDos(String taskDescription, boolean taskDoneStatus) { + ToDos t = new ToDos(taskDescription); + t.setDone(taskDoneStatus); + return t; + } + + private static boolean checkDoneStatus(String content) { + return content.equalsIgnoreCase("1"); + } + +} diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java new file mode 100644 index 00000000..19d74c45 --- /dev/null +++ b/src/main/java/duke/TaskList.java @@ -0,0 +1,53 @@ +package duke; + +import java.util.ArrayList; +import java.util.Iterator; + +import duke.tasks.Task; + +public class TaskList { + private ArrayList taskList; + + public TaskList() { + this.taskList = new ArrayList<>(); + } + + public Task get(int i) { + return taskList.get(i); + } + + public void remove(int i) { + taskList.remove(i); + } + + public void add(Task t) { + taskList.add(t); + } + + public Iterator iterator() { + return taskList.iterator(); + } + + public ArrayList getWholeList() { + return this.taskList; + } + + public int size() { + return taskList.size(); + } + + /** + * @param keyword the key input word + * @return + */ + public ArrayList findKeywordList(String keyword) { + ArrayList keywordTaskList = new ArrayList<>(); + for (Task t:this.taskList) { + if (t.getTaskDescription().contains(keyword)) { + keywordTaskList.add(t); + } + } + return keywordTaskList; + } + +} diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java new file mode 100644 index 00000000..44304a5a --- /dev/null +++ b/src/main/java/duke/Ui.java @@ -0,0 +1,22 @@ +package duke; + +import java.util.Scanner; + +import duke.tasks.Task; + +public class Ui { + private Scanner scanner; + + public Ui() { + this.scanner = new Scanner(System.in); + } + + /** + * @param t A normal task + * @param tasks the stored task list + */ + public String printTask(Task t, TaskList tasks) { + return "Got it. I've added this task:" + System.lineSeparator() + t.toString() + System.lineSeparator() + + "Now you have " + tasks.size() + " tasks in the list."; + } +} diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java new file mode 100644 index 00000000..fb972e2b --- /dev/null +++ b/src/main/java/duke/command/Command.java @@ -0,0 +1,14 @@ +package duke.command; + +public abstract class Command { + protected String type; + protected String content; + + public String getCmdContent() { + return content; + } + + public String getCmdType() { + return type; + } +} diff --git a/src/main/java/duke/command/DeadlineCommand.java b/src/main/java/duke/command/DeadlineCommand.java new file mode 100644 index 00000000..f5788113 --- /dev/null +++ b/src/main/java/duke/command/DeadlineCommand.java @@ -0,0 +1,19 @@ +package duke.command; + +public class DeadlineCommand extends Command { + private String time; + + /** + * @param content deadline content + * @param time the deadline time + */ + public DeadlineCommand(String content, String time) { + this.time = time; + this.type = "deadline"; + this.content = content; + } + + public String getTime() { + return time; + } +} diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java new file mode 100644 index 00000000..1ee44467 --- /dev/null +++ b/src/main/java/duke/command/DeleteCommand.java @@ -0,0 +1,18 @@ +package duke.command; + +public class DeleteCommand extends Command { + private int index; + + /** + * @param i the index + */ + public DeleteCommand(String i) { + type = "delete"; + content = ""; + index = Integer.parseInt(i); + } + + public int getIndex() { + return index; + } +} diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java new file mode 100644 index 00000000..1ea48ac6 --- /dev/null +++ b/src/main/java/duke/command/DoneCommand.java @@ -0,0 +1,19 @@ +package duke.command; + +public class DoneCommand extends Command { + private int index; + + /** + * @param i the index + */ + public DoneCommand(String i) { + type = "done"; + content = ""; + index = Integer.parseInt(i); + } + + public int getIndex() { + return index; + } + +} diff --git a/src/main/java/duke/command/EventCommand.java b/src/main/java/duke/command/EventCommand.java new file mode 100644 index 00000000..4a3c6c8d --- /dev/null +++ b/src/main/java/duke/command/EventCommand.java @@ -0,0 +1,19 @@ +package duke.command; + +public class EventCommand extends Command { + private String time; + + /** + * @param content the content of the task + * @param time the event time of task + */ + public EventCommand(String content, String time) { + this.time = time; + this.type = "event"; + this.content = content; + } + + public String getTime() { + return time; + } +} diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java new file mode 100644 index 00000000..953c89c9 --- /dev/null +++ b/src/main/java/duke/command/FindCommand.java @@ -0,0 +1,12 @@ +package duke.command; + +public class FindCommand extends Command { + + /** + * @param keyword the command keyword + */ + public FindCommand(String keyword) { + type = "find"; + this.content = keyword; + } +} diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java new file mode 100644 index 00000000..22a17141 --- /dev/null +++ b/src/main/java/duke/command/ListCommand.java @@ -0,0 +1,12 @@ +package duke.command; + +public class ListCommand extends Command { + /** + * the constructor of list command + */ + public ListCommand() { + type = "list"; + content = ""; + } + +} diff --git a/src/main/java/duke/command/OtherCommand.java b/src/main/java/duke/command/OtherCommand.java new file mode 100644 index 00000000..ca36ba77 --- /dev/null +++ b/src/main/java/duke/command/OtherCommand.java @@ -0,0 +1,7 @@ +package duke.command; + +public class OtherCommand extends Command { + public OtherCommand(String type) { + this.type = type; + } +} diff --git a/src/main/java/duke/command/TagCommand.java b/src/main/java/duke/command/TagCommand.java new file mode 100644 index 00000000..69d4fbc5 --- /dev/null +++ b/src/main/java/duke/command/TagCommand.java @@ -0,0 +1,22 @@ +package duke.command; + +public class TagCommand extends Command { + private int index; + + /** + * @param s The tag content + */ + public TagCommand(int i, String s) { + this.type = "tag"; + this.content = s; + this.index = i; + } + + /** + * @return the index which needs to be tagged + */ + public int getIndex() { + return index; + } + +} diff --git a/src/main/java/duke/command/TodoCommand.java b/src/main/java/duke/command/TodoCommand.java new file mode 100644 index 00000000..b668c2e3 --- /dev/null +++ b/src/main/java/duke/command/TodoCommand.java @@ -0,0 +1,12 @@ +package duke.command; + +public class TodoCommand extends Command { + /** + * @param content the content to create an todo command + */ + public TodoCommand(String content) { + type = "todo"; + this.content = content; + } + +} diff --git a/src/main/java/duke/exceptions/DukeException.java b/src/main/java/duke/exceptions/DukeException.java new file mode 100644 index 00000000..fb1d8bf1 --- /dev/null +++ b/src/main/java/duke/exceptions/DukeException.java @@ -0,0 +1,10 @@ +package duke.exceptions; + +public class DukeException extends Exception { + /** + * @param message create a customized exception with certain message + */ + public DukeException(String message) { + super(message); + } +} diff --git a/src/main/java/duke/tasks/Deadlines.java b/src/main/java/duke/tasks/Deadlines.java new file mode 100644 index 00000000..23443ed6 --- /dev/null +++ b/src/main/java/duke/tasks/Deadlines.java @@ -0,0 +1,33 @@ +package duke.tasks; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public class Deadlines extends Task { + private LocalDate deadline; + + /** + * @param taskDescription description of the deadline task + * @param deadline deadline time of the deadline task + * create a deadline task + */ + public Deadlines(String taskDescription, String deadline) { + super(taskDescription); + super.taskType = "[D]"; + this.deadline = LocalDate.parse(deadline, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + + @Override + public String toString() { + if (existTags()) { + return super.toString() + " (by: " + deadline.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")" + + System.lineSeparator() + printTags(); + } + return super.toString() + " (by: " + deadline.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")"; + } + + @Override + public String toSavingString() { + return super.toSavingString() + "|" + deadline.format(DateTimeFormatter.ofPattern("MMM dd yyyy")); + } +} diff --git a/src/main/java/duke/tasks/Events.java b/src/main/java/duke/tasks/Events.java new file mode 100644 index 00000000..75372144 --- /dev/null +++ b/src/main/java/duke/tasks/Events.java @@ -0,0 +1,33 @@ +package duke.tasks; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public class Events extends Task { + private LocalDate eventTime; + + /** + * @param taskDescription description of the event + * @param eventTime the time of the event + * To create a event task + */ + public Events(String taskDescription, String eventTime) { + super(taskDescription); + super.taskType = "[E]"; + this.eventTime = LocalDate.parse(eventTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + + @Override + public String toString() { + if (existTags()) { + return super.toString() + " (at: " + eventTime.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")" + + System.lineSeparator() + printTags(); + } + return super.toString() + " (at: " + eventTime.format(DateTimeFormatter.ofPattern("MMM dd yyyy")) + ")"; + } + + @Override + public String toSavingString() { + return super.toSavingString() + "|" + eventTime.format(DateTimeFormatter.ofPattern("MMM dd yyyy")); + } +} diff --git a/src/main/java/duke/tasks/Tag.java b/src/main/java/duke/tasks/Tag.java new file mode 100644 index 00000000..db516954 --- /dev/null +++ b/src/main/java/duke/tasks/Tag.java @@ -0,0 +1,88 @@ +package duke.tasks; + +import java.util.ArrayList; + +public abstract class Tag { + + private static String tagDiv = "/div/"; + + protected ArrayList tags = new ArrayList<>(); + + public ArrayList getTags() { + return tags; + } + + /** + * @return return the tag divider + */ + public static String getTagDiv() { + return tagDiv; + } + + /** + * @return return true if tag exists + */ + public boolean existTags() { + if (tags.isEmpty()) { + return false; + } + return true; + } + + /** + * @param s the tag + */ + public void addTag(String s) { + if (s.charAt(0) != '#') { + tags.add('#' + s ); + } else { + tags.add(s); + } + } + + /** + * @param s the tag + * @return true if remove successfully, else false. + */ + public boolean removeTag(String s) { + if (tags.contains(s)) { + tags.remove(tags.indexOf(s)); + return true; + } + return false; + } + + /** + * @param i the index of the tag + * @return true if successfully, else false + */ + public boolean removeTag(int i) { + if (i >= 0 && i < this.tags.size()) { + tags.remove(i); + return true; + } + return false; + } + + /** + * @return Output all the tags as a single string + */ + public String printTags() { + StringBuffer s = new StringBuffer(); + for (String tag : this.tags) { + s.append(tag + System.lineSeparator()); + } + return s.toString(); + } + + /** + * @return return a string that contains all tags info and could be stored in file + */ + public String toTagsString() { + StringBuffer s = new StringBuffer(); + for (String tag : this.tags) { + s.append(tag + tagDiv); + } + return s.toString(); + } +} diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java new file mode 100644 index 00000000..13f9467a --- /dev/null +++ b/src/main/java/duke/tasks/Task.java @@ -0,0 +1,59 @@ +package duke.tasks; + +public abstract class Task extends Tag { + protected String taskType; + protected boolean isDone = false; + protected String taskDescription; + protected String taskSymbol = "[" + "\u2718" + "]"; + + /** + * @param taskDescription Description on the task detail. + */ + public Task(String taskDescription) { + this.taskDescription = taskDescription; + } + + /** + * @return convert the task to a readable string + */ + @Override + public String toString() { + return taskType + taskSymbol + " " + taskDescription; + } + + /** + * @param done set the task done status + */ + public void setDone(boolean done) { + this.isDone = done; + setSymbol(); + } + + private void setSymbol() { + if (isDone) { + this.taskSymbol = "[" + "\u2714" + "]"; + } else { + this.taskSymbol = "[" + "\u2718" + "]"; + } + } + + /** + * @return a saving method used to save string + */ + public String toSavingString() { + String doneStatus = isDone ? "1" : "0"; + return taskType.charAt(1) + "|" + doneStatus + "|" + taskDescription; + } + + /** + * @return a saving method used to save string + */ + public String toSavingStringWithTag() { + String doneStatus = isDone ? "1" : "0"; + return taskType.charAt(1) + "|" + doneStatus + "|" + taskDescription + "|" + this.toTagsString(); + } + + public String getTaskDescription() { + return this.taskDescription; + } +} diff --git a/src/main/java/duke/tasks/ToDos.java b/src/main/java/duke/tasks/ToDos.java new file mode 100644 index 00000000..f2e97232 --- /dev/null +++ b/src/main/java/duke/tasks/ToDos.java @@ -0,0 +1,20 @@ +package duke.tasks; + +public class ToDos extends Task { + + /** + * @param taskDescription create a ToDos task with description of the task + */ + public ToDos(String taskDescription) { + super(taskDescription); + super.taskType = "[T]"; + } + + @Override + public String toString() { + if (existTags()) { + return super.toString() + System.lineSeparator() + printTags(); + } + return super.toString(); + } +} diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 00000000..304e90f1 Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png new file mode 100644 index 00000000..d0f303ca Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/images/background.jpg b/src/main/resources/images/background.jpg new file mode 100644 index 00000000..b8deb31e Binary files /dev/null and b/src/main/resources/images/background.jpg differ diff --git a/src/main/resources/images/background.png b/src/main/resources/images/background.png new file mode 100644 index 00000000..12842a95 Binary files /dev/null and b/src/main/resources/images/background.png differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 00000000..98baa7d5 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 00000000..66436789 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +