diff --git a/README.md b/README.md index 8715d4d9..63d0ad6b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# Duke.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. @@ -13,7 +13,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. If there are any further prompts, accept the defaults. 1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: +3. After that, locate the `src/main/java/Duke.Duke.java` file, right-click it, and choose `Run Duke.Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: ``` Hello from ____ _ diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..73b1f710 --- /dev/null +++ b/build.gradle @@ -0,0 +1,46 @@ +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' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "Duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +run{ + standardInput = System.in +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f3d88b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b7c8c5db --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..2fe81a7d --- /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 00000000..62bd9b9c --- /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 5d313334..00000000 --- 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/Duke/Checker/FileLineChecker.java b/src/main/java/Duke/Checker/FileLineChecker.java new file mode 100644 index 00000000..d887cf04 --- /dev/null +++ b/src/main/java/Duke/Checker/FileLineChecker.java @@ -0,0 +1,82 @@ +package Duke.Checker; + +/** + * FileLine Checker contains methods to check whether + * the lines from the files + * contains valid task information. + */ +public class FileLineChecker { + + /** + * Takes a string array and checks whether + * it contains parts with valid information of a "to do" object. + * @param parts + * @return boolean + */ + public static boolean isValidTodoLine(String[] parts) { + assert(parts[0].trim().equals("T")); + if (parts.length != 4) { + return false; + } else if (parts[2].trim().equals("")) { + return false; + } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) { + return false; + } + return true; + } + + /** + * Takes a string array and checks whether + * it contains parts with valid information of a "deadline" object. + * @param parts + * @return boolean + */ + public static boolean isValidDeadlineLine(String[] parts) { + assert(parts[0].trim().equals("D")); + if (parts.length != 5) { + return false; + } else if (parts[2].trim().equals("") || parts[3].trim().equals("")) { + return false; + } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) { + return false; + } + return true; + } + + /** + * Takes a string array and checks whether + * it contains parts with valid information of an "event" object. + * @param parts + * @return boolean + */ + public static boolean isValidEventLine(String[] parts) { + assert(parts[0].trim().equals("E")); + if (parts.length != 5) { + return false; + } else if (parts[2].trim().equals("") || parts[3].trim().equals("")) { + return false; + } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) { + return false; + } + return true; + } + + /** + * Takes a string array and checks whether + * it contains parts with valid information of a "dowithin" object. + * @param parts + * @return + */ + public static boolean isValidDoWithinLine(String[] parts) { + assert(parts[0].trim().equals("W")); + if (parts.length != 6) { + return false; + } else if (parts[2].trim().equals("") || parts[3].trim().equals("") + || parts[4].trim().equals("")) { + return false; + } else if (!parts[1].trim().equals("1") && !parts[1].trim().equals("0")) { + return false; + } + return true; + } +} diff --git a/src/main/java/Duke/Checker/InputChecker.java b/src/main/java/Duke/Checker/InputChecker.java new file mode 100644 index 00000000..2bf08022 --- /dev/null +++ b/src/main/java/Duke/Checker/InputChecker.java @@ -0,0 +1,175 @@ +package Duke.Checker; +import Duke.DukeLogic.DukeException; + +/** + * Checks whether the input by the Duke user + * contains valid task information. + */ +public class InputChecker { + + /** + * Check whether input contains all the parts of a "to do" object. + * Returns true if it contains all the respective parts. + * @param input + * @return boolean + * @throws DukeException + */ + public static boolean isValidTodoInput(String input) throws DukeException { + assert(input.startsWith("todo ")); + if (input.length() < 5) { + throw new DukeException("☹ OOPS!!! The description of a todo cannot be empty."); + } else if (input.substring(4).trim().equals("")) { + throw new DukeException("☹ OOPS!!! The description of a todo cannot be empty."); + } else { + return true; + } + } + + /** + * Checks whether input contains all that parts of a "deadline" object. + * Returns true if it contains all the respective parts. + * @param input + * @return boolean + * @throws DukeException + */ + public static boolean isValidDeadlineInput(String input) throws DukeException { + assert(input.startsWith("deadline ")); + if (input.contains("/by")) { + String[] parts = input.substring(8).split("/by"); + if (parts.length != 2) { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding deadline."); + } else if (parts[0].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The task description of a deadline cannot be empty."); + } else if (parts[1].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The due date/time of a deadline cannot be empty."); + } else { + return true; + } + } else { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding deadline."); + } + } + + /** + * Checks whether input contains all that parts of an "event" object. + * Returns true if it contains all the respective parts. + * @param input + * @return boolean + * @throws DukeException + */ + public static boolean isValidEventInput(String input) throws DukeException { + assert(input.startsWith("event ")); + if (input.contains("/at")) { + String[] parts = input.substring(5).split("/at"); + if (parts.length != 2) { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding event."); + } else if (parts[0].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The task description of an event cannot be empty."); + } else if (parts[1].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The date/time of an event cannot be empty."); + } else { + return true; + } + } else { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding event."); + } + } + + /** + * Checks whether input contains all that parts of a "dowithin" object. + * Returns true if it contains all the respective parts. + * @param input + * @return boolean + * @throws DukeException + */ + public static boolean isValidDoWithinInput(String input) throws DukeException { + assert(input.startsWith("dowithin ")); + if (input.contains("/start")) { + String[] parts = input.substring(8).split("/start"); + String[] startEnd; + if (parts[1].contains("/end")) { + startEnd = parts[1].split("/end"); + } else { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin."); + } + if (parts.length != 2) { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin."); + } else if (parts[0].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The task description of " + + "an dowithin cannot be empty."); + } + if (startEnd.length != 2) { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin."); + } else if (startEnd[0].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The start date/time " + + "of a dowithin cannot be empty."); + } else if (startEnd[1].trim().equals("")) { + throw new DukeException("☹ OOPS!!! The end date/time " + + "of a dowithin cannot be empty."); + } + return true; + } else { + throw new DukeException("☹ OOPS!!! Invalid syntax for adding dowithin."); + } + } + + /** + * Checks whether input contains the enough and correct information + * to mark a task as done. + * @param input + * @return boolean + * @throws DukeException + */ + public static boolean isValidDoneInput(String input) throws DukeException { + assert(input.startsWith("done ")); + if (input.length() < 5) { + throw new DukeException("☹ OOPS!!! The index of the task to be marked as done is missing."); + } else { + try { + int index = Integer.parseInt(input.substring(4).trim()) - 1; + } catch (NumberFormatException e) { + throw new DukeException("☹ OOPS!!! " + + "The index of the task to mark as done has to be an integer!"); + } + return true; + } + } + + /** + * Checks whether input contains the enough and correct information + * to delete a task. + * @param input + * @return boolean + * @throws DukeException + */ + public static boolean isValidDeleteInput(String input) throws DukeException { + assert(input.startsWith("delete ")); + if (input.length() < 7) { + throw new DukeException("☹ OOPS!!! The index of the task to delete is missing."); + } else { + try { + int test = Integer.parseInt(input.substring(6).trim()) - 1; + } catch (NumberFormatException e) { + throw new DukeException("☹ OOPS!!! " + + "The index of the task to delete has to be an integer!"); + } + return true; + } + } + + /** + * Checks whether input contains enough and correct information + * to find a task with a keyword. + * @param parts + * @return + * @throws DukeException + */ + public static boolean isValidFindInput(String[] parts) throws DukeException { + assert(parts[0].trim().equals("find")); + if (parts.length != 2) { + throw new DukeException("☹ OOPS!!! The keyword to find is missing."); + } else { + return true; + } + } +} diff --git a/src/main/java/Duke/Duke.java b/src/main/java/Duke/Duke.java new file mode 100644 index 00000000..310697cf --- /dev/null +++ b/src/main/java/Duke/Duke.java @@ -0,0 +1,74 @@ +package Duke; + +import Duke.DukeLogic.DukeException; +import Duke.DukeLogic.Storage; +import Duke.Parser.*; +import Duke.DukeLogic.Ui; +import java.io.File; +import java.util.Scanner; + +/** + * Duke is a program that opens a storage file, + * parses the tasks in the file into a list in Duke + * and that allows users to do CRUD operations on tasks. + */ +public class Duke { + static Scanner in = new Scanner(System.in); + + /** + * Scans and returns user input as String. + * @return + */ + public static String getInput() { + String input = in.nextLine(); + return input; + } + + /** + * Starts Duke by opening storage file + * and updating the duke list using th information from the file. + * Then print the duke start up message and a greeting from the program. + */ + public static void runDuke() { + try { + File StorageFile = Storage.openStorageFile(); + Storage.readFileToArray(StorageFile); + } catch (DukeException e) { + e.printErrMsg(); + } + Ui.startDuke(); + Ui.greet(); + } + + /** + * Continuously read input from the user of Duke + * and parse the different inputs. + */ + public static void useDuke() { + while (true) { + String input = getInput(); + System.out.println(Ui.LINE); + if (input.equals("bye")) { + Ui.endDuke(); + break; + } else { + InputParser.parseInput(input); + } + System.out.println(Ui.LINE + "\n"); + } + } + + /** + * Runs the main logic of Duke program. + * @param args + */ + public static void main(String[] args) { + runDuke(); + useDuke(); + try { + Storage.writeListToFile(Storage.openStorageFile()); + } catch (DukeException e) { + System.out.println("Failed to write to file"); + } + } +} diff --git a/src/main/java/Duke/DukeLogic/DukeException.java b/src/main/java/Duke/DukeLogic/DukeException.java new file mode 100644 index 00000000..3d1da4bd --- /dev/null +++ b/src/main/java/Duke/DukeLogic/DukeException.java @@ -0,0 +1,31 @@ +package Duke.DukeLogic; + +/** + * An exception class that represents the errors that are thrown when using duke. + */ +public class DukeException extends Exception{ + protected String ErrMsg; + + /** + * DukeException constructor that creates a DukeException + * with an empty error message. + */ + public DukeException() { + this.ErrMsg = ""; + } + + /** + * DukeException constructor takes in a custom error message. + * @param invalid + */ + public DukeException(String invalid) { + this.ErrMsg = invalid; + } + + /** + * Prints the custom error message of a DukeException if any. + */ + public void printErrMsg() { + System.out.println(ErrMsg); + } +} diff --git a/src/main/java/Duke/DukeLogic/Storage.java b/src/main/java/Duke/DukeLogic/Storage.java new file mode 100644 index 00000000..839ac8ed --- /dev/null +++ b/src/main/java/Duke/DukeLogic/Storage.java @@ -0,0 +1,131 @@ +package Duke.DukeLogic; + +import Duke.Parser.FileLineParser; +import Duke.Models.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Storage class contains the methods related to the storage file. + */ +public class Storage { + + /** + * Creates a new file and directory. + * @return the new File object. + * @throws DukeException + */ + public static File createStorageFile() throws DukeException { + File newFile = new File("data\\Duke.DukeLogic.Storage.txt"); + try { + newFile.getParentFile().mkdirs(); + newFile.createNewFile(); + System.out.println("New Duke.DukeLogic.Storage.txt file created."); + return newFile; + } catch (IOException err) { + throw new DukeException("Failed to create new Duke.DukeLogic.Storage.txt file."); + } + } + + /** + * Opens and returns storage file. + * If a storage file does not exist, create one and return that. + * @return an opened File object. + * @throws DukeException + */ + public static File openStorageFile() throws DukeException { + File newFile = new File("data\\storage.txt"); + if (!newFile.exists()) { + try { + newFile = createStorageFile(); + } catch (DukeException e) { + throw new DukeException("Duke.Duke will run without " + + "any prior stored task information"); + } + } + return newFile; + } + + /** + * Takes a file and parse the contents into task objects + * The parsed task objects will then be added to Duke's task list. + * @param storageFile + */ + public static void readFileToArray(File storageFile) { + try { + Scanner s = new Scanner(storageFile); + while (s.hasNext()) { + try { + Task taskFromLine = FileLineParser.parseLineFromFile(s.nextLine()); + TaskList.addTaskToList(taskFromLine); + } catch (DukeException e) { + e.printErrMsg(); + } + } + } catch (FileNotFoundException e) { + System.out.println("File cannot be opened, " + + "Duke will not have any prior stored task information"); + } + } + + /** + * Retrieves information of all the tasks inside Duke task list + * and converts each into a string to store inside storage file. + * @param FileWrite + */ + public static void writeListToFile(File FileWrite) { + try { + FileWriter fw = new FileWriter(FileWrite, false); + ArrayList list = TaskList.getDukeList(); + for (int i = 0; i < list.size(); i++) { + Task taskAtIndex = list.get(i); + String taskType = taskAtIndex.getTaskType(); + String newLine = ""; + if (taskType.equals("T")) { + newLine = buildStorageLine(taskAtIndex); + } else if (taskType.equals("D")) { + newLine = buildStorageLine(taskAtIndex); + } else if (taskType.equals("E")) { + newLine = buildStorageLine(taskAtIndex); + } else if (taskType.equals("W")) { + newLine = buildStorageLine(taskAtIndex); + } + fw.write(newLine + System.getProperty("line.separator")); + } + fw.close(); + } catch (IOException e) { + System.out.println("Write to file failed"); + } + } + + /** + * Takes in a task object using it to build + * and return a string with the format of lines in storage file. + * @param targetTask + * @return a String with task information in storage line format. + */ + public static String buildStorageLine(Task targetTask) { + assert(targetTask != null); + String divider = " | "; + String initial = targetTask.getTaskType(); + String newLine = initial + divider; + if (targetTask.isCompleted()) { + newLine = newLine + "1" + divider; + } else { + newLine = newLine + "0" + divider; + } + newLine = newLine + targetTask.getDescription(); + + if (initial.equals("D") || initial.equals("E") || initial.equals("W")) { + newLine = newLine + divider + targetTask.getAdditionalInfo(); + } + + newLine = newLine + divider + targetTask.getPriority(); + + return newLine; + } +} diff --git a/src/main/java/Duke/DukeLogic/TaskList.java b/src/main/java/Duke/DukeLogic/TaskList.java new file mode 100644 index 00000000..bae15ed7 --- /dev/null +++ b/src/main/java/Duke/DukeLogic/TaskList.java @@ -0,0 +1,123 @@ +package Duke.DukeLogic; + +import Duke.Models.*; + +import java.util.ArrayList; + +/** + * Represents the list that contains the task inputted by using Duke. + * + */ +public class TaskList { + protected static ArrayList DukeList = new ArrayList<>(); + + public static ArrayList getDukeList() { + return DukeList; + } + + /** + * Returns the number of tasks inside the task list. + * @return int of the size of task list. + */ + public static int size() { + return DukeList.size(); + } + + /** + * Takes in a new Task + * and adds it to the task list. + * @param newEntry + */ + public static void addTaskToList(Task newEntry) { + assert(newEntry != null); + DukeList.add(newEntry); + } + + /** + * Takes in a index and removes the task at the index from the task list. + * @param index + */ + public static void removeTask(int index) { + assert(index < DukeList.size()); + DukeList.remove(index); + } + + /** + * Prints all the task in the task list with their information. + */ + public static void printTaskList() { + System.out.println("Here are the tasks in your list:"); + for (int i = 0; i < DukeList.size(); i++) { + System.out.println((i + 1) + "." + DukeList.get(i).getTaskInfo()); + } + } + + /** + * Takes in a valid "done" input and gets the index. + * If index is valid, marks the task at that index of the list as completed. + * + * @param input + * @throws DukeException + */ + public static void markTaskAtIndex(String input) throws DukeException { + int index = Integer.parseInt(input.substring(4).trim()) - 1; + if (index < DukeList.size() && index > -1) { + markTask(DukeList.get(index)); + System.out.println("Nice! I've marked this task as done:\n " + + DukeList.get(index).getTaskInfo()); + } else { + throw new DukeException("☹ OOPS!!! " + + "The index number of the task to be done is invalid!"); + } + } + + /** + * Mark the task given as a parameter as completed. + * @param inputTask + */ + public static void markTask(Task inputTask) { + assert(inputTask != null); + inputTask.markCompleted(); + } + + /** + * Takes in a valid "delete" input and gets the index. + * If index is valid, the task at that index of the list is deleted from the list. + * + * @param input + * @throws DukeException + */ + public static void deleteTaskAtIndex(String input) throws DukeException { + int index = Integer.parseInt(input.substring(6).trim()) - 1; + if (index < DukeList.size() && index > -1) { + String DeletedInfo = DukeList.get(index).getTaskInfo(); + removeTask(index); + System.out.println("Noted! I've removed this task:\n " + DeletedInfo); + Ui.printTaskCount(); + } else { + throw new DukeException("☹ OOPS!!! The index number of the task to delete is invalid!"); + } + } + + /** + * Prints tasks with description matching the input string. + * @param desc + */ + public static void printTaskWithDesc(String desc) { + assert(desc.trim() != ""); + int count = 0; + for (int i = 0; i < TaskList.size(); i++) { + Task currentTask = DukeList.get(i); + if (currentTask.getDescription().toLowerCase().contains(desc.toLowerCase())) { + if (count == 0) { + System.out.println("Here are the matching tasks in your list:"); + } + count++; + System.out.println(count + ". " + currentTask.getTaskInfo()); + } + } + if (count == 0) { + System.out.println("There are no matching tasks in your list."); + } + } +} diff --git a/src/main/java/Duke/DukeLogic/Ui.java b/src/main/java/Duke/DukeLogic/Ui.java new file mode 100644 index 00000000..cc2e7561 --- /dev/null +++ b/src/main/java/Duke/DukeLogic/Ui.java @@ -0,0 +1,68 @@ +package Duke.DukeLogic; + +import Duke.Models.Task; + +/** + * Ui class holds Ui display methods that interact with the user. + */ +public class Ui { + public final static String LINE = "____________________________________________________________"; + + /** + * Prints the opening message of Duke. + */ + public static void startDuke() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println(LINE + "\nWelcome to\n" + logo + " chatbot!"); + } + + /** + * Prints a greeting from Duke. + */ + public static void greet() { + String start = "Hello, I'm Duke.\n" + + "What can I do for you?\n"; + System.out.println(LINE + "\n" + start + LINE + "\n"); + } + + /** + * Prints a message with the number of task inside the task list of Duke. + */ + public static void printTaskCount() { + System.out.println("Now you have " + TaskList.size() + " tasks in the list."); + } + + /** + * Prints a message that a Task that has been added. + * @param newTask + */ + public static void printTaskAdded(Task newTask) { + assert(newTask != null); + System.out.println(LINE); + System.out.println("Got it. I've added this task:\n" + + newTask.getTaskInfo()); + } + + /** + * Prints a request for user to choose priority leve + * of a newly added task. + */ + public static void requestPriorityLevel() { + System.out.println("What is the priority level of this task?" + + "\n(enter 1 - LOW priority, 2 - MEDIUM priority, 3 - HIGH priority." + + "\nOther inputs will assume task has NIL priority level.)"); + System.out.println(LINE); + } + + /** + * Prints the closing message of Duke. + */ + public static void endDuke() { + System.out.println("Bye. Hope to see you again soon!"); + System.out.println(LINE); + } +} diff --git a/src/main/java/Duke/Models/Deadline.java b/src/main/java/Duke/Models/Deadline.java new file mode 100644 index 00000000..a15ca583 --- /dev/null +++ b/src/main/java/Duke/Models/Deadline.java @@ -0,0 +1,121 @@ +package Duke.Models; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * Represents a task with a deadline + * with a description of the task + * and the date and time when it is due. + */ +public class Deadline extends Task { + protected String by; + protected LocalDate date; + protected LocalTime time; + + /** + * A constructor for deadline object that takes in two strings + * one as the task description + * and the other as the deadline of the deadline object. + * @param newTask + * @param datetime + */ + public Deadline(String newTask, String datetime) { + super(newTask); + by = datetime; + } + + /** + * A constructor for deadline object that takes in a string and LocalDate object + * where the string is the task description + * and the LocalDate is the date the task is due. + * @param newTask + * @param newDate + */ + public Deadline(String newTask, LocalDate newDate) { + super(newTask); + date = newDate; + } + + /** + * A constructor for deadline object that takes in a string and LocalDate object + * where the string is the task description, + * the LocalDate is the date the task is due + * and the LocalTime is the time the task is due. + * @param newTask + * @param newDate + */ + public Deadline(String newTask, LocalDate newDate, LocalTime newTime) { + super(newTask); + date = newDate; + time = newTime; + } + + /** + * Get the date and time of the deadline. + * @return + */ + public String getAdditionalInfo() { + String info; + if (date == null) { + info = by; + } else { + info = date.toString(); + if (time != null) { + info = info + " " + time; + } + } + return info; + } + + /** + * Gets a string with task type symbol + * and a cross symbol to indicate whether a task is complete. + * @return String with task type and completed symbol. + */ + public String getCompletedSymbol() { + if (Completed) { + return "[D][X]"; + } else { + return "[D][ ]"; + } + } + + /** + * Return string with information of the deadline. + * Date is given in a new format if event has a LocalDate object. + * @return String with information on the deadline of the task. + */ + public String getTaskInfo() { + String info = super.getTaskInfo() + " (by: "; + if (date == null) { + info = info + by; + } else { + info = info + date.format(DateTimeFormatter.ofPattern("MMM dd yyyy")); + if (time != null) { + info = info + " " + time; + } + } + info = info + ")"; + info = info + " - with " + Priority + " priority"; + return info; + } + + /** + * Returns task type symbol. + * @return String with initials of the task type. + */ + @Override + public String getTaskType() { + return "D"; + } + + /** + * Prints event date in "MMM, dd, yyyy" format. + * Only works if event object has a LocalTime variable. + */ + public void printDate() { + System.out.println(date.format(DateTimeFormatter.ofPattern("MMM dd yyyy"))); + } +} diff --git a/src/main/java/Duke/Models/DoWithin.java b/src/main/java/Duke/Models/DoWithin.java new file mode 100644 index 00000000..c1a6ada8 --- /dev/null +++ b/src/main/java/Duke/Models/DoWithin.java @@ -0,0 +1,138 @@ +package Duke.Models; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +public class DoWithin extends Task { + protected String start; + protected LocalDate startDate; + protected LocalTime startTime; + protected String end; + protected LocalDate endDate; + protected LocalTime endTime; + + /** + * A constructor for dowithin object that takes in three strings + * one as the task description + * and the other two as the start date/time and end date/time + * of the "dowithin" task. + * @param newTask + * @param newStart + * @param newEnd + */ + public DoWithin(String newTask, String newStart, String newEnd) { + super(newTask); + start = newStart; + end = newEnd; + } + + /** + * A constructor for dowithin that takes in + * three strings, two localtime and two localdate objects + * newTask string representing the task description + * newStart, newStartDate and newStartTime representing the starting date/time of task + * newEnd, newEndDate and newEndTime representing the ending date/time of task + * @param newTask + * @param newStart + * @param newEnd + * @param newStartDate + * @param newStartTime + * @param newEndDate + * @param newEndTime + */ + public DoWithin (String newTask, String newStart, String newEnd, LocalDate newStartDate, + LocalTime newStartTime, LocalDate newEndDate, LocalTime newEndTime) { + super(newTask.trim()); + if (newStartDate != null) { + startDate = newStartDate; + startTime = newStartTime; + } else { + start = newStart.trim(); + } + if (newEndTime != null) { + endDate = newEndDate; + endTime = newEndTime; + } else { + end = newEnd.trim(); + } + } + + /** + * Get the start date and time + * and end date and time of the deadline. + * @return + */ + public String getAdditionalInfo() { + String info; + if (startDate == null) { + info = start; + } else { + info = startDate.toString(); + if (startTime != null) { + info = info + " " + startTime; + } + } + info = info + " | "; + if (endDate == null) { + info = info + end; + } else { + info = info + endDate.toString(); + if (endTime != null) + info = info + " " + endTime; + } + return info; + } + + /** + * Gets a string with task type symbol + * and a cross symbol to indicate whether a task is complete. + * @return String with task type and completed symbol. + */ + public String getCompletedSymbol() { + if (Completed) { + return "[W][X]"; + } else { + return "[W][ ]"; + } + } + + /** + * Return string with information of the "do within" task. + * Date is given in a new format if "dowithin" has a LocalDate object. + * @return String with information on the event of the task. + */ + public String getTaskInfo() { + String info = super.getTaskInfo() + " (between: "; + if (startDate == null) { + info = info + start; + } else { + info = info + startDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy")); + if (startTime != null) { + info = info + " " + startTime; + } + } + info = info + " and "; + if (endDate == null) { + info = info + end; + } else { + info = info + endDate.format(DateTimeFormatter.ofPattern("MMM dd yyyy")); + if (endTime != null) { + info = info + " " + endTime; + } + } + info = info + ")"; + info = info + " - with " + Priority + " priority"; + return info; + } + + /** + * Returns task type symbol. + * @return String with initials of the task type. + */ + @Override + public String getTaskType() { + return "W"; + } + +} diff --git a/src/main/java/Duke/Models/Event.java b/src/main/java/Duke/Models/Event.java new file mode 100644 index 00000000..d0493e66 --- /dev/null +++ b/src/main/java/Duke/Models/Event.java @@ -0,0 +1,121 @@ +package Duke.Models; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * Represents Events the user has to attend + * with the description of the event and date and time of the event. + */ +public class Event extends Task { + protected String at; + protected LocalDate date; + protected LocalTime time; + + + /** + * A constructor for event object that takes in two strings + * one as the task description + * and the other as the date/time of event. + * @param newTask + * @param datetime + */ + public Event(String newTask, String datetime) { + super(newTask); + at = datetime; + } + + /** + * A constructor for event object that takes in a string and LocalDate object + * where the string is the task description + * and the LocalDate is the date of the event. + * @param newTask + * @param newDate + */ + public Event(String newTask, LocalDate newDate) { + super(newTask); + date = newDate; + } + + /** + * A constructor for event object that takes in a string, LocalDate and LocalTime object + * where the string is the task description, + * the LocalDate is the date of the event + * and the LocalTime is the time of the event. + * @param newTask + * @param newDate + */ + public Event(String newTask, LocalDate newDate, LocalTime newTime) { + super(newTask); + date = newDate; + time = newTime; + } + + /** + * Get the date and time information of the event. + * @return String with date and time of the event. + */ + public String getAdditionalInfo() { + String info; + if (date == null) { + info = at; + } else { + info = date.toString(); + if (time != null) { + info = info + " " + time; + } + } + return info; + } + + /** + * Gets a string with task type symbol + * and a cross symbol to indicate whether a task is complete. + * @return String with task type and completed symbol. + */ + public String getCompletedSymbol() { + if (Completed) { + return "[E][X]"; + } else { + return "[E][ ]"; + } + } + + /** + * Return string with information of the event. + * Date is given in a new format if event has a LocalDate object. + * @return String with information on the event of the task. + */ + public String getTaskInfo() { + String info = super.getTaskInfo() + " (at: "; + if (date == null) { + info = info + at; + } else { + info = info + date.format(DateTimeFormatter.ofPattern("MMM dd yyyy")); + if (time != null) { + info = info + " " + time; + } + } + info = info + ")"; + info = info + " - with " + Priority + " priority"; + return info; + } + + /** + * Returns task type symbol. + * @return String with initials of the task type. + */ + @Override + public String getTaskType() { + return "E"; + } + + /** + * Prints event date in "MMM, dd, yyyy" format. + * Only works if event object has a LocalTime variable. + */ + public void printDate() { + System.out.println(date.format(DateTimeFormatter.ofPattern("MMM dd yyyy"))); + } +} diff --git a/src/main/java/Duke/Models/PriorityLevel.java b/src/main/java/Duke/Models/PriorityLevel.java new file mode 100644 index 00000000..ec65acd2 --- /dev/null +++ b/src/main/java/Duke/Models/PriorityLevel.java @@ -0,0 +1,8 @@ +package Duke.Models; + +/** + * Represents the priority levels of tasks. + */ +public enum PriorityLevel { + HIGH, MEDIUM, LOW, NIL +} diff --git a/src/main/java/Duke/Models/Task.java b/src/main/java/Duke/Models/Task.java new file mode 100644 index 00000000..69947f63 --- /dev/null +++ b/src/main/java/Duke/Models/Task.java @@ -0,0 +1,135 @@ +package Duke.Models; + +import Duke.DukeLogic.DukeException; + +/** + * Represents a Task with information regarding the + * description of the task and whether it is completed. + */ +public class Task { + + protected String Description; + protected boolean Completed; + protected PriorityLevel Priority; + + /** + * Task constructor method that takes in a string as description + * and sets task as uncompleted. + * @param newTask + */ + public Task(String newTask) { + this.Description = newTask; + this.Completed = false; + this.Priority = PriorityLevel.NIL; + } + + /** + * Task constructor method that takes in two strings + * one as the description of the task + * and the other as an indication whether task is completed. + * @param newTask + * @param toComplete + * @throws DukeException + */ + public Task(String newTask, String toComplete) throws DukeException { + this.Description = newTask; + if (toComplete.equals("1")){ + this.Completed = true; + } else if (toComplete.equals("0")) { + this.Completed = false; + } else { + throw new DukeException("Invalid input"); + } + } + + /** + * Returns description of the task as a string. + * @return String with description of the task. + */ + public String getDescription() { + return Description; + } + + /** + * Returns a boolean variable based on whether the task is completed. + * @return boolean + */ + public boolean isCompleted() { + return Completed; + } + + /** + * Gets additional info from task object if any. + * @return String with additional info. + */ + public String getAdditionalInfo() { + return ""; + } + + /** + * Returns a "[X]" symbol as a string if task is completed. + * If uncompleted, return "[ ]" as a string. + * @return String indicating whether a task is completed. + */ + public String getCompletedSymbol() { + if (Completed) { + return "[X]"; + } else { + return "[ ]"; + } + } + + /** + * Returns string with task information regarding completion and description. + * @return String with information on Task. + */ + public String getTaskInfo() { + String TaskInfo = this.getCompletedSymbol() + " " + Description; + return TaskInfo; + } + + /** + * Marks "Completed" variable as true. + */ + public void markCompleted() { + Completed = true; + } + + /** + * Gets the task type of a task object. + * @return String of initials of task type in any. + */ + public String getTaskType() { + return ""; + } + + /** + * Sets the priority level of the task + * based on the parameter passed. + * @param level + */ + public void setPriority(String level) { + try { + this.Priority = PriorityLevel.valueOf(level.trim()); + } catch (IllegalArgumentException e) { + switch (level.trim()) { + case "3": + this.Priority = PriorityLevel.HIGH; + break; + case "2": + this.Priority = PriorityLevel.MEDIUM; + break; + case "1": + this.Priority = PriorityLevel.LOW; + break; + default: + this.Priority = PriorityLevel.NIL; + } + } + } + + public PriorityLevel getPriority() { + return Priority; + + } +} diff --git a/src/main/java/Duke/Models/Todo.java b/src/main/java/Duke/Models/Todo.java new file mode 100644 index 00000000..4b73a5d1 --- /dev/null +++ b/src/main/java/Duke/Models/Todo.java @@ -0,0 +1,49 @@ +package Duke.Models; + +/** + * Represents a task that are to be done + * with the description of the task. + */ +public class Todo extends Task { + + /** + * Constructor method for To do object. + * @param newTask + */ + public Todo(String newTask) { + super(newTask); + } + + /** + * Gets a string with task type symbol + * and a cross symbol to indicate whether a task is complete. + * @return String with task type and completed symbol. + */ + public String getCompletedSymbol() { + if (Completed) { + return "[T][X]"; + } else { + return "[T][ ]"; + } + } + + /** + * Returns task type symbol. + * @return String with the initials of task type. + */ + @Override + public String getTaskType() { + return "T"; + } + + /** + * Return a string with information of the "to do" task + * @return + */ + @Override + public String getTaskInfo() { + String info = super.getTaskInfo(); + info = info + " - with " + Priority + " priority"; + return info; + } +} diff --git a/src/main/java/Duke/Parser/FileLineParser.java b/src/main/java/Duke/Parser/FileLineParser.java new file mode 100644 index 00000000..f3f76772 --- /dev/null +++ b/src/main/java/Duke/Parser/FileLineParser.java @@ -0,0 +1,183 @@ +package Duke.Parser; + +import Duke.Checker.FileLineChecker; + +import Duke.DukeLogic.DukeException; +import Duke.DukeLogic.TaskList; +import Duke.Models.*; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeParseException; + +public class FileLineParser { + /** + * Takes and parse a line from the storage file, + * uses the information from the line to create + * and return the respective task object based on the line. + * @param FileLine + * @return Task object created from file line. + * @throws DukeException + */ + public static Task parseLineFromFile(String FileLine) throws DukeException { + String[] parts = FileLine.split("\\|"); + if (parts[0].trim().equals("T")) { + if(FileLineChecker.isValidTodoLine(parts)) { + Todo newTodo = getTodoFromLine(parts); + return newTodo; + } + } else if (parts[0].trim().equals("D")) { + if(FileLineChecker.isValidDeadlineLine(parts)) { + Deadline newDeadline = getDeadlineFromLine(parts); + return newDeadline; + } + } else if (parts[0].trim().equals("E")) { + if (FileLineChecker.isValidEventLine(parts)) { + Event newEvent = getEventFromLine(parts); + return newEvent; + } + } else if (parts[0].trim().equals("W")) { + if (FileLineChecker.isValidDoWithinLine(parts)) { + DoWithin newDoWithin = getDoWithinFromLine(parts); + return newDoWithin; + } + } else { + System.out.println("Line is invalid"); + } + throw new DukeException("A line from storage file is invalid and will not be added to Duke."); + } + + /** + * Takes in the parsed parts of a line from the storage file + * and return a new "To do" object created from the parts. + * @param parts + * @return "To do" object created from information parsed from a file line. + */ + public static Todo getTodoFromLine(String[] parts) { + assert(parts[0].trim().equals("T")); + Todo newTodo = new Todo(parts[2].trim()); + if (parts[1].trim().equals("1")) { + TaskList.markTask(newTodo); + } + newTodo.setPriority(parts[3]); + return newTodo; + } + + /** + * Takes in the parsed parts of a line from the storage file, + * checks whether it has a date and time and creates and returns + * a new "Deadline" object using the correct constructor based on the checks. + * @param parts + * @return Deadline object created from information parsed from a file line. + */ + public static Deadline getDeadlineFromLine(String[] parts) { + assert(parts[0].trim().equals("D")); + String[] datetime = parts[3].trim().split(" "); + Deadline newDeadline; + if (datetime.length > 2 ) { + newDeadline = new Deadline(parts[2].trim(), parts[3].trim()); + } else { + LocalDate newDate = null; + LocalTime newTime = null; + try { + newDate = LocalDate.parse(datetime[0]); + newTime = LocalTime.parse(datetime[1]); + } catch (DateTimeParseException e) { + e.getMessage(); + } catch (ArrayIndexOutOfBoundsException e) { + e.getMessage(); + } + if (newDate != null && newTime != null) { + newDeadline = new Deadline(parts[2].trim(), newDate, newTime); + } else if (newDate != null) { + newDeadline = new Deadline(parts[2].trim(), newDate); + } else { + newDeadline = new Deadline(parts[2].trim(), parts[3].trim()); + } + } + if (parts[1].trim().equals("1")) { + TaskList.markTask(newDeadline); + } + newDeadline.setPriority(parts[4]); + return newDeadline; + } + + /** + * Takes in the parsed parts of a line from the storage file, + * checks whether it has a date and time and creates and returns + * a new "Event" object using the correct constructor based on the checks. + * @param parts + * @return an Event object created from information parsed from a file line. + */ + public static Event getEventFromLine(String[] parts) { + assert(parts[0].trim().equals("E")); + String[] datetime = parts[3].trim().split(" "); + Event newEvent; + if (datetime.length > 2 ) { + newEvent = new Event(parts[2].trim(), parts[3].trim()); + } else { + LocalDate newDate = null; + LocalTime newTime = null; + try { + newDate = LocalDate.parse(datetime[0]); + newTime = LocalTime.parse(datetime[1]); + } catch (DateTimeParseException e) { + e.getMessage(); + } catch (ArrayIndexOutOfBoundsException e) { + e.getMessage(); + } + if (newDate != null && newTime != null) { + newEvent = new Event(parts[2].trim(), newDate, newTime); + } else if (newDate != null) { + newEvent = new Event(parts[2].trim(), newDate); + } else { + newEvent = new Event(parts[2].trim(), parts[3].trim()); + } + } + if (parts[1].trim().equals("1")) { + TaskList.markTask(newEvent); + } + newEvent.setPriority(parts[4]); + return newEvent; + } + + /** + * Takes in the parsed parts of a line from the storage file, + * checks whether it have start date, start time, end date and end time + * and creates and returns + * a new "DoWithin" object using the correct constructor based on the checks. + * @param parts + * @return an DoWithin object created from information parsed from a file line. + */ + public static DoWithin getDoWithinFromLine(String[] parts) { + assert(parts[0].trim().equals("W")); + String[] startDateTime = parts[3].trim().split(" "); + String[] endDateTime = parts[4].trim().split(" "); + DoWithin newDoWithin; + LocalDate newStartDate = null, newEndDate = null; + LocalTime newStartTime = null, newEndTime = null; + try { + newStartDate = LocalDate.parse(startDateTime[0]); + newStartTime = LocalTime.parse(startDateTime[1]); + } catch (DateTimeParseException e) { + e.getMessage(); + } catch (ArrayIndexOutOfBoundsException e) { + e.getMessage(); + } + try { + newEndDate = LocalDate.parse(endDateTime[0]); + newEndTime = LocalTime.parse(endDateTime[1]); + } catch (DateTimeParseException e) { + e.getMessage(); + } catch (ArrayIndexOutOfBoundsException e) { + e.getMessage(); + } + newDoWithin = new DoWithin(parts[2].trim(), parts[3].trim(), parts[4].trim(), + newStartDate, newStartTime, newEndDate, newEndTime); + if (parts[1].trim().equals("1")) { + TaskList.markTask(newDoWithin); + } + newDoWithin.setPriority(parts[5]); + return newDoWithin; + } +} diff --git a/src/main/java/Duke/Parser/InputParser.java b/src/main/java/Duke/Parser/InputParser.java new file mode 100644 index 00000000..0040c451 --- /dev/null +++ b/src/main/java/Duke/Parser/InputParser.java @@ -0,0 +1,395 @@ +package Duke.Parser; + +import Duke.Checker.InputChecker; +import Duke.Models.*; +import Duke.DukeLogic.DukeException; +import Duke.DukeLogic.TaskList; +import Duke.DukeLogic.Ui; +import Duke.Duke; +import java.time.LocalDate; +import java.time.LocalTime; + +/** + * Represents a parser that make sense of inputs and retrieved lines. + */ +public class InputParser { + + /** + * Reads the input + * create a task object based on the input + * and adds it into the task list of Duke. + * @param input + */ + public static void parseInput(String input) { + if (input.trim().equals("list")) { + TaskList.printTaskList(); + } else if (input.startsWith("todo ")) { + parseTodoInput(input); + } else if (input.startsWith("deadline ")) { + parseDeadlineInput(input); + } else if (input.startsWith("event ")) { + parseEventInput(input); + } else if (input.startsWith("done ")) { + parseDoneInput(input); + } else if (input.startsWith("delete ")) { + parseDeleteInput(input); + } else if (input.startsWith("find ")) { + parseFindInput(input); + } else if (input.startsWith("dowithin ")) { + parseDoWithinInput(input); + } else { + System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + } + + /** + * If input contains valid information for a "To do" object, + * method creates the "to do" Object from the input + * and adds it to duke task list + * @param input + */ + public static void parseTodoInput(String input) { + assert(input.startsWith("todo ")); + try { + if (InputChecker.isValidTodoInput(input)) { + String newTask = input.substring(4).trim(); + Todo newTodo = new Todo(newTask); + TaskList.addTaskToList(newTodo); + addPriorityToTask(newTodo); + Ui.printTaskAdded(newTodo); + Ui.printTaskCount(); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + + /** + * If input contains valid information for a "deadline" object, + * method creates the "deadline" Object from the input + * and adds it to duke task list + * @param input + */ + public static void parseDeadlineInput(String input) { + assert(input.startsWith("deadline ")); + try { + if (InputChecker.isValidDeadlineInput(input)) { + Deadline newDeadline = buildDeadline(input); + TaskList.addTaskToList(newDeadline); + addPriorityToTask(newDeadline); + Ui.printTaskAdded(newDeadline); + Ui.printTaskCount(); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + + /** + * Takes in a valid input with information on a deadline, + * parse the parts of the input + * and returns a new Deadline object created using the relevant constructor. + * @param input + * @return Deadline object created from a valid input with deadline information. + */ + public static Deadline buildDeadline(String input) { + assert(input.startsWith("deadline ")); + String[] parts = input.substring(8).split("/by"); + String[] datetime = parts[1].trim().split(" "); + Deadline newDeadline; + if (datetime.length > 2) { + newDeadline = new Deadline(parts[0].trim(), parts[1].trim()); + } else { + String[] dateDetails = datetime[0].split("/"); + if (checkDateInput(dateDetails)) { + LocalDate taskDate = buildTaskDate(dateDetails); + if (datetime.length == 2 && checkTimeInput(datetime[1])) { + LocalTime taskTime = buildTaskTime(datetime[1]); + newDeadline = new Deadline(parts[0].trim(), taskDate, taskTime); + } else { + newDeadline = new Deadline(parts[0].trim(), taskDate); + } + } else { + newDeadline = new Deadline(parts[0].trim(), parts[1].trim()); + } + } + return newDeadline; + } + + /** + * If input contains valid information for an "event" object, + * method creates the "event" Object from the input + * and adds it to duke task list + * @param input + */ + public static void parseEventInput(String input) { + assert(input.startsWith("event ")); + try { + if (InputChecker.isValidEventInput(input)) { + Event newEvent = buildEvent(input); + TaskList.addTaskToList(newEvent); + addPriorityToTask(newEvent); + Ui.printTaskAdded(newEvent); + Ui.printTaskCount(); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + + /** + * Takes in a valid input with information on an event, + * parse the parts of the input + * and returns a new Event object created using the relevant constructor. + * @param input + * @return an Event object created from a valid input with event information. + */ + public static Event buildEvent(String input) { + assert(input.startsWith("event ")); + String[] parts = input.substring(5).split("/at"); + String[] datetime = parts[1].trim().split(" "); + Event newEvent; + if (datetime.length > 2) { + newEvent = new Event(parts[0].trim(), parts[1].trim()); + } else { + String[] dateDetails = datetime[0].split("/"); + if (checkDateInput(dateDetails)) { + LocalDate taskDate = buildTaskDate(dateDetails); + if (datetime.length == 2 && checkTimeInput(datetime[1])) { + LocalTime taskTime = buildTaskTime(datetime[1]); + newEvent = new Event(parts[0].trim(), taskDate, taskTime); + } else { + newEvent = new Event(parts[0].trim(), taskDate); + } + } else { + newEvent = new Event(parts[0].trim(), parts[1].trim()); + } + } + return newEvent; + } + + /** + * If input contains valid information for a "dowithin" object, + * method creates the "dowithin" Object from the input + * and adds it to duke task list + * @param input + */ + public static void parseDoWithinInput(String input) { + assert(input.startsWith("dowithin ")); + try { + if (InputChecker.isValidDoWithinInput(input)) { + DoWithin newDoWithin = buildDoWithin(input); + TaskList.addTaskToList(newDoWithin); + addPriorityToTask(newDoWithin); + Ui.printTaskAdded(newDoWithin); + Ui.printTaskCount(); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + + /** + * Takes in a valid input with information on + * a "dowithin" task that has to be done within a period of time, + * parse the parts of the input + * and returns a new DoWithin object created using the relevant constructor. + * @param input + * @return a DoWithin object created from a valid input with dowithin information. + */ + public static DoWithin buildDoWithin(String input) { + assert(input.startsWith("dowithin ")); + String[] parts = input.substring(8).split("/start"); + String[] startEnd = parts[1].split("/end"); + String[] startDateTime = startEnd[0].trim().split(" "); + String[] endDateTime = startEnd[1].trim().split(" "); + DoWithin newDoWithin; + LocalDate newStartDate = null, newEndDate = null; + LocalTime newStartTime = null, newEndTime = null; + if (startDateTime.length > 2 && endDateTime.length > 2) { + newDoWithin = new DoWithin(parts[0], startEnd[0], startEnd[1]); + } else { + if (startDateTime.length < 3) { + String[] startDateDetails = startDateTime[0].trim().split("/"); + if (checkDateInput(startDateDetails)) { + newStartDate = buildTaskDate(startDateDetails); + if (startDateTime.length == 2 && checkTimeInput(startDateTime[1])) { + newStartTime = buildTaskTime(startDateTime[1]); + } + } + } + if (endDateTime.length < 3) { + String[] endDateDetails = endDateTime[0].trim().split("/"); + if (checkDateInput(endDateDetails)) { + newEndDate = buildTaskDate(endDateDetails); + if (endDateTime.length == 2 && checkTimeInput(endDateTime[1])) { + newEndTime = buildTaskTime(endDateTime[1]); + } + } + } + newDoWithin = new DoWithin(parts[0], startEnd[0], startEnd[1], newStartDate, newStartTime, + newEndDate, newEndTime); + } + return newDoWithin; + } + + /** + * Takes in a string and checks whether it is in an input representing time. + * If it is in valid format, return true. + * @param timeInput + * @return boolean on whether input is represented in a valid time format. + */ + public static boolean checkTimeInput(String timeInput) { + if (timeInput.equals("")) { + return false; + } + try { + int time = Integer.parseInt(timeInput); + int hour = time/100; + int mins = time%100; + if (hour < 24 && hour > -1 && mins < 60 && mins > -1) { + return true; + } else { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Takes and parse a string representing time + * and return a new LocalTime object + * created from the parsed parts of the string. + * @param timeInput + * @return LocalTime object created from a valid time format input. + */ + public static LocalTime buildTaskTime(String timeInput) { + int time = Integer.parseInt(timeInput); + int hour = time/100; + int mins = time%100; + LocalTime taskTime = LocalTime.of(hour, mins); + return taskTime; + } + + /** + * Takes in a string array and checks whether it contains valid strings + * that represent date, month and year. + * If valid, the method returns true. + * @param details + * @return boolean whether input contains the correct details to represent date. + */ + public static boolean checkDateInput(String[] details) { + if (details.length != 3) { + return false; + } + try { + int day = Integer.parseInt(details[0].trim()); + int month = Integer.parseInt(details[1].trim()); + int year = Integer.parseInt(details[2].trim()); + if (day > 31 || day < 1) { + return false; + } else if (month > 12 || month < 1) { + return false; + } else if (year < 0) { + return false; + } else { + return true; + } + } catch (NumberFormatException e) { + return false; + } + } + + /** Takes a string array with details on a specific date + * and return a LocalDate object + * created using the details. + * @param details + * @return LocalDate object created from the parts of a valid date format input. + */ + public static LocalDate buildTaskDate(String[] details) { + String day = details[0].trim(); + if (day.length() == 1) { + day = "0" + day; + } + String month = details[1].trim(); + if (month.length() == 1) { + month = "0" + month; + } + String year = details[2].trim(); + if (year.length() < 4) { + for (int i = year.length(); i < 4; i ++) { + year = "0" + year; + } + } + String newDateFormat = year + "-" + month + "-" + day; + LocalDate taskDate = LocalDate.parse(newDateFormat); + return taskDate; + } + + /** + * Takes in a task object and request priority level of task from user. + * Set the priority level of the task object based on the input from user. + * @param newTask + */ + public static void addPriorityToTask(Task newTask) { + Ui.requestPriorityLevel(); + String level = Duke.getInput(); + if (level.equals("HIGH") || level.equals("MEDIUM") + || level.equals("LOW") || level.equals("NIL")) { + level = "0"; + } + newTask.setPriority(level); + } + + /** + * If input contain valid information to identify a task in the list, + * mark said task object as completed. + * @param input + */ + public static void parseDoneInput(String input) { + assert(input.startsWith("done ")); + try { + if (InputChecker.isValidDoneInput(input)) { + TaskList.markTaskAtIndex(input); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + + /** + * If input contain valid information to identify a task in the list, + * delete said task object. + * @param input + */ + public static void parseDeleteInput(String input) { + assert(input.startsWith("delete ")); + try { + if (InputChecker.isValidDeleteInput(input)) { + TaskList.deleteTaskAtIndex(input); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + + /** + * If input contain a valid keyword to search for task, + * print tasks with the keyword in their descriptions. + * @param input + */ + public static void parseFindInput(String input) { + assert(input.startsWith("find ")); + String[] parts = input.split(" "); + try { + if (InputChecker.isValidFindInput(parts)) { + TaskList.printTaskWithDesc(parts[1]); + } + } catch (DukeException e) { + e.printErrMsg(); + } + } + +} + diff --git a/src/test/java/Duke/Checker/FileLineCheckerTest.java b/src/test/java/Duke/Checker/FileLineCheckerTest.java new file mode 100644 index 00000000..2d68aa7f --- /dev/null +++ b/src/test/java/Duke/Checker/FileLineCheckerTest.java @@ -0,0 +1,34 @@ +package Duke.Checker; + +import Duke.DukeLogic.DukeException; +import org.junit.jupiter.api.Test; + +import static Duke.Checker.InputChecker.isValidTodoInput; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static Duke.Checker.FileLineChecker.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class FileLineCheckerTest { + + @Test + public void checkTodoLineTest() { + assertEquals(true, isValidTodoLine(new String[]{"T", "1", "Homework", "MEDIUM"})); + assertThrows(AssertionError.class, () -> isValidTodoLine(new String[]{"", "", ""})); + } + + @Test + public void checkDeadlineLineTest() { + assertEquals(true, + isValidDeadlineLine(new String[]{"D", "0", "Submit Project", "Sunday", "HIGH"})); + assertEquals(false, + isValidDeadlineLine(new String[]{"D", "3", "Do task", "Today", "LOW"})); + } + + @Test + public void checkEventLineTest() { + assertEquals(true, + isValidEventLine(new String[]{"E", "1", "Party", "Friend's house", "LOW"})); + assertEquals(false, + isValidEventLine(new String[]{"E", "0", "", "Nowhere", "NIL"})); + } +} diff --git a/src/test/java/Duke/Checker/InputCheckerTest.java b/src/test/java/Duke/Checker/InputCheckerTest.java new file mode 100644 index 00000000..2535cf49 --- /dev/null +++ b/src/test/java/Duke/Checker/InputCheckerTest.java @@ -0,0 +1,42 @@ +package Duke.Checker; + +import Duke.DukeLogic.DukeException; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static Duke.Checker.InputChecker.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class InputCheckerTest { + + @Test + public void checkValidTodoTest() throws DukeException { + assertEquals(true, isValidTodoInput("todo task 1")); + assertThrows(AssertionError.class, () -> isValidTodoInput("tod")); + assertThrows(DukeException.class, () -> isValidTodoInput("todo ")); + } + + @Test + public void checkValidDeadlineTest() throws DukeException { + assertEquals(true, isValidDeadlineInput("deadline homework /by today")); + assertThrows(AssertionError.class, () -> isValidDeadlineInput("deadli")); + assertThrows(DukeException.class, () -> isValidDeadlineInput("deadline return book /by")); + assertThrows(DukeException.class, () -> isValidDeadlineInput("deadline /by tomorrow")); + + } + + @Test + public void checkValidEventTest() throws DukeException { + assertEquals(true, isValidEventInput("event assembly /at school")); + assertThrows(AssertionError.class, () -> isValidEventInput("eve")); + assertThrows(DukeException.class, () -> isValidEventInput("event birthday /at")); + assertThrows(DukeException.class, () -> isValidEventInput("event /at home")); + } + + @Test + public void checkValidFindTest() throws DukeException { + assertEquals(true, isValidFindInput(new String[]{"find","task"})); + assertThrows(DukeException.class, () -> isValidFindInput(new String[]{"find"})); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> isValidFindInput(new String[]{})); + + } +} diff --git a/src/test/java/Duke/Models/DeadlineTest.java b/src/test/java/Duke/Models/DeadlineTest.java new file mode 100644 index 00000000..27a44b45 --- /dev/null +++ b/src/test/java/Duke/Models/DeadlineTest.java @@ -0,0 +1,79 @@ +package Duke.Models; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.time.LocalTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DeadlineTest { + Deadline Test1 = new Deadline("Task 1", "tomorrow"); + Deadline Test2 = new Deadline("Task 2", LocalDate.parse("2010-10-19")); + Deadline Test3 = new Deadline("Task 3", + LocalDate.parse("2012-01-20"), LocalTime.parse("18:00")); + + @Test + public void getAdditionalInfoTest() { + assertEquals("tomorrow", Test1.getAdditionalInfo()); + assertEquals("2010-10-19", Test2.getAdditionalInfo()); + assertEquals("2012-01-20 18:00", Test3.getAdditionalInfo()); + } + + @Test + public void isCompletedTest() { + assertEquals(false, Test1.isCompleted()); + assertEquals(false, Test2.isCompleted()); + assertEquals(false, Test3.isCompleted()); + Test1.markCompleted(); + Test3.markCompleted(); + assertEquals(true, Test1.isCompleted()); + assertEquals(true, Test3.isCompleted()); + } + + @Test + public void getTaskTypeTest(){ + assertEquals("D", Test1.getTaskType()); + assertEquals("D", Test2.getTaskType()); + assertEquals("D", Test3.getTaskType()); + } + + @Test + public void getCompletedSymbolTest() { + assertEquals("[D][ ]", Test1.getCompletedSymbol()); + assertEquals("[D][ ]", Test2.getCompletedSymbol()); + assertEquals("[D][ ]", Test3.getCompletedSymbol()); + Test1.markCompleted(); + Test2.markCompleted(); + assertEquals("[D][X]", Test1.getCompletedSymbol()); + assertEquals("[D][X]", Test2.getCompletedSymbol()); + } + + @Test + public void setPriorityTest() { + Test1.setPriority("MEDIUM"); + Test2.setPriority("1"); + Test3.setPriority("abc"); + assertEquals("MEDIUM", Test1.getPriority().toString()); + assertEquals("LOW", Test2.getPriority().toString()); + assertEquals("NIL", Test3.getPriority().toString()); + } + + @Test + public void getTaskInfoTest() { + assertEquals("[D][ ] Task 1 (by: tomorrow) " + + "- with NIL priority", Test1.getTaskInfo()); + Test1.setPriority("LOW"); + Test2.setPriority("3"); + Test3.setPriority("NIL"); + assertEquals("[D][ ] Task 1 (by: tomorrow) " + + "- with LOW priority", Test1.getTaskInfo()); + assertEquals("[D][ ] Task 2 (by: Oct 19 2010) " + + "- with HIGH priority", Test2.getTaskInfo()); + assertEquals("[D][ ] Task 3 (by: Jan 20 2012 18:00) " + + "- with NIL priority", Test3.getTaskInfo()); + Test2.markCompleted(); + assertEquals("[D][X] Task 2 (by: Oct 19 2010) " + + "- with HIGH priority", Test2.getTaskInfo()); + } +} diff --git a/src/test/java/Duke/Models/EventTest.java b/src/test/java/Duke/Models/EventTest.java new file mode 100644 index 00000000..740e1963 --- /dev/null +++ b/src/test/java/Duke/Models/EventTest.java @@ -0,0 +1,80 @@ +package Duke.Models; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.time.LocalTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EventTest { + Event Test1 = new Event("Task 1", "School"); + Event Test2 = new Event("Task 2", LocalDate.parse("2020-06-15")); + Event Test3 = new Event("Task 3", + LocalDate.parse("2021-11-14"), LocalTime.parse("23:59")); + + @Test + public void getAdditionalInfoTest() { + assertEquals("School", Test1.getAdditionalInfo()); + assertEquals("2020-06-15", Test2.getAdditionalInfo()); + assertEquals("2021-11-14 23:59", Test3.getAdditionalInfo()); + } + + @Test + public void isCompletedTest() { + assertEquals(false, Test1.isCompleted()); + assertEquals(false, Test2.isCompleted()); + assertEquals(false, Test3.isCompleted()); + Test2.markCompleted(); + Test3.markCompleted(); + assertEquals(true, Test2.isCompleted()); + assertEquals(true, Test3.isCompleted()); + } + + @Test + public void getTaskTypeTest(){ + assertEquals("E", Test1.getTaskType()); + assertEquals("E", Test2.getTaskType()); + assertEquals("E", Test3.getTaskType()); + } + + @Test + public void getCompletedSymbolTest() { + assertEquals("[E][ ]", Test1.getCompletedSymbol()); + assertEquals("[E][ ]", Test2.getCompletedSymbol()); + assertEquals("[E][ ]", Test3.getCompletedSymbol()); + Test1.markCompleted(); + Test3.markCompleted(); + assertEquals("[E][X]", Test1.getCompletedSymbol()); + assertEquals("[E][X]", Test3.getCompletedSymbol()); + } + + @Test + public void setPriorityTest() { + Test1.setPriority("3"); + Test2.setPriority("1"); + Test3.setPriority("MEDIUM"); + assertEquals("HIGH", Test1.getPriority().toString()); + assertEquals("LOW", Test2.getPriority().toString()); + assertEquals("MEDIUM", Test3.getPriority().toString()); + } + + @Test + public void getTaskInfoTest() { + assertEquals("[E][ ] Task 1 (at: School) " + + "- with NIL priority", Test1.getTaskInfo()); + Test1.setPriority("2"); + Test2.setPriority("testing"); + Test3.setPriority("HIGH"); + assertEquals("[E][ ] Task 1 (at: School) " + + "- with MEDIUM priority", Test1.getTaskInfo()); + assertEquals("[E][ ] Task 2 (at: Jun 15 2020) " + + "- with NIL priority", Test2.getTaskInfo()); + assertEquals("[E][ ] Task 3 (at: Nov 14 2021 23:59) " + + "- with HIGH priority", Test3.getTaskInfo()); + Test1.markCompleted(); + assertEquals("[E][X] Task 1 (at: School) " + + "- with MEDIUM priority", Test1.getTaskInfo()); + } +} + diff --git a/src/test/java/Duke/Models/TaskTest.java b/src/test/java/Duke/Models/TaskTest.java new file mode 100644 index 00000000..af40fb9f --- /dev/null +++ b/src/test/java/Duke/Models/TaskTest.java @@ -0,0 +1,105 @@ +package Duke.Models; + +import Duke.DukeLogic.DukeException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TaskTest { + Task Test1 = new Task("Task 1"); + Task Test2 = new Task("Task 2"); + + @Test + public void getDescriptionTest() { + assertEquals("Task 1", Test1.getDescription()); + assertEquals("Task 2", Test2.getDescription()); + try { + Task Test3 = new Task("Task 3", "0"); + assertEquals("Task 3", Test3.getDescription()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + } + + @Test + public void completedTest() { + assertEquals(false, Test1.isCompleted()); + assertEquals(false, Test2.isCompleted()); + + Test2.markCompleted(); + assertEquals(true, Test2.isCompleted()); + + try { + Task Test3 = new Task("Task 3", "1"); + assertEquals(true, Test3.isCompleted()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + + try { + Task Test4 = new Task("Task 4", "0"); + assertEquals(false, Test4.isCompleted()); + Test4.markCompleted(); + assertEquals(true, Test4.isCompleted()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + } + + @Test + public void getCompleteSymbolTest() { + assertEquals("[ ]", Test1.getCompletedSymbol()); + assertEquals("[ ]", Test2.getCompletedSymbol()); + + Test1.markCompleted(); + assertEquals("[X]", Test1.getCompletedSymbol()); + + try { + Task Test3 = new Task("Task 3", "0"); + assertEquals("[ ]", Test3.getCompletedSymbol()); + Test3.markCompleted(); + assertEquals("[X]", Test3.getCompletedSymbol()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + + try { + Task Test4 = new Task("Task 4", "0"); + assertEquals("[ ]", Test4.getCompletedSymbol()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + } + + @Test + public void setPriorityTest() { + Test1.setPriority("1"); + Test2.setPriority("123"); + assertEquals("LOW", Test1.getPriority().toString()); + assertEquals("NIL", Test2.getPriority().toString()); + } + + @Test + public void getTaskInfoTest() { + assertEquals("[ ] Task 1", Test1.getTaskInfo()); + assertEquals("[ ] Task 2", Test2.getTaskInfo()); + Test2.markCompleted(); + assertEquals("[X] Task 2", Test2.getTaskInfo()); + + try { + Task Test3 = new Task("Task 3", "0"); + assertEquals("[ ] Task 3", Test3.getTaskInfo()); + Test3.markCompleted(); + assertEquals("[X] Task 3", Test3.getTaskInfo()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + + try { + Task Test4 = new Task("Task 4", "1"); + assertEquals("[X] Task 4", Test4.getTaskInfo()); + } catch (DukeException e) { + System.out.println("Task creation failed"); + } + } +} diff --git a/src/test/java/Duke/Models/TodoTest.java b/src/test/java/Duke/Models/TodoTest.java new file mode 100644 index 00000000..3103ed50 --- /dev/null +++ b/src/test/java/Duke/Models/TodoTest.java @@ -0,0 +1,59 @@ +package Duke.Models; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TodoTest { + Todo Test1 = new Todo("Task 1"); + Todo Test2 = new Todo("Task 2"); + Todo Test3 = new Todo("Task 3"); + + @Test + public void getTaskTypeTest(){ + assertEquals("T", Test1.getTaskType()); + assertEquals("T", Test2.getTaskType()); + assertEquals("T", Test3.getTaskType()); + } + + @Test + public void getCompletedSymbolTest() { + assertEquals("[T][ ]", Test1.getCompletedSymbol()); + assertEquals("[T][ ]", Test2.getCompletedSymbol()); + assertEquals("[T][ ]", Test3.getCompletedSymbol()); + Test2.markCompleted(); + Test3.markCompleted(); + assertEquals("[T][X]", Test2.getCompletedSymbol()); + assertEquals("[T][X]", Test3.getCompletedSymbol()); + } + + @Test + public void setPriorityTest() { + Test1.setPriority("2"); + Test2.setPriority("random"); + Test3.setPriority("HIGH"); + assertEquals("MEDIUM", Test1.getPriority().toString()); + assertEquals("NIL", Test2.getPriority().toString()); + assertEquals("HIGH", Test3.getPriority().toString()); + } + + @Test + public void getTaskInfoTest() { + assertEquals("[T][ ] Task 1 " + + "- with NIL priority", Test1.getTaskInfo()); + Test1.setPriority("HIGH"); + Test2.setPriority("0"); + Test3.setPriority("LOW"); + assertEquals("[T][ ] Task 1 " + + "- with HIGH priority", Test1.getTaskInfo()); + assertEquals("[T][ ] Task 2 " + + "- with NIL priority", Test2.getTaskInfo()); + assertEquals("[T][ ] Task 3 " + + "- with LOW priority", Test3.getTaskInfo()); + + Test3.markCompleted(); + Test3.setPriority(""); + assertEquals("[T][X] Task 3 " + + "- with NIL priority", Test3.getTaskInfo()); + } +} diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 08737446..cecfdb11 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin Duke.Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755