diff --git a/README.md b/README.md index 9d95025bce..da61b7869f 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/build.gradle b/build.gradle new file mode 100644 index 0000000000..d8c36eb05d --- /dev/null +++ b/build.gradle @@ -0,0 +1,61 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + 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' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "duke/Launcher" +} + +shadowJar { + archiveBaseName = "ip" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +run{ + standardInput = System.in +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..7951f3a6c4 --- /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 0000000000..6423654db1 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/docs/README.md b/docs/README.md index fd44069597..8b86c1bacc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,19 +2,191 @@ ## Features -### Feature 1 -Description of feature. +### Tasks +3 Different Task types exists, ToDo, Event and Deadline -## Usage +* ### Deadline + Use it to add a task involving a deadline(eg submission) + + ## User uses it to add a particular deadline + + Adds a task involving a deadline to the current list of tasks + Gives a response saying that it has been added, and gives the current + Number of tasks present + * Syntax: deadline (task name) /by (date and/or time) + + Examples of usage: + + deadline return book /by 23:59 + + Got it. I've added this task: + [D][✗] return book (by: 23:59) + Now you have 6 tasks in the list. + +* ### Event + Use it to add a task involving a period o time, + + ## User uses it to add a particular deadline(eg competition) + + Adds a task that occurs over a period of time, where date and/or time has to be given in the format as shown below + + Gives a response saying that it has been added and gives the current + Number of tasks present. + * Syntax: event (event name) /at (start date and/or time)-(end date and/or time) + + Examples of usage: + + event competition/at 12:00-14:00 + + Got it. I've added this task: + [D][✗] return book (at: 23:59) + Now you have 6 tasks in the list. +* ###ToDo + This is to add a normal task without a need to be completed by a certain time frame + + ## User uses it to keep reminder of tasks without a specific time frame + + After this is added, a message saying that the todo task is added and the current number + Of tasks is printed + + * Syntax: todo (todo name) + Example of Usage: + todo read book + + Got it. I've added this task: + [T][✗] read book + Now you have 3 tasks in the list. -### `Keyword` - Describe action -Describe action and its outcome. +### Delete +Use it to delete a specific task -Example of usage: +## User uses it to to delete a task -`keyword (optional arguments)` +### ‘delete’ - deletes specific task based on the number given -Expected outcome: +Deletes a task provided based on the number provided after delete, where the +Number provided should coincide with the ordering given in the list +If delete is absent or number given is more than the number of items available +Error is printed +* Syntax: delete (number) -`outcome` +Examples of usage: + +delete 3 (if at least 3 tasks in the list) + + Noted. I've removed this task: + [E][✗] project meeting (at: Aug 6th 2-4pm) + Now you have 4 tasks in the list. + + +### Find +Used to find tasks containing the words given by user +Allows user to search for tasks with specific words + +##User uses it to search for tasks + +Finds out tasks using their names and the tasks containing all the words given are given +* Syntax: find (words you wish to search for) + +Examples of usage: + +find book + + Here are the matching tasks in your list: + 1.[T][✓] buy book + + ### Done +Use it to mark a task as done, once a task is completed + +##User uses it to mark a task as done, to keep track of tasks done + +Marks the task at the number(input by user) as done and returns a string representing it +* Syntax: done (ID as number) + +Examples: + +done 2 + + Nice! I've marked this task as done: + [E][✓] concert (at: 12:00-14:00) + + +### List +Lists out all the current tasks that the user has + +##User can use it to check and keep track of + current tasks she has, and how many she has completed and how many she has uncompleted + +The tasks are given in order in which it has been added and tick represents completed tasks +Cross represents undone tasks + +* Syntax: list + +Examples: + + 1. [T][✓] buy book + 2. [E][✓] concert (at: 12:00-14:00) + 3. [T][✗] read book + 4. [T][✗] read book + +### Exit +Used to exit app + +##After this one more input would lead to the app to close immediately + +After this, a bye message would be printed, then another input causes the app to close immediately. + +* Syntax: bye + +Examples of usage: +Bye + + Bye. Hope to see you again soon! + +###ShortCuts +This is to make app more user friendly so that users do not have to type much + +## User can use existing short forms or define own short forms + +ShortForms-Original + * b - Bye + * t - todo + * e - event + * s - short + * l -list + * de - deadline + * do - done + * d - delete + * f - find +You can also define your own short forms using syntax shown below: +short (original) (short form) +* Syntax: (short form) (same format as original excluding key word) + +Example: +b + + Bye. Hope to see you again soon! + +You can also define your own short cuts + +##You can use a short cuts, that is user defined + +Syntax: short (original form) (short form) + +Example: +by + + short cut successfully added + +Example of using user defined short cut: +by + + Bye. Hope to see you again soon! + +### Random Command +If you insert none of the above commands a random command would be given + +Example: blah + + '☹ OOPS!!! I'm sorry, but I don't know what that means :-( \ No newline at end of file diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..b664a62da3 Binary files /dev/null and b/docs/Ui.png differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f3d88b1c2f 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 0000000000..26bfbb87eb --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Sep 09 11:19:39 SGT 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-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 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## 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" "-Xms64m"' + +# 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 or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..62bd9b9cce --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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 Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@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" "-Xms64m" + +@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/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334cc..0000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..2c9a9745c5 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.Duke + diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java new file mode 100644 index 0000000000..f06d658116 --- /dev/null +++ b/src/main/java/duke/DialogBox.java @@ -0,0 +1,107 @@ +package duke; + +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.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.Border; +import javafx.scene.layout.BorderStroke; +import javafx.scene.layout.BorderStrokeStyle; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.paint.ImagePattern; +import javafx.scene.shape.Circle; +import javafx.scene.text.Font; +import javafx.scene.text.FontPosture; +import javafx.scene.text.FontWeight; + + + +/** + * 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; //displays String given by user or Duke + @FXML + private Circle circleDisplayPicture; //display picture of Duke or user + + /** + * Constructor for DialogBox used to initialize DialogBox object + * + * @param text is input placed in dialog + * @param img used in the image for circleDisplayPicture + */ + 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); + dialog.setBackground(new Background(new BackgroundFill(Color.THISTLE, new CornerRadii(5), null))); + circleDisplayPicture.setRadius(50); + circleDisplayPicture.setFill(new ImagePattern(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); + } + + /** + * Gives the DialogBox of the user + * + * @param text input by the user + * @param img user image + * @return DialogBox of user + */ + public static DialogBox getUserDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.setBackground(new Background(new BackgroundFill(Color.MISTYROSE, new CornerRadii(20), null))); + //sets background to MISTYROSE + db.setBorder(new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.DOTTED, + new CornerRadii(20), null))); //sets dotted border + return db; + } + + /** + * Gives the DialogBox of Duke + * + * @param response input by duke in response to the input given by user + * @param img Duke image + * @return DialogBox of Duke + */ + public static DialogBox getDukeDialog(Response response, Image img) { + String text = response.getOutput(); + var db = new DialogBox(text, img); + db.dialog.setFont(Font.font("Times new Roman")); + db.setBackground(new Background(new BackgroundFill(Color.SALMON, new CornerRadii(20), null))); + //sets background to salmon + db.flip(); //to have a Duke appear on different side + if (response.getIsException()) { + db.dialog.setFont(Font.font("Times new Roman", FontWeight.BOLD, FontPosture.ITALIC, 12)); + //when the response given is an exception, it is emphasized with BOLD and italic + } else { } + return db; + } +} diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 0000000000..da78ebf3fd --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,152 @@ +package duke; + +import java.io.IOException; + +import duke.commands.Command; +import duke.errors.DukeException; +import duke.helpers.Parser; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + + +/** + * This Duke class is the main class that prints out the relevant outputs based on input given by user + */ +public class Duke { + private boolean isExit = false; + //checks whether the Duke has been given the user command, true if exit command is given and false otherwise + private Storage storage; //deals with loading tasks from the file, saving tasks and shortcuts in the file + private TaskList tasks; //contains the task list e.g., it has operations to add/delete tasks in the list + private Ui ui = new Ui(); //deals with interactions with the user + + /** + * A constructor used to initialise Duke. + */ + public Duke(){} + + /** + * Assigns the above member variables with the appropriate values, and throws certain exceptions if file in + * the filePath mentioned is empty or absent + * + * @param filePath represents where the filepath of where the file may exist. + * @param shortFormPath contains all the pre user defined short forms + */ + public Duke(String filePath, String shortFormPath) { + ui = new Ui(); + storage = new Storage(filePath, shortFormPath); + try { + tasks = new TaskList(storage.load()); + storage.setShortForm(); + } catch (DukeException e) { + ui.setDukeException(e); + ui.showLoadingError(); + tasks = new TaskList(); + } + } + + /** + * Assigns the above member variables with the appropriate values, and throws certain exceptions if file in + * the filePath mentioned is empty or absent + * + * @param filePath represents where the filepath of where the file may exist. + * @param input filepath for the inputs + * @param shortFormPath filepath for file containing short cuts + */ + public Duke(String filePath, String input, String shortFormPath) { + storage = new Storage(filePath, shortFormPath); + try { + ui = new Ui(input); + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.setDukeException(e); + ui.showLoadingError(); + tasks = new TaskList(); + } + } + /** + * This gives the boolean value of isExit + * + * @return returns the value of isExit. + */ + public boolean isExit() { + return isExit; + } + + /** + * This gives the respective output based on the input + * + * @param inputs what the user inputs in the GUI, can be multiple inputs. + * @return the output based on the output in the form of an array, where + */ + public Response[] getResponse(String... inputs) { + Response[] responses = new Response[inputs.length]; + for (int i = 0; i < inputs.length; i++) { + String input = inputs[i]; + Response response = getResponse(input); //receives input depending on input depending on + responses[i] = response; + } + return responses; + } + + /** + * Returns a Response depending on the input given by the user + * + * @param input given by user + * @return Response with a boolean value true if the user input is wrong, leading to an exception + */ + private Response getResponse(String input) { + Command c = Parser.parse(input); //respective command depending on input + Response response; + String output; + try { + isExit = c.isExit(); + output = c.execute(tasks, new Ui(), storage); //concatenates output message + response = new Response(output, false); //since user input is in correct + } catch (DukeException e) { + output = e.getMessage(); //concatenates error message + response = new Response(output, true); //since user input is wrong + } + return response; + } + /** + * gives main logic of the App, + * where exceptions are caught and printed and if bye is there code stops. also starts with hello + */ + public void run() { + ui.showWelcome(); + ui.showLine(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); // show the divider line ("_______") + Command c = Parser.parse(fullCommand); + System.out.println(c.execute(tasks, ui, storage)); + isExit = c.isExit(); //if true exits program as bye is mentioned + } catch (DukeException e) { + ui.showLoadingError(); + } finally { + ui.showLine(); + } + } + } + /** + * + * @param args of type String[] + * reads input using scan() and adds it to todos. + * Then, prints out relevant information using the output() func. + */ + public static void main(String[] args) throws IOException { + Duke duke = new Duke("src/main/java/tasks.txt", "src/main/java/shortCuts.txt"); + duke.run(); + //String s = duke.getResponse("ToDo read book"); + /*PrintStream fileOut = new PrintStream("src/main/java/output.txt"); + System.setOut(fileOut); + FileWriter fw = new FileWriter("src/main/java/tasks.txt"); + fw.write(""); + fw.close(); + Duke duke = new Duke("src/main/java/tasks.txt", "src/main/java/input.txt", "src/main/java/shortCuts.txt"); + duke.run();*/ + } +} diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..fd3e841ece --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,18 @@ +package duke; + +import javafx.application.Application; + +/** + * Class that launches GUI for Duke + */ +public class Launcher { + /** + * Launches the GUI + * + * @param args + */ + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} + diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..c3a592253f --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,43 @@ +package duke; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.paint.Color; +import javafx.stage.Stage; + +/** + * A GUI for Duke. + */ +public class Main extends Application { + private Duke duke = new Duke("tasks.txt", "shortCuts.txt"); + + /** + * Implements the abstract start method in Application class + * + * @param stage it is to show the main screen + */ + @Override + public void start(Stage stage) { + if (duke.isExit()) { + stage.close(); + } + try { + stage.setTitle("Duke"); + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + MainWindow ap = fxmlLoader.load(); + ap.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null))); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke, stage); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/duke/MainWindow.java b/src/main/java/duke/MainWindow.java new file mode 100644 index 0000000000..9b8a5ed57e --- /dev/null +++ b/src/main/java/duke/MainWindow.java @@ -0,0 +1,75 @@ +package duke; + +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.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +/** + * Controller for Duke.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; + @FXML + private TextField display; + 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")); + private Stage stage; + + /** + * Initializes scrollPane + */ + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + /** + * Also ensures that the getDukeDialog prints hello when opened + * + * @param d assigns duke value of d + * @param stage assigns stage value of stage + */ + public void setDuke(Duke d, Stage stage) { + duke = d; + this.stage = stage; + Response hello = new Response(" Hello! I'm Duke\n" + " What can I do for you?", false); + //opening message and isException is false as it is not an error + dialogContainer.getChildren().add(DialogBox.getDukeDialog(hello, dukeImage)); //Introduction given by Duke + } + + /** + * 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() { + if (duke.isExit()) { + stage.close(); //since bye message is given + } + String input = userInput.getText(); //input can be multiple input separated by ',' sign + String[] inputs = input.split(", "); + Response[] responses = duke.getResponse(inputs); //response given by duke + for (int i = 0; i < inputs.length; i++) { + DialogBox user = DialogBox.getUserDialog(inputs[i], userImage); //DialogBox for user + DialogBox dukeResponse = DialogBox.getDukeDialog(responses[i], dukeImage); //DialogBox from Duke + dialogContainer.getChildren().addAll(user, dukeResponse); //Duke input displayed + } + userInput.clear(); + HBox buttonsContainer = new HBox(); + buttonsContainer.getStyleClass().add("jfx-decorator-buttons-container"); + } +} diff --git a/src/main/java/duke/Response.java b/src/main/java/duke/Response.java new file mode 100644 index 0000000000..bffa3102f3 --- /dev/null +++ b/src/main/java/duke/Response.java @@ -0,0 +1,38 @@ +package duke; + +/** + * Returns the Response to the input given by user + */ +public class Response { + private String output; //string output given by Duke + private boolean isException; //tests whether the output given is an exception + + /** + * Constructor that assigns respective values to + * + * @param response + * @param isException + */ + public Response(String response, boolean isException) { + this.output = response; + this.isException = isException; + } + + /** + * Returns String value of response + * + * @return value of output + */ + public String getOutput() { + return output; + } + + /** + * Returns value of isException + * + * @return boolean value of isException, which is true if Duke gives exception and false otherwise + */ + public boolean getIsException() { + return isException; + } +} diff --git a/src/main/java/duke/commands/AddCommand.java b/src/main/java/duke/commands/AddCommand.java new file mode 100644 index 0000000000..bdecb560e1 --- /dev/null +++ b/src/main/java/duke/commands/AddCommand.java @@ -0,0 +1,58 @@ +package duke.commands; + +import java.io.FileWriter; +import java.io.IOException; + +import duke.errors.DukeException; +import duke.errors.FileAbsentException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.tasks.Task; + +/** + * This class handles the case of adding different tasks which are ToDo, deadline and Event + */ +public abstract class AddCommand extends Command { + /** + * constructor that assigns string value of string + * + * @param input passes it to super class constructor + */ + public AddCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Gives a String saying that the task list has been updated + * + * @param task to be added into taskList + * @param taskList where task is added + * @return String that informs task is added into taskList + */ + protected static String stringToUpdateTaskList(Task task, TaskList taskList) { + return " Got it. I've added this task:\n " + task.toString() + "\n" + //Task added message + " Now you have " + taskList.getAllTasks().size() + " tasks in the list."; + } + + /** + * adds the task to list of task in taskList and into the file in storage + * + * @param storage where the file here is updated + * @param task this task is added into storage and taskList + * @param taskList where the tasks here is updated with task added + * @throws DukeException when the file in storage is not present + */ + protected static String updateTaskList(Storage storage, Task task, TaskList taskList) throws DukeException { + try { + FileWriter fw = new FileWriter(storage.getFilePath(), true); + //updates the file in storage as new task is added + taskList.getAllTasks().add(task); + fw.write(task.inputListFormat() + "\n"); + fw.close(); + } catch (IOException i) { + throw new FileAbsentException(storage.getFilePath()); + } + return stringToUpdateTaskList(task, taskList); + } + +} diff --git a/src/main/java/duke/commands/Command.java b/src/main/java/duke/commands/Command.java new file mode 100644 index 0000000000..7a132248ad --- /dev/null +++ b/src/main/java/duke/commands/Command.java @@ -0,0 +1,59 @@ +package duke.commands; + +import duke.errors.DukeException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + +/** + * this is an abstract class used for polymorphism, parent class of all Command classes + */ +public abstract class Command { + /** + * commandName which contains information on task and details to perform task + * isExit is used to tell whether program terminates, where id true, it terminates + */ + protected String userInput; //the String input given by user + protected int lengthOfKeyword; //length of keyword eg for find is 4 + private boolean isExit = false; + + /** + * Assigns string to a value + * + * @param userInput assigns this.string to string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public Command(String userInput, int lengthOfKeyword) { + this.userInput = userInput; + this.lengthOfKeyword = lengthOfKeyword; + } + + /** + * gets value of exit + * @return exit + */ + public boolean isExit() { + return this.isExit; + } + + /** + * Executes the necessary task + * + * @param tasks used to access tasks in its list and change if necessary + * @param ui used to assign ui's dukeException if there is an error in user input + * @param storage to change the input there if necessary + * @throws DukeException if there are exceptions present + */ + public abstract String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; + + /** + * Returns whether the number/description is present. + * + * @return true is the number/description is absent and false if number is present. + */ + protected boolean isNumberOrDescriptionAbsent() { + return userInput.length() == lengthOfKeyword || userInput.length() == lengthOfKeyword + 1; + //since the delete number appears after length of keyword/+1 + } + +} diff --git a/src/main/java/duke/commands/DeadlineCommand.java b/src/main/java/duke/commands/DeadlineCommand.java new file mode 100644 index 0000000000..7327b5b75e --- /dev/null +++ b/src/main/java/duke/commands/DeadlineCommand.java @@ -0,0 +1,55 @@ +package duke.commands; + +import duke.errors.DeadlineException; +import duke.errors.DukeException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; +import duke.tasks.Deadline; + +/** + * Handles when input is deadline + */ +public class DeadlineCommand extends AddCommand { + /** + * assigns string to a value of string and initialize Deadline Command + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public DeadlineCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * to add deadline into a task list in TaskList + * + * @param tasks to change the taskList if necessary + * @param ui to store the DukeException that may be thrown if there is an error in user input + * @param storage to change the file in the if necessary + * @return String returns the string of the output that informs the action is successful + * @throws DukeException whenever there is an error, where the time adn or date is absent or in wrong format, no + * description + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + exceptionThrownIfNumberOrDescriptionAbsent(); + return Deadline.addDeadlineTask(tasks, ui, storage, userInput); + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Test whether description is absent and exception is thrown if absent + * + * @throws DukeException thrown if description for event is absent + */ + protected void exceptionThrownIfNumberOrDescriptionAbsent() throws DukeException { + if (isNumberOrDescriptionAbsent()) { + throw new DeadlineException(true, false, false); //Since description is absent + } + } + +} diff --git a/src/main/java/duke/commands/DeleteCommand.java b/src/main/java/duke/commands/DeleteCommand.java new file mode 100644 index 0000000000..eb245d977f --- /dev/null +++ b/src/main/java/duke/commands/DeleteCommand.java @@ -0,0 +1,153 @@ +package duke.commands; + +import java.io.FileWriter; +import java.io.IOException; + +import duke.errors.DeleteException; +import duke.errors.DukeException; +import duke.errors.FileAbsentException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + +/** + * Handles the case when the keyword is delete + */ +public class DeleteCommand extends Command { + /** + * assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public DeleteCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Deletes task and handles error + * + * @param tasks to change the taskList since item is deleted + * @param ui to set its dukeException variable is exception is thrown + * @param storage to change the file since item is deleted + * @return String returns the string of the output that informs the delete action has been complete. + * @throws DukeException thrown if the ID is more than number of ID is absent + */ + @Override + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + return process(tasks, storage); + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Returns a String if the input is given in the correct order, else Exception is thrown + * + * @param tasks is used to check whether the tasks to be found is present in TaskList, to delete the task + * @param storage is used to update the file in storage that contains current tasks, to remove it from the file + * @return String if the user input is correct + * @throws DukeException if the user input is wrong + */ + private String process(TaskList tasks, Storage storage) throws DukeException { + if (isNumberOrDescriptionAbsent()) { + throw new DeleteException(true, false); //when number is absent + } else { + int id = Integer.parseInt(userInput.substring(lengthOfKeyword + 1)); + if (isNumberNotInList(id, tasks)) { + throw new DeleteException(false, true); //when ID is more than number of tasks in list + } else { + return rewrite(storage, tasks, id); //to update TaskList and file in Storage + } + } + } + /** + * Returns whether the task is present in the list. + * + * @param iD of task to be removed + * @param tasks from which the task is to be removed. + * @return true if the task is not present in list. + */ + private boolean isNumberNotInList(int iD, TaskList tasks) { + return iD > tasks.getAllTasks().size(); //ID cannot be more than length of tasks + } + + /** + * Returns the String informing that the task is deleted + * + * @param tasks uses to give the current number of tasks + * @param iD uses to get the task to be deleted. + * @return String informing that the task is deleted. + */ + private String deleteTaskString(TaskList tasks, int iD) { + return " Noted. I've removed this task:\n" + // gives delete message + " " + tasks.getAllTasks().get(iD - 1).toString() + "\n" + + " Now you have " + (tasks.getAllTasks().size() - 1) + " tasks in the list."; + } + + /** + * updates the the file in storage after task is deleted. + * + * @param newList where this is the new input replaces the old input in the file + * @param storage which contains file to be changed + * @throws FileAbsentException when the file to be updated is absent in Storage + */ + private void updateTaskInStorage(String newList, Storage storage) throws FileAbsentException { + try { + FileWriter fw = new FileWriter(storage.getFilePath()); //updates the file in Storage with new String + fw.write(newList); + fw.close(); + } catch (IOException i) { + throw new FileAbsentException(storage.getFilePath()); + } + } + + /** + * gives the string for the new task list + * + * @param tasks contains current tasks + * @return the string for the new task list + */ + private String newInputInStorageFIle(TaskList tasks) { + String s = ""; + for (int i = 0; i < tasks.getAllTasks().size(); i++) { + s = s + tasks.getAllTasks().get(i).inputListFormat() + "\n"; + // Task of ID is deleted and then the String of tasks is updated + } + return s; + } + + /** + * Removes task that has to be deleted from TaskList + * + * @param tasks where task with index (ID - 1) is removed + * @param iD gives information on which task to remove. + */ + private void deleteTaskInTaskList(TaskList tasks, int iD) { + tasks.getAllTasks().remove(iD - 1); //removes task with ID from task + } + /** + * This returns the string that the task has been deleted and also updated the TakList. + * + * @param storage in which the file is updated. + * @param tasks used to update the task for the task to be deleted. + * @param iD of the task to be deleted. + * @return String saying that task has been deleted. + * @throws DukeException throws if file is absent + */ + private String rewrite(Storage storage, TaskList tasks, int iD) throws DukeException { + try { + String message = deleteTaskString(tasks, iD); + deleteTaskInTaskList(tasks, iD); //deleted the task with ID in TaskList + String newTaskList = newInputInStorageFIle(tasks); //gives new file input and deletes + updateTaskInStorage(newTaskList, storage); //replaces old list in storage file with this + return message; + } catch (DukeException dukeException) { + throw new FileAbsentException(storage.getFilePath()); + } + } + +} + diff --git a/src/main/java/duke/commands/DoneCommand.java b/src/main/java/duke/commands/DoneCommand.java new file mode 100644 index 0000000000..d69c6a2733 --- /dev/null +++ b/src/main/java/duke/commands/DoneCommand.java @@ -0,0 +1,153 @@ +package duke.commands; + +import java.io.FileWriter; +import java.io.IOException; + +import duke.errors.DoneException; +import duke.errors.DukeException; +import duke.errors.FileAbsentException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; +import duke.tasks.Task; + +/** + * Handles case when done is keyword + */ +public class DoneCommand extends Command { + /** + * assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public DoneCommand (String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Completes done task and handle error + * + * @param tasks to change the taskList as a task is completed + * @param ui to store the DukeException that may be thrown if there is an error in user input + * @param storage to change the file as task is completed + * @return String returns the string of the output that informs the done action has been complete. + * @throws DukeException thrown if the ID is more than number of ID is absent + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + return process(tasks, storage); + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Returns a String if the input is given in the correct order, else Exception is thrown + * + * @param tasks is used to check whether the tasks to be found is present in TaskList, to mark as done + * @param storage is used to update the file in storage that contains current tasks, to mark that task as done + * @return String if the user input is correct + * @throws DukeException if the user input is wrong + */ + private String process(TaskList tasks, Storage storage) throws DukeException { + if (isNumberOrDescriptionAbsent()) { + throw new DoneException(true, false, false); //when number is absent + } else { + int iD = Integer.parseInt(userInput.substring(lengthOfKeyword + 1)); + if (isNumberNotInList(iD, tasks)) { + throw new DoneException(false, true, false); //when number is not in list + } else if (isDone(iD, tasks)) { + throw new DoneException(false, false, true); + } else { + return rewrite(storage, tasks, iD); //where the file in Storage is updated and TaskList is updated + } + } + } + /** + * Returns whether the task is present in the list. + * + * @param iD of task to be removed + * @param tasks from which the task is to be removed. + * @return true if the task is not present in list. + */ + private boolean isNumberNotInList(int iD, TaskList tasks) { + return iD > tasks.getAllTasks().size(); //since ID cannot be more that number of tasks present + } + + /** + * Gives whether the task at a particular input has been completed + * + * @param iD of the task that user wants to mark as complete + * @param tasks where you want + * @return true if task is completed and false otherwise + */ + private boolean isDone(int iD, TaskList tasks) { + Task task = tasks.getAllTasks().get(iD - 1); + return task.getIsDone(); + } + /** + * updates the the file in storage after task is marked as done. + * + * @param newList where this is the new input replaces the old input in the file. + * @param storage which contains file to be changed. + * @throws FileAbsentException when the file to be updated is absent in Storage. + */ + private void updateTaskInStorage(String newList, Storage storage) throws FileAbsentException { + try { + FileWriter fw = new FileWriter(storage.getFilePath()); + fw.write(newList); //updates task list to newList since one task is marked as done + fw.close(); + } catch (IOException i) { + throw new FileAbsentException(storage.getFilePath()); + } + } + + /** + * gives the string for the new task list + * + * @param tasks marks the task as done + * @return the string for the new task list + */ + private String newInputInStorageFIle(TaskList tasks) { + String s = ""; + for (int i = 0; i < tasks.getAllTasks().size(); i++) { + s = s + tasks.getAllTasks().get(i).inputListFormat() + "\n"; + //new taskList String since done is being set for task with iD mentioned by user + } + return s; + } + + /** + * Returns the String informing that the task that is marked as done + * + * @param tasks uses to give the current number of tasks. + * @param iD uses to get the task to mark done. + * @return String informing that the task is marked as done. + */ + private String doneMessage(TaskList tasks, int iD) { + return " Nice! I've marked this task as done:\n" + " " + + tasks.getAllTasks().get(iD - 1).toString(); //gives the doneMessage + } + /** + * This returns the string that the task has been deleted adn also updated the TakList. + * + * @param storage in which the file is updated. + * @param tasks used to update the task for the task to mark as done. + * @param iD of the task to mark as done. + * @return String of done message + * @throws DukeException throws if file is absent + */ + private String rewrite(Storage storage, TaskList tasks, int iD) throws DukeException { + tasks.getAllTasks().get(iD - 1).setDone(true); //sets the task at (ID - 1) as done + String s = newInputInStorageFIle(tasks); //new List for storage file + try { + updateTaskInStorage(s, storage); //updates the TaskList and the file in storage file + return doneMessage(tasks, iD); // returns done message + } catch (FileAbsentException f) { + throw new FileAbsentException(storage.getFilePath()); + } + } + +} diff --git a/src/main/java/duke/commands/EventCommand.java b/src/main/java/duke/commands/EventCommand.java new file mode 100644 index 0000000000..32b67eda1d --- /dev/null +++ b/src/main/java/duke/commands/EventCommand.java @@ -0,0 +1,56 @@ +package duke.commands; + +import duke.errors.DukeException; +import duke.errors.EventException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; +import duke.tasks.Event; + +/** + * Handles cases when Event is keyword + */ +public class EventCommand extends AddCommand { + /** + * Assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public EventCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Adds Event task or handle exceptions + * + * @param tasks to change the taskList if necessary when no error + * @param ui to store the DukeException that may be thrown if there is an error in user input + * @param storage to change the file in the if necessary when no error + * @return String returns the string of the output that informs the action has been complete. + * @throws DukeException if there no description after Event no time or time is wrong format + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + exceptionThrownIfNumberOrDescriptionAbsent(); + return Event.addEventTask(tasks, ui, storage, userInput); + //Returns string if correct input and updates tasks and file in storage if correct input by user, else + // throws exception + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Test whether description is absent and exception is thrown if absent + * + * @throws DukeException thrown if description for event is absent + */ + protected void exceptionThrownIfNumberOrDescriptionAbsent() throws DukeException { + if (isNumberOrDescriptionAbsent()) { //tests whether the description is absent + throw new EventException(true, false, false, false, false); + } + } +} + diff --git a/src/main/java/duke/commands/ExitCommand.java b/src/main/java/duke/commands/ExitCommand.java new file mode 100644 index 0000000000..01be963a02 --- /dev/null +++ b/src/main/java/duke/commands/ExitCommand.java @@ -0,0 +1,51 @@ +package duke.commands; + +import duke.errors.DukeException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + +/** + * Handles case when exit is the keyword + */ +public class ExitCommand extends Command { + /** + * assigns string to a value of string + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public ExitCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Gives the exit message + * + * @return String of exit message. + */ + private String exitMessage() { + return " Bye. Hope to see you again soon!"; + } + /** + * Prints bye message + * + * @param tasks no change made + * @param ui + * @param storage no change made + * @return String returns the string of the output that informs the exit action has been complete. + * @throws DukeException not thrown here + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + return exitMessage(); + } + + /** + * Returns true to exit program + * + * @return true to exit program + */ + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/duke/commands/FindCommand.java b/src/main/java/duke/commands/FindCommand.java new file mode 100644 index 0000000000..07326043dc --- /dev/null +++ b/src/main/java/duke/commands/FindCommand.java @@ -0,0 +1,140 @@ +package duke.commands; + +import java.util.ArrayList; +import java.util.List; + +import duke.errors.DukeException; +import duke.errors.FindException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; +import duke.tasks.Task; + +/** + * Handles case where find is keyword + */ +public class FindCommand extends Command { + private List tasks = new ArrayList<>(); + //contains list of Task objects whose name contains words after the find keyword + + /** + * Assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public FindCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Finds the tasks which contains keyword in string + * + * @param tasks to look for the task's string value + * @param ui to store the DukeException that may be thrown if there is an error in user input + * @param storage no need + * @return String returns the string of the output that informs the find action is successful. + * @throws DukeException used to throw error when no words mentioned after find or the keyword is not present in + * tasks. + */ + @Override + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + return process(tasks); + //Returns string if correct input and updates tasks and file in storage if correct input by user, else + // throws exception + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Returns String informing user that find command is successful otherwise throws FindException + * + * @param tasks to access the tasks to find out the description used and to compare keywords given for this command + * @return String if user input is in correct format + * @throws FindException when user input is given in wrong format + */ + public String process(TaskList tasks) throws FindException { + if (isNumberOrDescriptionAbsent()) { + throw new FindException(false, true, ""); + } + String find = userInput.substring(5); + String[] strings = find.split(" ", -2); // keywords split into different Strings + setTasks(strings, tasks); + if (this.tasks.size() == 0) { + throw new FindException(true, false, find); + } else { + return findMessage(); + } + } + /** + * sets the Tasks list here with Tasks containing words given by user. + * + * @param strings contains String of key words + * @param tasks contains all the current tasks + */ + private void setTasks(String[] strings, TaskList tasks) { + List allTasks = tasks.getAllTasks(); + for (int i = 0; i < tasks.getAllTasks().size(); i++) { + Task task = allTasks.get(i); + String string = task.getName(); //gives name of Task object + String[] comp = string.split(" ", -2); //split object into separate words + boolean contains = taskContainsUserWords(strings, comp); // + if (contains) { + this.tasks.add(task); //if contains is true, Task is added to ArrayList of tasks. + } + } + } + + /** + * Checks whether the Task contains the words given by user + * + * @param strings array of Task name split into different words + * @param comp array of user words split into different words + * @return true is task contains words given by user and false otherwise + */ + private boolean taskContainsUserWords(String[] strings, String[] comp) { + boolean contains = false; + for (int j = 0; j < strings.length; j++) { + String word = strings[j]; + contains = wordPresentInUserWords(word, comp); + } + return contains; + } + + /** + * Checks whether a word is the same as the array of words(String) given by user + * + * @param word a word from from Task name + * @param comp array of words given by user + * @return true if word is contained in comp and false otherwise + */ + private boolean wordPresentInUserWords(String word, String[] comp) { + boolean contains = false; + for (int k = 0; k < comp.length; k++) { + if (comp[k].equals(word)) { //checks whether Task name/description contains keywords given by user + contains = true; //then assigns contains true if that is the case + break; + } + } + return contains; + } + /** + * Prints out the find message + * + * @return String of response + */ + private String findMessage() { + String s = " Here are the matching tasks in your list:"; + for (int i = 0; i < this.tasks.size(); i++) { + Task task = this.tasks.get(i); + s = s + "\n" + " " + (i + 1) + "." + task.toString(); // concatenates all the Task present in tasks + } + return s; + } + + +} + diff --git a/src/main/java/duke/commands/ListCommand.java b/src/main/java/duke/commands/ListCommand.java new file mode 100644 index 0000000000..125313b498 --- /dev/null +++ b/src/main/java/duke/commands/ListCommand.java @@ -0,0 +1,50 @@ +package duke.commands; + +import duke.errors.DukeException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + +/** + * Used to handle case when list is the keyword + */ +public class ListCommand extends Command { + /** + * Assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public ListCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Lists all the tasks that are currently present in the tasks. + * + * @param tasks to access the list and print them + * @param ui + * @param storage + * @return String returns the string of the output that informs the action has been complete. + * @throws DukeException not returned in this scenario + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + return listMessage(tasks); + } + + /** + * gives the message when list is called. + * + * @param tasks gives the current list, which is used to return current list + * @return all the current list + */ + private String listMessage(TaskList tasks) { + String s = ""; + for (int i = 0; i < tasks.getAllTasks().size(); i++) { + s = s + "\n" + " " + (i + 1) + ". " + tasks.getAllTasks().get(i); + // concatenates all the string representation of Tasks TaskList + } + return s.substring(1); + } +} + diff --git a/src/main/java/duke/commands/RandomCommand.java b/src/main/java/duke/commands/RandomCommand.java new file mode 100644 index 0000000000..c5b578fe24 --- /dev/null +++ b/src/main/java/duke/commands/RandomCommand.java @@ -0,0 +1,36 @@ +package duke.commands; + +import duke.errors.WrongInputException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + +/** + * handles case where a random word is being input + */ +public class RandomCommand extends Command { + /** + * Assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public RandomCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Gives wrong input exception + * + * @param tasks + * @param ui to store the DukeException that is thrown since there is an error in user input + * @param storage + * @return String returns the string of the output that informs the action has been complete. + * @throws WrongInputException is thrown + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws WrongInputException { + ui.setDukeException(new WrongInputException()); + throw new WrongInputException(); + } +} + diff --git a/src/main/java/duke/commands/ShortCutCommand.java b/src/main/java/duke/commands/ShortCutCommand.java new file mode 100644 index 0000000000..095596f4bc --- /dev/null +++ b/src/main/java/duke/commands/ShortCutCommand.java @@ -0,0 +1,152 @@ +package duke.commands; + +import java.io.FileWriter; +import java.io.IOException; + +import duke.errors.DukeException; +import duke.errors.FileAbsentException; +import duke.errors.ShortCutException; +import duke.helpers.ShortCuts; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + + +/** + * Handles case when short is the input to add short cuts + */ +public class ShortCutCommand extends Command { + + /** + * Assigns string to a value and integer to lengthOfKeyword + * + * @param commandDescription assigns this.string to string + * @param lengthOfKeyword assigns length of keyword to this var + */ + public ShortCutCommand(String commandDescription, int lengthOfKeyword) { + super(commandDescription, lengthOfKeyword); + } + + /** + * Duke gives String depending on input, in this case that short cut has been added + * + * @param tasks used to access tasks in its list and change if necessary + * @param ui to store the DukeException that may be thrown if there is an error in user input + * @param storage to change the input there if necessary + * @return String that short cut is added + * @throws DukeException when the user input is wrong such as + */ + @Override + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + return process(storage); + //function processes the given input and gives message that short cut is added or else exception thrown + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Judges whether the String given is correct, else it throws an error + * + * @param storage which contains information of the various short forms and checks for short forms in them + * @return String value if the String given by user is correct + * @throws DukeException thrown if there is an error in user input + */ + private String process(Storage storage) throws DukeException { + if (isNumberOrDescriptionAbsent()) { + throw new ShortCutException(true, false, false, false, ""); //if description is absent + } + String[] splitData = splitDescription(userInput); + if (shortCutPresent(splitData[1])) { + throw new ShortCutException(false, false, true, false, splitData[1]); + //if short cut is already present + } else if (containsUselessShortCut(splitData[0])) { + throw new ShortCutException(false, false, false, true, splitData[1]); + //if short cut exception is useless + } else if (!shortCutPresent(splitData[0])) { + ShortCuts.addShortCut(splitData[0], splitData[1]); + updateShortCutFile(splitData[1], splitData[0], storage); + return shortCutMessage(); //when it is correct + } else { + return null; + } + } + /** + * splits input into original form and short form + * + * @param input by user + * @return String array containing original form index 0 and short form index 1 + * @throws DukeException when there are errors in user input + */ + private String[] splitDescription(String input) throws DukeException { + boolean originalOfShortFormPresent = false; + int index = -1; + String originalKeyWord = ""; + + for (int i = lengthOfKeyword + 1; i < input.length(); i++) { + if (input.charAt(i) == ' ') { //after " " comes short form + index = i; + originalOfShortFormPresent = true; + break; + } + originalKeyWord = originalKeyWord + input.charAt(i); + } + if (!originalOfShortFormPresent || userInput.substring(index + 1).length() == 0) { + throw new ShortCutException(false, true, false, false, ""); //happens when there is nothing after keyword + } + String[] splitData = new String[]{originalKeyWord, userInput.substring(index + 1)}; + return splitData; + } + + /** + * returns boolean value of whether input is already present + * + * @param input short cut added by user + * @return true if hashMap contains input and false otherwise. + */ + private boolean shortCutPresent(String input) { + return ShortCuts.getShortCuts().containsKey(input); //if present is true + } + + /** + * Informs user that short cut has been added + * + * @return String that short cut is added + */ + private String shortCutMessage() { + return " short cut successfully added"; + } + + /** + * adds new short cuts by user into file + * + * @param shortCut by user + * @param original duke function name + * @param storage where the file is contained in + * @throws DukeException thrown when file doesn't exist + */ + private void updateShortCutFile(String shortCut, String original, Storage storage) throws DukeException { + try { + FileWriter fw = new FileWriter(storage.getShortFormsFilePath(), true); + //updates the file in storage as new task is added + String newShortCutToAdd = shortCut + " " + original; + fw.write(newShortCutToAdd + "\n"); + fw.close(); + } catch (IOException i) { + throw new FileAbsentException(storage.getShortFormsFilePath()); + } + } + + /** + * Checks whether original form given is uselss + * + * @param originalForm given by user + * @return true if useless and false otherwise + */ + private boolean containsUselessShortCut(String originalForm) { + return !ShortCuts.getShortCuts().containsValue(originalForm); + //since the short cut in ShortCut contains value of all tasks recognised by user + } +} diff --git a/src/main/java/duke/commands/TodoCommand.java b/src/main/java/duke/commands/TodoCommand.java new file mode 100644 index 0000000000..1f7ed417bb --- /dev/null +++ b/src/main/java/duke/commands/TodoCommand.java @@ -0,0 +1,69 @@ +package duke.commands; + +import duke.errors.DukeException; +import duke.errors.TodoException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; +import duke.tasks.ToDo; + +/** + * Handles case where todo is the input + */ +public class TodoCommand extends AddCommand { + /** + * Assigns string to a value of string + * + * @param input assigns string to this this.string + * @param lengthOfKeyword assigns this to this.lengthOfKeyword + */ + public TodoCommand(String input, int lengthOfKeyword) { + super(input, lengthOfKeyword); + } + + /** + * Adds deadline into a task list in TaskList. + * + * @param tasks to change the taskList if necessary + * @param ui to store the DukeException that may be thrown if there is an error in user input + * @param storage to change the file in the if necessary + * @return String returns the string of the output that informs the action has been complete. + * @throws DukeException whenever there is an error, no + * description + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + try { + return process(tasks, storage); + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + + /** + * Returns String if user input is correct and updates the TaskList tasks and file containing tasks file in + * storage + * + * @param tasks to add ToDo task here if the user input is correct + * @param storage to add ToDo task in the file containing tasks + * @return String informing that the user has input the task + * @throws DukeException if user input is correct + */ + private String process(TaskList tasks, Storage storage) throws DukeException { + if (isNumberOrDescriptionAbsent()) { + throw new TodoException(); + } + ToDo t = new ToDo(todoDescription()); + return updateTaskList(storage, t, tasks); + } + /** + * returns the name of the task + * + * @return name of task + */ + private String todoDescription() { + return userInput.substring(lengthOfKeyword + 1); + } + +} + diff --git a/src/main/java/duke/errors/DeadlineException.java b/src/main/java/duke/errors/DeadlineException.java new file mode 100644 index 0000000000..2b0cc0ba21 --- /dev/null +++ b/src/main/java/duke/errors/DeadlineException.java @@ -0,0 +1,79 @@ +package duke.errors; + +/** + * This DeadlineException is used to print out exceptions when there is an incomplete input where wether the description + * or date is absent. + */ + +public class DeadlineException extends DukeException { + /** + * descriptionPresent tests shows whether the description is present in the input of the user or not. + * If description is not present it is true, else it is false + */ + private boolean isDescriptionAbsent; //true if description for deadline keyword is not given, false otherwise + private boolean isDateTimeFormatWrong; //true if date and/ or time is given in wrong format by user, false otherwise + private boolean isDateTimeAbsent; //true if date is not given by user, false otherwise + + /** + * constructor for deadline exception that assigns description and format values + * + * @param isDescriptionAbsent true if description absent + * @param isDateTimeFormatWrong true if format is wrong + * @param isDateTimeAbsent true if date and time are absent. + */ + public DeadlineException(boolean isDescriptionAbsent, boolean isDateTimeFormatWrong, boolean isDateTimeAbsent) { + this.isDescriptionAbsent = isDescriptionAbsent; + this.isDateTimeFormatWrong = isDateTimeFormatWrong; + this.isDateTimeAbsent = isDateTimeAbsent; + } + + /** + * doesn't take in any arguments, overrides the in-built toString() method. + * + * @return returns a string informing that the description is empty if descriptionAbsent is true. Else, it tests + * whether the format is wrong, if it then a string describing it would be returned. + * If isDateTimeAbsent is true then + * date time for deadline is absent and a String describing it would be returned. + * Else then default returns which + * should not occur. + */ + public String toString() { + if (this.isDescriptionAbsent) { + return descriptionAbsent(); //when descriptionAbsent + } else if (this.isDateTimeFormatWrong) { + return dateTimeFormatWrong(); //when dateTimeFormat is wrong + } else if (this.isDateTimeAbsent) { + return dateTimeAbsent(); //when dateTime is absent + } else { + return "default"; + } + } + + /** + * returns String on condition where description for Deadline is absent + * + * @return String informing user that the description for Deadline is absent + */ + private String descriptionAbsent() { + return " '\u2639' OOPS!!! The description of a deadline cannot be empty."; + } + + /** + * returns String on condition where format for date and/or time format for Deadline is wrong + * + * @return String informing user format for date and/or time format for Deadline is wrong + */ + private String dateTimeFormatWrong() { + return " '\u2639' OOPS!!! The formats of date and/ or include " + + "yyyy MM dd/ yyyy MM dd, HH:mm/ HH:mm"; + } + + /** + * returns String on condition where date and/or time for Deadline is absent + * + * @return String informing user that the date and/or time for Deadline is absent + */ + private String dateTimeAbsent() { + return " '\u2639' OOPS!!! The specific date/time of a deadline cannot be empty."; + } +} diff --git a/src/main/java/duke/errors/DeleteException.java b/src/main/java/duke/errors/DeleteException.java new file mode 100644 index 0000000000..2e91de16d8 --- /dev/null +++ b/src/main/java/duke/errors/DeleteException.java @@ -0,0 +1,65 @@ +package duke.errors; + +/** + * This DeleteException is used to print out exceptions when there is an incomplete input where the ID + * is absent or the ID + * of that Task hasn't been defined yet or it has previously been deleted. + */ +public class DeleteException extends DukeException { + /** + * isiDabsent tests shows whether the ID is present in the input of the user or not. + * If ID is not present it is true, else it is false + * deleted checks whether the task was previously deleted, if deleted it is true else false. + */ + private boolean isiDAbsent; //true if iD is not given by user, false otherwise + private boolean isNotiDDefined; // true if iD is not defined yet, false otherwise + + /** + * constructor that assigns tne 2 variables its respective values + * + * @param isiDabsent input, true if ID is input my reader, false otherwise. + * @param isNOtiDDefined input, true if ID > number of tasks present, false otherwise. + */ + public DeleteException(boolean isiDabsent, boolean isNOtiDDefined) { + this.isiDAbsent = isiDabsent; + this.isNotiDDefined = isNOtiDDefined; + } + + /** + * doesn't take in any arguments, overrides the in-built toString() method, for printing in getMessage(). + * + * @return returns a string depending on the scenario. If the IDAbsent is true, then description + * that the description of + * delete cannot be empty. Else if isDefined is true then String returning that ID is not defined + * is returned. Else, + * default is returned which should not occur. + */ + @Override + public String toString() { + if (isiDAbsent) { + return iDAbsent(); //when ID is not input by user + } else if (isNotiDDefined) { + return iDNotDefined(); //when ID is not defined yet + } else { + return "default"; + } + } + + /** + * returns on condition when user didn't mention the task ID to delete + * + * @return String informing user that ID of task to be deleted is not mentioned + */ + private String iDAbsent() { + return " '\u2639' OOPS!!! The description of delete cannot be empty."; + } + + /** + * returns on condition when the ID is more than the number of tasks present + * + * @return String informing user that ID of task to be deleted is not defined yet. + */ + private String iDNotDefined() { + return " '\u2639' OOPS!!! The ID is not yet defined."; + } +} diff --git a/src/main/java/duke/errors/DoneException.java b/src/main/java/duke/errors/DoneException.java new file mode 100644 index 0000000000..99053c9779 --- /dev/null +++ b/src/main/java/duke/errors/DoneException.java @@ -0,0 +1,70 @@ +package duke.errors; + +/** + * This DoneException is used to print out exceptions when there is an incomplete input where the iD is absent or the iD + * of that Task hasnt been defined yet. + */ +public class DoneException extends DukeException { + /** + * IDabsent tests shows whether the iD is present in the input of the user or not. + * If ID is not present it is true, else it is false + */ + private boolean isiDAbsent; //true if iD is not given by user, false otherwise + private boolean isNotiDDefined; //true if iD is not defined yet, false otherwise + private boolean isDone; //true if task is already completed, false otherwise + + /** + * constructor that assigns tne 2 variables its respective values + * @param isiDabsent input, depending on whether the iD is present or not in the input.txt file. + * If present it is false else it is true. + * @param isNOtiDDefined input, true if ID > number of tasks present, false otherwise. + */ + public DoneException(boolean isiDabsent, boolean isNOtiDDefined, boolean isDone) { + this.isiDAbsent = isiDabsent; + this.isNotiDDefined = isNOtiDDefined; + this.isDone = isDone; + } + + /** + * doesn't take in any arguments, overrides the in-built toString() method. + * + * @return returns a string depending on the scenario. If the IDabsent is true, + * then description that the description of + * done cannot be empty.Else if isDefined is true then String returning that iD is not defined is returned. Else, + * default is returned which should not occur. + */ + @Override + public String toString() { + if (isiDAbsent) { + return iDAbsent(); //when ID is absent + } else if (isNotiDDefined) { + return iDNotDefined(); //when ID is not defined + } else if (isDone) { + return isDone(); + } else { + return "default"; + } + } + + /** + * Returns when iD is not given by user + * + * @return String saying that description of done(a number) cannot be empty + */ + private String iDAbsent() { + return " '\u2639' OOPS!!! The description of done cannot be empty."; + } + + /** + * Returns when ID input by user is not defined yet + * + * @return String saying that ID is not defined. + */ + private String iDNotDefined() { + return " '\u2639' OOPS!!! The ID is not yet defined."; + } + + private String isDone() { + return "You have already completed this task"; + } +} diff --git a/src/main/java/duke/errors/DukeException.java b/src/main/java/duke/errors/DukeException.java new file mode 100644 index 0000000000..7486347bdc --- /dev/null +++ b/src/main/java/duke/errors/DukeException.java @@ -0,0 +1,17 @@ +package duke.errors; + +/** + * this is a DukeException class which is the parents class of all the other exceptions in this package. + * this is never initialized and therefore is an abstract class and used for polymorphism. + */ +public abstract class DukeException extends Exception { + /** + * overrides getMessage of an error + * + * @return string of the exception + */ + public String getMessage() { + return toString(); + } +} + diff --git a/src/main/java/duke/errors/EventException.java b/src/main/java/duke/errors/EventException.java new file mode 100644 index 0000000000..70a940d2cb --- /dev/null +++ b/src/main/java/duke/errors/EventException.java @@ -0,0 +1,108 @@ +package duke.errors; + +/** + * This EventException is used to print out exceptions when there is an incomplete input where whether the description + * or date is absent. + */ +public class EventException extends DukeException { + /** + * description tests shows whether the description is present in the input of the user or not. + * If description is not present it is true, else it is false + */ + private boolean isDescriptionAbsent; //true if the user input does not have description, else false + private boolean isEndTimeAbsent; //true if end time is not given by user, false otherwise + private boolean isStartAfterEnd; //true if start>end, false otherwise + private boolean isDateTimeWrongFormat; //true if date and/ or time is in wrong format, false otherwise + private boolean isStartDateTimeEmpty; //true if start date and/ or time is not given by user, false otherwise + + /** + * + * @param isDescriptionAbsent input, depending on whether the description is given by user. + * @param isEndTimeAbsent is true when the user has no input for end time + * @param isStartAfterEnd is true when the start time is after end time + * @param isDateTimeWrongFormat is true when date and/or time is input in wrong format. + * @param isStartDateTimeEmpty is true when date abd/or time is input in wrong format + */ + public EventException(boolean isDescriptionAbsent, boolean isEndTimeAbsent, boolean isStartAfterEnd, + boolean isDateTimeWrongFormat, boolean isStartDateTimeEmpty) { + this.isDescriptionAbsent = isDescriptionAbsent; + this.isEndTimeAbsent = isEndTimeAbsent; + this.isStartAfterEnd = isStartAfterEnd; + this.isDateTimeWrongFormat = isDateTimeWrongFormat; + this.isStartDateTimeEmpty = isStartDateTimeEmpty; + } + + /** + * doesn't take in any arguments, overrides the in-built toString() method. + * @return returns a string informing that the description is empty if description is true. + * If isEndTimeAbsent is absent is true, end time is absent and then a + * description mentioning this would be returned. + * Else, if isStartAfterEnd is true, start would be more than end then a + * description describing this would be printed + * Else if ifDateWrongFormat is true, then the date is in wrong format and + * a description describing it would be printed + * Else if isDateEmpty is true, then String giving that is returned. + * Else default is returned. + */ + public String toString() { + if (this.isDescriptionAbsent) { + return descriptionAbsent(); //when description is not given by user + } else if (this.isEndTimeAbsent) { + return endTimeAbsent(); //when end time is not given by user + } else if (this.isStartAfterEnd) { + return startAfterEnd(); //when start is after end + } else if (this.isDateTimeWrongFormat) { + return dateTimeWrongFormat(); //when date is given in wrong format + } else if (this.isStartDateTimeEmpty) { + return startDateTimeEmpty(); //when start date and/or time is absent + } else { + return "default"; + } + } + + /** + * Returns when description of Event is absent + * + * @return String saying that description of Event is absent. + */ + private String descriptionAbsent() { + return " '\u2639' OOPS!!! The description of an Event cannot be empty."; + } + + /** + * Returns when endTime is not given by user + * + * @return String saying end time is not given by user + */ + private String endTimeAbsent() { + return " '\u2639' OOPS!!! There should be 2 occurrences of date and/or time values."; + } + + /** + * Returns when start time is more than end + * + * @return String saying that start < end + */ + private String startAfterEnd() { + return " '\u2639' OOPS!!! Start should be less than end."; + } + + /** + * Returns when date and/or time is in wrong format + * + * @return String saying that date and/or time is in wrong format and the format it should be in + */ + private String dateTimeWrongFormat() { + return " '\u2639' OOPS!!! Start and should be of the same format. The formats include " + + "yyyy MM dd/ yyyy MM dd, HH:mm/ HH:mm"; + } + + /** + * Returns when start date and/or time is empty + * + * @return String saying that start date and/or time is absent + */ + private String startDateTimeEmpty() { + return " '\u2639' OOPS!!! The specific date and/or time of an Event cannot be empty."; + } +} diff --git a/src/main/java/duke/errors/FIleEmptyException.java b/src/main/java/duke/errors/FIleEmptyException.java new file mode 100644 index 0000000000..bab09fbca2 --- /dev/null +++ b/src/main/java/duke/errors/FIleEmptyException.java @@ -0,0 +1,26 @@ +package duke.errors; + +/** + * This exception is thrown when the file is empty + */ +public class FIleEmptyException extends DukeException { + /** + * this overrides the toString() method + * + * @return a String representation of FileEmptyException + */ + @Override + public String toString() { + return fileEmpty(); //when file is empty + } + + /** + * Returns when file is empty + * + * @return informs user that file is empty + */ + private String fileEmpty() { + return " Task file is empty!"; + } +} + diff --git a/src/main/java/duke/errors/FileAbsentException.java b/src/main/java/duke/errors/FileAbsentException.java new file mode 100644 index 0000000000..025f8daef6 --- /dev/null +++ b/src/main/java/duke/errors/FileAbsentException.java @@ -0,0 +1,36 @@ +package duke.errors; + +/** + * This exception is thrown whenever there is a file absent at a specific path mentioned in the filePath + */ +public class FileAbsentException extends DukeException { + private String isFilePathAbsent; //the String of the filePath where file is not present + + /** + * This assigns filePath variable to a value + * + * @param isFilePathAbsent the value assigned to filePath + */ + public FileAbsentException(String isFilePathAbsent) { + this.isFilePathAbsent = isFilePathAbsent; + } + + /** + * This overrides the toString() method + * + * @return a String for the exception is printed. + */ + @Override + public String toString() { + return fileAbsent(); //when file is absent + } + + /** + * Returns when file is not present + * + * @return String saying that file is absent. + */ + private String fileAbsent() { + return " The file in this directory " + this.isFilePathAbsent + " is absent!"; + } +} diff --git a/src/main/java/duke/errors/FindException.java b/src/main/java/duke/errors/FindException.java new file mode 100644 index 0000000000..cface66e1b --- /dev/null +++ b/src/main/java/duke/errors/FindException.java @@ -0,0 +1,58 @@ +package duke.errors; + +/** + * The class FindException deals with what happens when an error occurs for task with find keyword + */ +public class FindException extends DukeException { + private boolean noMatches; //true if there are no matches from user input to the Task names + private boolean isDescriptionAbsent; //true if description is not given by input, false otherwise + private String description; //the description given by user(words to search for in tasks) + + /** + * constructor assigns values of description and string + * + * @param noMatches value is assigned to noMatches + * @param isDescriptionAbsent value is assigned to this.description + * @param description value is assigned to this.string + */ + public FindException(boolean noMatches, boolean isDescriptionAbsent, String description) { + this.noMatches = noMatches; + this.isDescriptionAbsent = isDescriptionAbsent; + this.description = description; + } + + /** + * overrides the toString() method + * + * @return if description is present error is due to no matches being present and an error message informing + * them would be printed. If it is not present than error is due to keywords being absent therefore an error + * message regarding that would be released. + */ + public String toString() { + if (noMatches) { + return noMatches(); //when there are no matches + } else if (isDescriptionAbsent) { + return descriptionAbsent(); //when description is absent + } else { + return "default"; + } + } + + /** + * Returns when there are no matches for the keywords given + * + * @return String that there are no matches to the keywords given + */ + public String noMatches() { + return " there are no matches to your keyword: " + description; + } + + /** + * Returns when there are no keywords given by user + * + * @return String that the keywords are absent. + */ + private String descriptionAbsent() { + return " description of find cannot be empty!"; + } +} diff --git a/src/main/java/duke/errors/ShortCutException.java b/src/main/java/duke/errors/ShortCutException.java new file mode 100644 index 0000000000..366f01e2c3 --- /dev/null +++ b/src/main/java/duke/errors/ShortCutException.java @@ -0,0 +1,88 @@ +package duke.errors; + +import duke.helpers.ShortCuts; + +/** + * used when there is an error in user input while adding short cut + */ +public class ShortCutException extends DukeException { + private boolean isDescriptionAbsent; //true if the description is absent in user input, false otherwise + private boolean isShortCutAbsent; //true if short cut is not provided + private boolean isShortCutAlreadyPresent; //true if short cut is already defined, false otherwise + private boolean isUselessShortCut; //if short cut is defined for a command not read by Duke + private String shortCut; + + /** + * constructor which assigns the above 4 member variables into its respective values + * + * @param isDescriptionAbsent true is description is not given by user, false otherwise + * @param isShortCutAbsent true if short form is not given by user, false otherwise + * @param isShortCutAlreadyPresent true if short cut is already present + * @param isUselessShortCut true if short cut's original value means nothing to Duke + * @param shortCut shortcut provided by user + */ + public ShortCutException(boolean isDescriptionAbsent, boolean isShortCutAbsent, + boolean isShortCutAlreadyPresent, boolean isUselessShortCut, String shortCut) { + this.isDescriptionAbsent = isDescriptionAbsent; + this.isShortCutAbsent = isShortCutAbsent; + this.isShortCutAlreadyPresent = isShortCutAlreadyPresent; + this.isUselessShortCut = isUselessShortCut; + this.shortCut = shortCut; + } + + /** + * overrides built-in toString method + * + * @return String for the reason this Exception is thrown + */ + public String toString() { + if (isDescriptionAbsent) { + return descriptionAbsent(); //when description is absent + } else if (isShortCutAbsent) { + return shortCutAbsent(); //when short cut is absent + } else if (isShortCutAlreadyPresent) { + return shortCutAlreadyPresent(); //when short cut is already present + } else if (isUselessShortCut) { + return uselessShortCut(); //when short cut has no meaning + } else { + return "default"; + } + } + + /** + * Describes that description of short cannot be empty + * + * @return String informing user that description of short cannot be empty" + */ + private String descriptionAbsent() { + return " The description of short cannot be empty"; + } + + /** + * Describes that short cut cannot be empty + * + * @return String informing user that short cut is not provided. + */ + private String shortCutAbsent() { + return "short cut is not provided"; + } + + /** + * Describes that short cut is already present + * + * @return String informing user that short cut is already present + */ + private String shortCutAlreadyPresent() { + assert ShortCuts.getShortCuts().containsKey(this.shortCut); + return this.shortCut + " is already present as a short form for" + ShortCuts.getShortCuts().get(this.shortCut); + } + + /** + * Describes that short cut has no meaning + * + * @return String informing user that short cut has no meaning + */ + private String uselessShortCut() { + return this.shortCut + " has no meaning"; + } +} diff --git a/src/main/java/duke/errors/TodoException.java b/src/main/java/duke/errors/TodoException.java new file mode 100644 index 0000000000..1d6613fab6 --- /dev/null +++ b/src/main/java/duke/errors/TodoException.java @@ -0,0 +1,26 @@ +package duke.errors; + +/** + * This TodoException is used to print out exceptions when there is an incomplete input where whether the description + * or date is absent. + */ +public class TodoException extends DukeException { + /** + * doesn't take in any arguments, overrides the in-built toString() method. + * + * @return returns a string informing that the description of the ToDo is absent as it cant be. + */ + public String toString() { + return descriptionAbsent(); //when description is absent + } + + /** + * Returns when description is absent + * + * @return String informign user that the description of ToDo cannot be empty. + */ + private String descriptionAbsent() { + return " '\u2639' OOPS!!! The description of a ToDo cannot be empty"; + } +} + diff --git a/src/main/java/duke/errors/WrongInputException.java b/src/main/java/duke/errors/WrongInputException.java new file mode 100644 index 0000000000..46b5325336 --- /dev/null +++ b/src/main/java/duke/errors/WrongInputException.java @@ -0,0 +1,24 @@ +package duke.errors; + +/** + * This prints the exception when a wrong word is being input. + */ +public class WrongInputException extends DukeException { + /** + * This takes not args and overrides the toString() method + * + * @return a string that describes the error that took place which is that an invalid string has been inserted + */ + public String toString() { + return wrongInputString(); + } + + /** + * returns when user gives something Duke doesn't understand. + * + * @return String that the user has mentioned something that the Duke cannot understand + */ + private String wrongInputString() { + return " '\u2639 OOPS!!! I'm sorry, but I don't know what that means :-("; + } +} diff --git a/src/main/java/duke/helpers/Parser.java b/src/main/java/duke/helpers/Parser.java new file mode 100644 index 0000000000..c7dbbc2eaa --- /dev/null +++ b/src/main/java/duke/helpers/Parser.java @@ -0,0 +1,267 @@ +package duke.helpers; + + +import duke.commands.Command; +import duke.commands.DeadlineCommand; +import duke.commands.DeleteCommand; +import duke.commands.DoneCommand; +import duke.commands.EventCommand; +import duke.commands.ExitCommand; +import duke.commands.FindCommand; +import duke.commands.ListCommand; +import duke.commands.RandomCommand; +import duke.commands.ShortCutCommand; +import duke.commands.TodoCommand; + +/** + * This is a Parser class that determines which command operation to choose, which in turn determines + * the action to be taken. + */ +public class Parser { + + /** + * Returns a Command, depending on the string being input + * + * @param input where the first words determines command to be returned + * @return Command is returned based on the first word of param string + */ + public static Command parse(String input) { + assert !input.contains(","); //to ensure that no , appears when multiple inputs are given + if (ShortCuts.containsShortCut(input)) { + String properInput = shortCutStringUpdate(input); + return parse(properInput); + } else if (isShortCutDefined(input)) { + return shortCutCommand(input); + } else if (isBye(input)) { //represents ExitCommand + return exitCommand(input); + } else if (isList(input)) { //represents ListCommand + return listCommand(input); + } else if (isDelete(input)) { //represents deleteCommand + return deleteCommand(input); + } else if (isDone(input)) { //represents doneCommand + return doneCommand(input); + } else if (isTodo(input)) { //represents ToDoCommand + return todoCommand(input); + } else if (isEvent(input)) { //represents EventCommand + return eventCommand(input); + } else if (isDeadline(input)) { //represents DeadlineCommand + return deadlineCommand(input); + } else if (isFind(input)) { //represents FindCommand + return findCommand(input); + } else { //rest are RandomCommand + return randomCommand(input); + } + } + + /** + * checks whether short cut is attempted to be defined + * + * @param input String by user + * @return boolean whether short cut is attempted to be defined + */ + private static boolean isShortCutDefined(String input) { + return input.length() >= 5 && input.substring(0, 5).equals("short"); + } + + /** + * checks whether bye is said by user + * + * @param input String by user + * @return boolean whether bye is said, true if bye is said and false otherwise + */ + private static boolean isBye(String input) { + return input.length() >= 3 && input.equals("bye"); + } + + /** + * checks whether list is said by user + * + * @param input String by user + * @return boolean whether list is said, true if list is said and false otherwise + */ + private static boolean isList(String input) { + return input.length() >= 4 && input.equals("list"); + } + + /** + * checks whether delete is said by user + * + * @param input String by user + * @return boolean whether delete is said, true if delete is said and false otherwise + */ + private static boolean isDelete(String input) { + return input.length() >= 6 && input.substring(0, 6).equals("delete"); + } + + /** + * checks whether done is said by user + * + * @param input String by user + * @return boolean whether done is said, true done is said and false otherwise + */ + private static boolean isDone(String input) { + return input.length() >= 4 && input.substring(0, 4).equals("done"); + } + + /** + * checks whether todo is said by user + * + * @param input String by user + * @return boolean whether todo is said, true if bye is said and false otherwise + */ + private static boolean isTodo(String input) { + return input.length() >= 4 && input.substring(0, 4).equals("todo"); + } + + /** + * checks whether event is said by user + * + * @param input String by user + * @return boolean whether event is said, true if event is said and false otherwise + */ + private static boolean isEvent(String input) { + return input.length() >= 5 && input.substring(0, 5).equals("event"); + } + + /** + * checks whether deadline is said by user + * + * @param input String by user + * @return boolean whether deadline is said, true if deadline is said and false otherwise + */ + private static boolean isDeadline(String input) { + return input.length() >= 8 && input.substring(0, 8).equals("deadline"); + } + + /** + *checks whether find is said by user + * + * @param input String by user + * @return boolean whether find is said, true if find is said and false otherwise + */ + private static boolean isFind(String input) { + return input.length() >= 4 && input.substring(0, 4).equals("find"); + } + + private static ShortCutCommand shortCutCommand(String input) { + int lengthOfKeyword = "short".length(); + return new ShortCutCommand(input, lengthOfKeyword); + } + /** + * Gives exit command + * + * @param input String given by user + * @return ExitCommand + */ + private static ExitCommand exitCommand(String input) { + int lengthOfKeyWord = "list".length(); + return new ExitCommand(input, lengthOfKeyWord); //when in short form + } + + /** + * Gives list command + * + * @param input String given by user + * @return ListCommand + */ + private static ListCommand listCommand(String input) { + int lengthOfKeyWord = "list".length(); + return new ListCommand(input, lengthOfKeyWord); //when in short form + } + + /** + * Gives delete command + * + * @param input String given by user + * @return DeleteCommand + */ + private static DeleteCommand deleteCommand(String input) { + int lengthOfKeyWord = "delete".length(); + return new DeleteCommand(input, lengthOfKeyWord); //when not in short form + } + + /** + * Gives done command + * + * @param input String given by user + * @return DoneCommand + */ + private static DoneCommand doneCommand(String input) { + int lengthOfKeyWord = "done".length(); + return new DoneCommand(input, lengthOfKeyWord); //when not in short form + } + + /** + * Gives todo command + * + * @param input String given by user + * @return ToDoCommand + */ + private static TodoCommand todoCommand(String input) { + int lengthOfKeyWord = "todo".length(); + return new TodoCommand(input, lengthOfKeyWord); //when not in short form + } + + /** + * Gives event command + * + * @param input String given by user + * @return EventCommand + */ + private static EventCommand eventCommand(String input) { + int lengthOfKeyWord = "event".length(); + return new EventCommand(input, lengthOfKeyWord); //when not in short form + } + + /** + * Gives deadline command + * + * @param input String given by user + * @return DeadlineCommand + */ + private static DeadlineCommand deadlineCommand(String input) { + int lengthOfKeyWord = "deadline".length(); + return new DeadlineCommand(input, lengthOfKeyWord); //when not in short form + } + + /** + * Gives exit command + * + * @param input String given by user + * @return ExitCommand + */ + private static FindCommand findCommand(String input) { + int lengthOfKeyWord = "find".length(); + return new FindCommand(input, lengthOfKeyWord); //when not in short form + } + + /** + * If short cuts is present it gives the original string, converts it to proper form. + * + * @param input String given by user + * @return gives proper String input without the short form + */ + public static String shortCutStringUpdate(String input) { + String[] strings = input.split(" "); + String command = ShortCuts.getShortCuts().get(strings[0]); + String properInput = command; + + for (int i = 1; i < strings.length; i++) { + properInput = properInput + " " + strings[i]; + } + return properInput; + } + + /** + * Gives random command when user doesn't make sense + * + * @param input String given by user + * @return RandomCommand + */ + private static RandomCommand randomCommand(String input) { + return new RandomCommand(input, -1); + } + + + +} diff --git a/src/main/java/duke/helpers/ShortCuts.java b/src/main/java/duke/helpers/ShortCuts.java new file mode 100644 index 0000000000..fdde2e6ea7 --- /dev/null +++ b/src/main/java/duke/helpers/ShortCuts.java @@ -0,0 +1,75 @@ +package duke.helpers; + +import java.util.HashMap; + +/** + * ShortCuts class is used to define shortcuts and access defined shortucts + */ +public class ShortCuts { + private static HashMap shortCuts = new HashMap<>(); + //Contains the shortcuts, where the Key is the short form and the value is the original value + + /** + * Constructor to insert shortCuts into shortCuts hashMap + * + * @param shortCut is the key for the hashMap + * @param originalForm is the value for the hashMap + */ + private ShortCuts(String shortCut, String originalForm) { + shortCuts.put(shortCut, originalForm); + } + /** + * gets the shortCuts and sets default values of short cut to proper keywords + * + * @return shortCuts + */ + public static HashMap getShortCuts() { + defaultShortCut(); //sets the default short cuts to shortcuts + return shortCuts; + } + + /** + * default short cuts provided by Duke + */ + public static void defaultShortCut() { + shortCuts.put("s", "short"); //s is short form for short + shortCuts.put("b", "bye"); //b is short form for bye + shortCuts.put("l", "list"); //l is short form for list + shortCuts.put("d", "delete"); //d is short form for delete + shortCuts.put("do", "done"); //do is short form for done + shortCuts.put("t", "todo"); //t is short form for todo + shortCuts.put("e", "event"); //e is short form for event + shortCuts.put("de", "deadline"); //de is short form for deadline + shortCuts.put("f", "find"); //f is short for find + } + + /** + * Adds shortcuts to the shortCuts hashMap given by user + * + * @param originalForm is the actual keyword + * @param shortForm the short form given by user + */ + public static void addShortCut(String originalForm, String shortForm) { + new ShortCuts(shortForm, originalForm); + //calls constructor which then puts originalForm into key and shortForm into value + } + + /** + * Checks whether the input contains the short cut + * + * @param input given by user + * @return boolean value on whether the keyword given by the user is a defined short cut. + */ + public static boolean containsShortCut(String input) { + defaultShortCut(); + String keyWord = ""; + for (int i = 0; i < input.length(); i++) { + if (input.charAt(i) == ' ') { //space indicates that the keyword is over. + break; + } + keyWord = keyWord + input.charAt(i); + } + return shortCuts.containsKey(keyWord); //checks whether hashmap contains keyword. + } + +} diff --git a/src/main/java/duke/helpers/Storage.java b/src/main/java/duke/helpers/Storage.java new file mode 100644 index 0000000000..fa5b1f3409 --- /dev/null +++ b/src/main/java/duke/helpers/Storage.java @@ -0,0 +1,192 @@ +package duke.helpers; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import duke.errors.DeadlineException; +import duke.errors.DukeException; +import duke.errors.FIleEmptyException; +import duke.errors.FileAbsentException; +import duke.tasks.Deadline; +import duke.tasks.Event; +import duke.tasks.Task; +import duke.tasks.ToDo; + + +/** + * Deals with loading tasks from the file and saving tasks in the file and stores shortcuts + */ +public class Storage { + private String filePath; + private String shortFormsFilePath; + /** + * Constructor assigns filePath to filePath + * @param filePath assigns this value to variable + */ + public Storage(String filePath, String shortFormsFile) { + this.filePath = filePath; + this.shortFormsFilePath = shortFormsFile; + } + public void setShortForm() throws DukeException { + File f = new File(this.shortFormsFilePath); + assert f.exists(); //file should exist to keep track of tasks + try { + Scanner sc = new Scanner(f); + if (sc.hasNextLine()) { + do { + String shortFormString = sc.nextLine(); + String[] shortFormDictionary = shortFormDictionary(shortFormString); + ShortCuts.getShortCuts().put(shortFormDictionary[0], shortFormDictionary[1]); + } while (sc.hasNextLine()); + } + } catch (IOException i) { + throw new FileAbsentException(shortFormsFilePath); + } + } + /** + * Converts the string form of tasks on the file to Task objects + * + * @return the List of tasks containing Task instead of String + * @throws DukeException when a file with FilePath doesnt exist. + */ + public List load() throws DukeException { + + File f = new File(this.filePath); + assert f.exists(); //file should exist to keep track of tasks + try { + List tasks = new ArrayList<>(); + Scanner sc = new Scanner(f); + while (sc.hasNext()) { + addTasks(tasks, sc); + } + //addTasks(tasks, sc); + if (tasks.size() == 0) { + throw new FIleEmptyException(); + } else { + return tasks; + } + } catch (IOException error) { + throw new FileAbsentException(this.filePath); + } + } + + /** + * Adds all the current tasks to the TaskList tasks + * + * @param tasks where task being scanned are inserted into TaskList tasks + * @param sc Scanner used to scan user input + * @throws DukeException + */ + private void addTasks(List tasks, Scanner sc) throws DukeException { + String input = sc.nextLine(); + char bool = input.charAt(4); //gives the char of 1 or 0 as it is always present at index 4 + boolean isDone = (bool == '1'); //since 1 indicates done + if (input.charAt(0) == 'T') { + ToDo todoPresent = todoPresent(input, isDone); + tasks.add(todoPresent); + } else if (input.charAt(0) == 'E') { + Event eventPresent = eventPresent(input, isDone); + tasks.add(eventPresent); + } else if (input.charAt(0) == 'D') { + Deadline deadlinePresent = deadlinePresent(input, isDone); + tasks.add(deadlinePresent); + } else { + + } + } + /** + * Returns ToDo task for that present in the list in storage + * + * @param input string from the file in storage + * @param isDone boolean of whether task is completed or not. True if completed and false otherwise. + * @return ToDo task + */ + private ToDo todoPresent(String input, boolean isDone) { + return new ToDo(input.substring(8), isDone); // since the string after index 8 + } + + /** + * Returns Event task for that present in list in storage + * + * @param input string from file in storage + * @param isDone boolean of whether task is completed or not. True if completed and false otherwise. + * @return Event task + */ + private Event eventPresent(String input, boolean isDone) throws DukeException { + String string = ""; + int index = -1; + for (int i = 8; i < input.length(); i++) { + if (input.charAt(i) == '|') { + index = i; + break; + } + string = string + input.charAt(i); //character "|" splits the description of event and time. + } + String another = ""; + for (int i = index + 2; i < input.length(); i++) { + if (input.charAt(i) == '-') { + index = i; + break; + } + another = another + input.charAt(i); // character "-" separates the start and end time. + } + return Event.eventTask(string, another, input.substring(index + 1), isDone); + } + /** + * Returns Deadline task for that present in list in storage + * + * @param input string from file in storage + * @param isDone boolean of whether task is completed or not. True if completed and false otherwise. + * @return Deadline task + */ + private Deadline deadlinePresent(String input, boolean isDone) throws DeadlineException { + String string = ""; + int index = -1; + for (int i = 8; i < input.length(); i++) { + if (input.charAt(i) == '|') { + index = i; + break; + } + string = string + input.charAt(i); //line '|' splits the description of deadline and time. + } + return Deadline.deadlineTask(string, input.substring(index + 2), isDone); + } + /** + * gives the filePath + * @return the value of filePath + */ + public String getFilePath() { + return filePath; + } + + /** + * This splits the short form into the original form and short form + * + * @param shortFormString is the String input by user + * @return String array containing the original and the short form of keywords + */ + public String[] shortFormDictionary(String shortFormString) { + String shortForm = ""; + int index = -1; + for (int i = 0; i < shortFormString.length(); i++) { + if (shortFormString.charAt(i) == ' ') { + index = i; + break; + } + shortForm = shortForm + shortFormString.charAt(i); + } + return new String[]{shortForm, shortFormString.substring(index + 1)}; + } + + /** + * Gets the shortFormsFilePath + * + * @return value of shortFormsFilePath + */ + public String getShortFormsFilePath() { + return shortFormsFilePath; + } +} diff --git a/src/main/java/duke/helpers/TaskList.java b/src/main/java/duke/helpers/TaskList.java new file mode 100644 index 0000000000..6597d50013 --- /dev/null +++ b/src/main/java/duke/helpers/TaskList.java @@ -0,0 +1,37 @@ +package duke.helpers; + +import java.util.ArrayList; +import java.util.List; + +import duke.tasks.Task; + +/** + * contains the task list e.g., it has operations to add/delete tasks in the list + */ +public class TaskList { + private List allTasks; //Contains all the current tasks + /** + * Assigns allTasks a value + * + * @param tasks assings the mem var a value of allTasks + */ + public TaskList(List tasks) { + this.allTasks = new ArrayList<>(tasks); + } + + /** + * another constructor, where the allTasks variable is just empty + */ + public TaskList() { + allTasks = new ArrayList<>(); + } + + /** + * Returns the list of tasks. + * + * @return the list of tasks. + */ + public List getAllTasks() { + return allTasks; + } +} diff --git a/src/main/java/duke/helpers/Ui.java b/src/main/java/duke/helpers/Ui.java new file mode 100644 index 0000000000..2cfac1c11d --- /dev/null +++ b/src/main/java/duke/helpers/Ui.java @@ -0,0 +1,85 @@ +package duke.helpers; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Scanner; + +import duke.errors.DukeException; +import duke.errors.FileAbsentException; + +/** + * deals with interactions with the user + */ +public class Ui { + private DukeException dukeException; //Contains the DukeException if it is thrown + private Scanner sc; //Used to scan the user input + + /** + * Constructor that assigns Scanner sc to Scanner object, to scan values given by user + */ + public Ui() { + sc = new Scanner(System.in); + } + + /** + * constructor that assigns variables with respective values + * + * @param file assigned to variable file + * @throws DukeException if file is absent at String file given + */ + public Ui(String file) throws DukeException { + try { + sc = new Scanner(new File(file)); + } catch (FileNotFoundException f) { + sc = new Scanner(System.in); + throw new FileAbsentException(file); + } + + } + + public void setDukeException(DukeException dukeException) { + this.dukeException = dukeException; + } + /** + * This prints out if there is an error when tasks are loaded + */ + public void showLoadingError() { + System.out.println(dukeException.getMessage()); + } + /** + * prints welcome message + */ + public void showWelcome() { + System.out.println(" ____________________________________________________________\n" + + " Hello! I'm Duke\n" + " What can I do for you?"); + } + + /** + * This prints the ____ for easier readability + */ + public void showLine() { + System.out.println(" ____________________________________________________________\n"); + } + + /** + * This prints the next line of code to execute if it exists + * + * @return the string of command + */ + public String readCommand() { + if (sc.hasNextLine()) { + return sc.nextLine(); + } else { + return null; + } + } + + /** + * Prints out the error + * + * @param s s is the error that is printed + */ + public void showError(String s) { + System.out.println(s); + } +} diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java new file mode 100644 index 0000000000..404dcda1f3 --- /dev/null +++ b/src/main/java/duke/tasks/Deadline.java @@ -0,0 +1,199 @@ +package duke.tasks; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import duke.errors.DeadlineException; +import duke.errors.DukeException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + +/** + * The deadline is a subclass of Task and it is used to describe tasks that has to be completed by a specific day. + */ +public class Deadline extends Task { + private String dayAndOrTime; //the day and or time that Task deadline had to be completed by + private LocalDate deadlineInDate; + private LocalDateTime deadlineInDateAndTime; + private LocalTime deadlineInTime; + + /** + * Assigns the name, dayAndOrTime, deadlineInDate with values initializes Deadline task + * + * @param name assigns this.name with value + * @param dayAndOrTime assigns this.dayAndOrTime with value(String format of deadlineInDate) + * @param deadlineInDate assigns this.deadlineInDate with value + */ + private Deadline(String name, String dayAndOrTime, LocalDate deadlineInDate) { + super(name); + this.dayAndOrTime = dayAndOrTime; + this.deadlineInDate = deadlineInDate; + } + + /** + * Assigns the name, dayAndOrTime, deadlineInDateAndTime with values initializes Deadline task + * + * @param name assigns this.name with value + * @param dayAndOrTime assigns this.dayAndOrTime with value(String format of deadlineInDateAndTime) + * @param deadlineInDateAndTime assigns this.deadlineInDate with value + */ + public Deadline(String name, String dayAndOrTime, LocalDateTime deadlineInDateAndTime) { + super(name); + this.dayAndOrTime = dayAndOrTime; + this.deadlineInDateAndTime = deadlineInDateAndTime; + } + + /** + * Assigns the name, dayAndOrTime, deadlineInDate with values initializes Deadline task + * + * @param name assigns this.name with value + * @param dayAndOrTime assigns this.dayAndOrTime with value(String format of deadlineInTime) + * @param deadlineInTime assigns this.deadlineInDate with value + */ + public Deadline(String name, String dayAndOrTime, LocalTime deadlineInTime) { + super(name); + this.dayAndOrTime = dayAndOrTime; + this.deadlineInTime = deadlineInTime; + } + /** + * Takes no arguments and overrides the toString method + * + * @return the specific representation for deadline class as mentioned with [D] + * indicating that it is a deadline class + * and also mentions the deadline. + */ + public String toString() { + return "[D]" + super.toString() + "(by: " + this.dayAndOrTime + ")"; + } + + /** + * Gives a specific string representation for that in the tasks.txt file and overrides that in Task to make + * it unique to that for Deadline + * + * @return the string representation + */ + public String inputListFormat() { + String s = super.inputListFormat(); + return "D" + super.inputListFormat() + "| " + this.dayAndOrTime; //format of Tasks to appear in file in Storage + } + + /** + * Used to add a deadline task to tasks in TaskList and update the tasks file in storage + * + * @param tasks contains all the current Tasks in a list, and to update this + * @param ui to set its DukeException if it is thrown + * @param storage contains all the current Tasks in a file, and to update this + * @param userInput String of the user input + * @return String message that Deadline has been added successfully + * @throws DukeException thrown if error is present in user input + */ + public static String addDeadlineTask(TaskList tasks, Ui ui, Storage storage, String userInput) + throws DukeException { + try { + String[] dataSplit = splitData(userInput); + //Split userInput into description name and time and/or date + Deadline d = deadlineTask(dataSplit[0], dataSplit[1]); //gives the Deadline + return updateTaskList(storage, d, tasks); //updates the tasks and file in storage + } catch (DukeException dukeException) { + ui.setDukeException(dukeException); + throw dukeException; + } + } + /** + * splits the userInput into Deadline description and the Deadline date and/ or time. + * If the date and/or time is absent then DeadlineException is thrown. + * + * @return the String array where the first String is the name of the Deadline and the second is the date + * and/or time of deadline + * @throws DeadlineException thrown when the time and/or date is absent. + */ + private static String[] splitData(String userInput) throws DeadlineException { + String s = ""; + int index = -1; + boolean time = false; + for (int i = 8; i < userInput.length(); i++) { + if (userInput.charAt(i) == '/') { + index = i; + time = true; //since date appears after + break; + } + s = s + userInput.charAt(i); + } + if (!time) { + throw new DeadlineException(false, false, true); + } + assert !s.substring(1, s.length() - 1).contains("/"); // description should not contain / + assert !userInput.substring(index + 4).contains("/by"); ////date and/or time should not contain /at + String[] dataSplit = new String[]{s.substring(1, s.length() - 1), userInput.substring(index + 4)}; + return dataSplit; + } + + /** + * This method creates a deadline task by checking whether the date and/or time given is in the correct + * format. If it is then Deadline task is returned else, DeadlineException is returned. + * + * @param name description of Deadline task + * @param dateTime gives the dateTime, to check whether they are in the correct format + * @return deadline if the dateTime is in the correct format + * @throws DeadlineException if the dateTime is in the incorrect format + */ + private static Deadline deadlineTask(String name, String dateTime) throws DeadlineException { + Deadline e; + try { + LocalDate parsedDate = stringToLocalDate(dateTime); //converts string to date + e = new Deadline(name, parsedDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy")), parsedDate); + } catch (DateTimeException d) { + try { + LocalDateTime parsedDate = stringToLocalDateTime(dateTime); //converts string to date and time + e = new Deadline(name, parsedDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm")), + parsedDate); + } catch (DateTimeException g) { + try { + LocalTime parsedDate = stringToLocalTime(dateTime); //converts string to date + e = new Deadline(name, parsedDate.format(DateTimeFormatter.ofPattern("HH:mm")), parsedDate); + } catch (DateTimeException f) { + throw new DeadlineException(false, true, false); + } + } + } + return e; + } + /** + * This method creates a deadline task by checking whether the date and/or time given is in the correct + * format. If it is then Deadline task is returned else, DeadlineException is returned. + * + * @param name description of Deadline task + * @param dateTime gives the dateTime, to check whether they are in the correct format + * @param done true if task is done and false if not done and is assigned to isDone variable + * @return deadline if the dateTime is in the correct format + * @throws DeadlineException if the dateTime is in the incorrect format + */ + public static Deadline deadlineTask(String name, String dateTime, boolean done) throws DeadlineException { + Deadline e; + try { + LocalDate parsedDate = stringToLocalDateExistingTask(dateTime); //converts string to date + e = new Deadline(name, parsedDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy")), parsedDate); + } catch (DateTimeException d) { + try { + LocalDateTime parsedDate = stringToLocalDateTimeExistingTask(dateTime); + //converts string to date and time + e = new Deadline(name, parsedDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm")), + parsedDate); + } catch (DateTimeException g) { + try { + LocalTime parsedDate = stringToLocalTimeExistingTask(dateTime); //converts string to date + e = new Deadline(name, parsedDate.format(DateTimeFormatter.ofPattern("HH:mm")), parsedDate); + } catch (DateTimeException f) { + throw new DeadlineException(false, true, false); + } + } + } + e.setDone(done); + return e; + } + +} diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java new file mode 100644 index 0000000000..3e08ab7f35 --- /dev/null +++ b/src/main/java/duke/tasks/Event.java @@ -0,0 +1,294 @@ +package duke.tasks; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import duke.errors.DeadlineException; +import duke.errors.DukeException; +import duke.errors.EventException; +import duke.helpers.Storage; +import duke.helpers.TaskList; +import duke.helpers.Ui; + + +/** + * The Event is a subclass of Task and it is used to describe tasks that has to be completed by a specific day and time + */ +public class Event extends Task { + private String startDateAndOrTime; //the start date and/or time of the event + private String endDateAndOrTime; //the end date and/or time of the event + private LocalDate startDate; + private LocalDate endDate; + private LocalDateTime startDateTime; + private LocalDateTime endDateTime; + private LocalTime startTime; + private LocalTime endTime; + /** + * Assigns the name, done and day variables with values and used to initialize Event task + * + * @param name super(name) so that it does whatever is mentioned in the parent class + * @param start assigns this.dayTime to dayTime value + */ + public Event(String name, String start, String end) { + super(name); + this.startDateAndOrTime = start; + this.endDateAndOrTime = end; + } + + /** + * Assigns the name, starDateAndOrTime, endDateAndOrTime, startDate, endDate with values and initializes Event task + * + * @param name assigned to super.name(name of deadline) + * @param startDateAndOrTime assigned to this.startDateAndOrTime + * @param endDateAndOrTime assigned to this.endDateAndOrTime + * @param startDate assigned to this.startDate + * @param endDate assigned to this.endDate + */ + public Event(String name, String startDateAndOrTime, String endDateAndOrTime, LocalDate startDate, + LocalDate endDate) { + super(name); + this.startDateAndOrTime = startDateAndOrTime; + this.endDateAndOrTime = endDateAndOrTime; + this.startDate = startDate; + this.endDate = endDate; + } + + /** + * Assigns the name, starDateAndOrTime, endDateAndOrTime, startDateTime, + * endDateTime with values and initializes Event task + * + * @param name assigned to super.name(name of deadline) + * @param startDateAndOrTime assigned to this.startDateAndOrTime + * @param endDateAndOrTime assigned to this.endDateAndOrTime + * @param startDateTime assigned to this.startDateTime + * @param endDateTime assigned to this.endDateTime + */ + public Event(String name, String startDateAndOrTime, String endDateAndOrTime, LocalDateTime startDateTime, + LocalDateTime endDateTime) { + super(name); + this.startDateAndOrTime = startDateAndOrTime; + this.endDateAndOrTime = endDateAndOrTime; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + } + + /** + * Assigns the name, starDateAndOrTime, endDateAndOrTime, startTime, endTime with values and initializes Event task + * + * @param name assigned to super.name(name of deadline) + * @param startDateAndOrTime assigned to this.startDateAndOrTime + * @param endDateAndOrTime assigned to this.endDateAndOrTime + * @param startTime assigned to this.startTime + * @param endTime assigned to this.endTime + */ + public Event(String name, String startDateAndOrTime, String endDateAndOrTime, LocalTime startTime, + LocalTime endTime) { + super(name); + this.startDateAndOrTime = startDateAndOrTime; + this.endDateAndOrTime = endDateAndOrTime; + this.startTime = startTime; + this.endTime = endTime; + } + /** + * Overrides the toString methods + * + * @return the specific representation for Event class as mentioned with [E] indicating that it is a Event class + * * and also mentions the Event. + */ + public String toString() { + return "[E]" + super.toString() + "(at: " + this.startDateAndOrTime + "-" + this.endDateAndOrTime + ")"; + } + + /** + * Gives a specific string representation for that in the tasks.txt file and overrides that in Task to make + * it unique to that for Event + * + * @return the string representation + */ + public String inputListFormat() { + return "E" + super.inputListFormat() + "| " + this.startDateAndOrTime + "-" + this.endDateAndOrTime; + //format of Tasks to appear in file in Storage + } + public static String addEventTask(TaskList tasks, Ui ui, Storage storage, String commandDescription) + throws DukeException { + return process(tasks, storage, commandDescription); + } + /** + * Returns String if the user input is correct and throws exception otherwise + * + * @param tasks used to add Event into tasks if user input is correct + * @param storage used to update file in storage that contains file if user input is correct + * @return String informing user that Event has been added to list + * @throws DukeException thrown if user input is wrong + */ + private static String process(TaskList tasks, Storage storage, String commandDescription) throws DukeException { + String[] dataSplit = splitData(commandDescription); + //splits String into different of Event name, start and end time and/or date + Event event = eventTask(dataSplit[0], dataSplit[1], dataSplit[2]); + //gives the event or throws exception + return updateTaskList(storage, event, tasks); + //updates the Task list in Storage and TaskList since the Event is added + } + /** + * splits the data into Deadline description and the Deadline date and/ or time. If the date and/or time is absent + * then DeadlineException is thrown. + * + * @return the String array where the first String is the name of the Deadline + * and the second is the date and/or time + * of start for event, third is the date and/or time of end for event. + * @throws EventException thrown when the time and/or date is absent. + */ + private static String[] splitData(String commandDescription) throws EventException { + String s = ""; + int index = -1; + int end = -1; + boolean startPresent = false; + boolean endPresent = false; + String start = ""; + for (int i = 5; i < commandDescription.length(); i++) { + if (commandDescription.charAt(i) == '/') { + index = i; + startPresent = true; //the presence of / indicates that the start time is present. + break; + } + s = s + commandDescription.charAt(i); + } + for (int i = index + 1; i < commandDescription.length(); i++) { + if (commandDescription.charAt(i) == '-' && i != commandDescription.length() - 1) { + end = i; + endPresent = true; // - indicates that end time is present + break; + } + start = start + commandDescription.charAt(i); + } + if (!startPresent) { + throw new EventException(false, false, false, false, true); + } + if (!endPresent) { + throw new EventException(false, true, false, false, false); + } + String[] dataSplit = new String[3]; + + assert !s.substring(1, s.length() - 1).contains("/"); //description cannot contain + assert !commandDescription.substring(index + 4).contains("/at"); //start date and/or time cannot contain /at + assert !commandDescription.substring(end + 1).contains("-"); //end date and/or time cannot contain - + + + dataSplit[0] = s.substring(1, s.length() - 1); + dataSplit[1] = commandDescription.substring(index + 4, end); + dataSplit[2] = commandDescription.substring(end + 1); + return dataSplit; + } + /** + * This method creates a deadline task by checking whether the date and/or time given is in the correct + * format. If it is then Deadline task is returned else, DeadlineException is returned. + * + * @param name description of Deadline task + * @param start gives the dateTime of the start to check whether they are in the correct format + * @param end gives the dateTime of the end to check whether they are in the correct format + * @return deadline if the dateTime is in the correct format + * @throws DeadlineException if the dateTime is in the incorrect format + */ + private static Event eventTask(String name, String start, String end) throws DukeException { + Event e; + try { + LocalDate startDate = stringToLocalDateExistingTask(start); //converts start to date + LocalDate endDate = stringToLocalDateExistingTask(end); //converts end to date + + if (startDate.isAfter(endDate)) { + throw new EventException(false, false, true, false, false); //if start > end then it throws this error. + } + e = new Event(name, startDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy")), + endDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy")), startDate, endDate); + } catch (EventException event) { + throw new EventException(false, false, true, false, false); + } catch (DateTimeException d) { + try { + LocalDateTime startDateTime = stringToLocalDateTimeExistingTask(start); //converts start to dateTime + LocalDateTime endDateTime = stringToLocalDateTimeExistingTask(end); //converts end to dateTime + if (startDateTime.isAfter(endDateTime)) { + throw new EventException(false, false, true, false, false); //if start > end it throws this error + } + e = new Event(name, startDateTime.format(DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm")), + endDateTime.format(DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm")), + startDateTime, endDateTime); + } catch (EventException event) { + throw new EventException(false, false, true, false, false); + } catch (DateTimeException g) { + try { + LocalTime startTime = stringToLocalTimeExistingTask(start); //converts start to time + LocalTime endTime = stringToLocalTimeExistingTask(end); //converts end to time + if (startTime.isAfter(endTime)) { + throw new EventException(false, false, true, false, false); //if start > end it throws error + } + e = new Event(name, startTime.format(DateTimeFormatter.ofPattern("HH:mm")), + endTime.format(DateTimeFormatter.ofPattern("HH:mm")), startTime, endTime); + } catch (EventException y) { + throw new EventException(false, false, true, false, false); + } catch (DateTimeException z) { + throw new EventException(false, false, false, true, false); + } + } + } + return e; + } + + /** + * This method creates a deadline task by checking whether the date and/or time given is in the correct + * format. If it is then Deadline task is returned else, DeadlineException is returned. + * + * @param name description of Deadline task + * @param start gives the dateTime of the start to check whether they are in the correct format + * @param end gives the dateTime of the end to check whether they are in the correct format + * @param isDone true if task is done and false if not done and is assigned to isDone variable + * @return deadline if the dateTime is in the correct format + * @throws DeadlineException if the dateTime is in the incorrect format + */ + public static Event eventTask(String name, String start, String end, boolean isDone) throws DukeException { + Event e; + try { + LocalDate startDate = stringToLocalDateExistingTask(start); //converts start to date + LocalDate endDate = stringToLocalDateExistingTask(end); //converts end to date + + if (startDate.isAfter(endDate)) { + throw new EventException(false, false, true, false, false); //if start > end then it throws this error. + } + e = new Event(name, startDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy")), + endDate.format(DateTimeFormatter.ofPattern("dd LLL yyyy")), startDate, endDate); + } catch (EventException event) { + throw new EventException(false, false, true, false, false); + } catch (DateTimeException d) { + try { + LocalDateTime startDateTime = stringToLocalDateTimeExistingTask(start); //converts start to dateTime + LocalDateTime endDateTime = stringToLocalDateTimeExistingTask(end); //converts end to dateTime + if (startDateTime.isAfter(endDateTime)) { + throw new EventException(false, false, true, false, false); //if start > end it throws this error + } + e = new Event(name, startDateTime.format(DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm")), + endDateTime.format(DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm")), + startDateTime, endDateTime); + } catch (EventException event) { + throw new EventException(false, false, true, false, false); + } catch (DateTimeException g) { + try { + LocalTime startTime = stringToLocalTimeExistingTask(start); //converts start to time + LocalTime endTime = stringToLocalTimeExistingTask(end); //converts end to time + if (startTime.isAfter(endTime)) { + throw new EventException(false, false, true, false, false); //if start > end it throws error + } + e = new Event(name, startTime.format(DateTimeFormatter.ofPattern("HH:mm")), + endTime.format(DateTimeFormatter.ofPattern("HH:mm")), startTime, endTime); + } catch (EventException y) { + throw new EventException(false, false, true, false, false); + } catch (DateTimeException z) { + throw new EventException(false, false, false, true, false); + } + } + } + e.setDone(isDone); + return e; + } +} diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java new file mode 100644 index 0000000000..06236e86fb --- /dev/null +++ b/src/main/java/duke/tasks/Task.java @@ -0,0 +1,208 @@ +package duke.tasks; + +import java.io.FileWriter; +import java.io.IOException; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import duke.errors.DukeException; +import duke.errors.FileAbsentException; +import duke.helpers.Storage; +import duke.helpers.TaskList; + +/** + * This Task is made abstract because it is never initialized in the actual code, however, + * it is used so that polymorphism + * is able to work properly. + */ +public abstract class Task { + /** + * Tasks is made static because it contains the different tasks that are added, and therefore it is not limited to + * a single instance of Task + */ + private boolean isDone; //true if Task is completed, false otherwise + private String name; //gives name of the Task + /** + * constructor assigns name variable a value + * + * @param name this assigns the name of the Task to the name being given in the constructor + */ + Task(String name) { + this.isDone = false; + this.name = name; + } + + /** + * constructor assigns name and done a value. + * + * @param name assigns name to this.name + * @param isDone assigns name to this.done + */ + Task(String name, boolean isDone) { + this.isDone = isDone; + this.name = name; + } + /** + * gives name of task + * + * @return name of task + */ + public String getName() { + return name; + } + + /** + * Returns value isDone of a task + * + * @return true if task is done, false otherwise + */ + public boolean getIsDone() { + return isDone; + } + /** + * setter that sets Done to the done value stated + * + * @param done value given to set it to done var + */ + public void setDone(boolean done) { + this.isDone = done; + } + + /** + * Overrides the toString methods + * + * @return String which contains info on task name as well as whether it is completed(tick sign) or not(cross sign). + */ + public String toString() { + if (this.isDone) { + return "[" + "\u2713" + "] " + this.name; //\u2713 is a tick, denoting done + } else if (!this.isDone) { + return "[" + "\u2717" + "] " + this.name; //\u2717 is a cross, deonoting not done + } else { + return "default"; + } + } + + /** + * Gives a specific string representation for that in the tasks.txt file + * + * @return the string representation + */ + public String inputListFormat() { //format of Tasks to appear in file in Storage + if (this.isDone) { + return " | 1 | " + this.name; //1 denotes done + } else if (!this.isDone) { + return " | 0 | " + this.name; //0 denotes not done + } else { + return "default"; + } + } + /** + * converts string to date + * + * @param string string to convert to date + * @return local date which is converted from string, if cannot then DateTimeException thrown + */ + protected static LocalDate stringToLocalDate(String string) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd"); + LocalDate parsedDate = LocalDate.parse(string, formatter); //converts string to date + return parsedDate; + } catch (DateTimeException d) { + throw d; + } + } + /** + * converts string to dateTime + * + * @param string string to convert to dateTime + * @return local dateTime which is converted from string, if cannot then DateTimeException thrown + */ + protected static LocalDateTime stringToLocalDateTime(String string) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd, HH:mm"); + LocalDateTime parsedDate = LocalDateTime.parse(string, formatter); //converts string to date and time + return parsedDate; + } catch (DateTimeException g) { + throw g; + } + } + + /** + * converts string to time + * + * @param string string to convert to time + * @return local time which is converted from string, if cannot then DateTimeException thrown + */ + protected static LocalTime stringToLocalTime(String string) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); + LocalTime parsedDate = LocalTime.parse(string, formatter); //converts string to time + return parsedDate; + } catch (DateTimeException f) { + throw f; + } + } + protected static LocalDate stringToLocalDateExistingTask(String string) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd LLL yyyy"); + LocalDate parsedDate = LocalDate.parse(string, formatter); //converts string to time + return parsedDate; + } catch (DateTimeException f) { + throw f; + } + } + protected static LocalDateTime stringToLocalDateTimeExistingTask(String string) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd LLL yyyy, HH:mm"); + LocalDateTime parsedDate = LocalDateTime.parse(string, formatter); //converts string to time + return parsedDate; + } catch (DateTimeException f) { + throw f; + } + } + protected static LocalTime stringToLocalTimeExistingTask(String string) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); + LocalTime parsedDate = LocalTime.parse(string, formatter); //converts string to time + return parsedDate; + } catch (DateTimeException f) { + throw f; + } + } + /** + * adds the task to list of task in taskList and into the file in storage + * + * @param storage where the file here is updated + * @param task this task is added into storage and taskList + * @param taskList where the tasks here is updated with task added + * @throws DukeException when the file in storage is not present + */ + protected static String updateTaskList(Storage storage, Task task, TaskList taskList) throws DukeException { + try { + FileWriter fw = new FileWriter(storage.getFilePath(), true); + //updates the file in storage as new task is added + taskList.getAllTasks().add(task); + fw.write(task.inputListFormat() + "\n"); + fw.close(); + } catch (IOException i) { + throw new FileAbsentException(storage.getFilePath()); + } + return stringToUpdateTaskList(task, taskList); + } + + /** + * Gives a String saying that the task list has been updated + * + * @param task to be added into taskList + * @param taskList where task is added + * @return String that informs task is added into taskList + */ + protected static String stringToUpdateTaskList(Task task, TaskList taskList) { + return " Got it. I've added this task:\n " + task.toString() + "\n" + //Task added message + " Now you have " + taskList.getAllTasks().size() + " tasks in the list."; + } +} diff --git a/src/main/java/duke/tasks/ToDo.java b/src/main/java/duke/tasks/ToDo.java new file mode 100644 index 0000000000..4e949ffe11 --- /dev/null +++ b/src/main/java/duke/tasks/ToDo.java @@ -0,0 +1,42 @@ +package duke.tasks; + +/** + * The ToDo is a subclass of Task and it is used to describe tasks that have no specific deadline + */ + +public class ToDo extends Task { + /** + * Constructor assigns name to name value and initialize ToDO task + * + * @param name super(name) so that it does whatever is mentioned in the parent class + */ + public ToDo(String name) { + super(name); + } + + /** + * Constructor assigns name and done to values and initialize ToDO task + * + * @param name assigns name to this.name + * @param done assigns done to this.done + */ + public ToDo(String name, boolean done) { + super(name, done); + } + + /** + * Takes no arguments and overrides the toString method + * + * @return the specific representation for ToDo class as mentioned with [T] indicating that it is a ToDo class + * + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } + + + public String inputListFormat() { + return "T" + super.inputListFormat(); //format of Tasks to appear in file in Storage + } +} diff --git a/src/main/java/expected.txt b/src/main/java/expected.txt new file mode 100644 index 0000000000..37e22c7a90 --- /dev/null +++ b/src/main/java/expected.txt @@ -0,0 +1,57 @@ + Task file is empty! + ____________________________________________________________ + Hello! I'm Duke + What can I do for you? + ____________________________________________________________ + + ____________________________________________________________ + + '☹' OOPS!!! Start should be less than end. + ____________________________________________________________ + + ____________________________________________________________ + + Got it. I've added this task: + [T][✗] buy book + Now you have 1 tasks in the list. + ____________________________________________________________ + + ____________________________________________________________ + + Nice! I've marked this task as done: + [T][✓] buy book + ____________________________________________________________ + + ____________________________________________________________ + + '☹' OOPS!!! Start should be less than end. + ____________________________________________________________ + + ____________________________________________________________ + + '☹' OOPS!!! The ID is not yet defined. + ____________________________________________________________ + + ____________________________________________________________ + + Got it. I've added this task: + [D][✗] submission(by: 14 Nov 2020) + Now you have 2 tasks in the list. + ____________________________________________________________ + + ____________________________________________________________ + +[T][✓] buy book +[D][✗] submission(by: 14 Nov 2020) + ____________________________________________________________ + + ____________________________________________________________ + + '☹ OOPS!!! I'm sorry, but I don't know what that means :-( + ____________________________________________________________ + + ____________________________________________________________ + + Bye. Hope to see you again soon! + ____________________________________________________________ + diff --git a/src/main/java/input.txt b/src/main/java/input.txt new file mode 100644 index 0000000000..cf55267da8 --- /dev/null +++ b/src/main/java/input.txt @@ -0,0 +1,9 @@ +event concert /at 12:00-10:00 +todo buy book +done 1 +event book club /at 12:00-10:00 +delete 2 +deadline submission /by 2020 11 14 +list +blah +bye diff --git a/src/main/java/output.txt b/src/main/java/output.txt new file mode 100644 index 0000000000..593dd101f0 --- /dev/null +++ b/src/main/java/output.txt @@ -0,0 +1,57 @@ + Task file is empty! + ____________________________________________________________ + Hello! I'm Duke + What can I do for you? + ____________________________________________________________ + + ____________________________________________________________ + + '☹' OOPS!!! Start should be less than end. + ____________________________________________________________ + + ____________________________________________________________ + + Got it. I've added this task: + [T][✗] buy book + Now you have 1 tasks in the list. + ____________________________________________________________ + + ____________________________________________________________ + + Nice! I've marked this task as done: + [T][✓] buy book + ____________________________________________________________ + + ____________________________________________________________ + + '☹' OOPS!!! Start should be less than end. + ____________________________________________________________ + + ____________________________________________________________ + + '☹' OOPS!!! The ID is not yet defined. + ____________________________________________________________ + + ____________________________________________________________ + + Got it. I've added this task: + [D][✗] submission(by: 14 Nov 2020) + Now you have 2 tasks in the list. + ____________________________________________________________ + + ____________________________________________________________ + + [T][✓] buy book + [D][✗] submission(by: 14 Nov 2020) + ____________________________________________________________ + + ____________________________________________________________ + + '☹ OOPS!!! I'm sorry, but I don't know what that means :-( + ____________________________________________________________ + + ____________________________________________________________ + + Bye. Hope to see you again soon! + ____________________________________________________________ + diff --git a/src/main/java/shortCuts.txt b/src/main/java/shortCuts.txt new file mode 100644 index 0000000000..ea08d51874 --- /dev/null +++ b/src/main/java/shortCuts.txt @@ -0,0 +1,7 @@ +by bye +li list +to todo +dead deadline +fi find +tod todo +x bye diff --git a/src/main/java/tasks.txt b/src/main/java/tasks.txt new file mode 100644 index 0000000000..a2a6b3d13c --- /dev/null +++ b/src/main/java/tasks.txt @@ -0,0 +1,4 @@ +T | 1 | buy book +E | 1 | concert | 12:00-14:00 +T | 0 | read book +T | 0 | read book diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100755 index 0000000000..ed8c75fdfa 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 100755 index 0000000000..1ad5098c89 Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..a9fc8e054c --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..9b910aa6fa --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,20 @@ + + + + + + + + + + + + +