diff --git a/.gitignore b/.gitignore index f69985ef1f..110417a037 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +*.class \ No newline at end of file diff --git a/README.md b/README.md index 8715d4d915..7b44397355 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 file, right-click it, and choose (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 0000000000..d397ad2781 --- /dev/null +++ b/build.gradle @@ -0,0 +1,63 @@ +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' +} + +dependencies { + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "duke.Launcher" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +run{ + standardInput = System.in +} diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 0000000000..9a379f6276 --- /dev/null +++ b/data/duke.txt @@ -0,0 +1,2 @@ +[T][ ]/home +[T][ ]/party diff --git a/docs/README.md b/docs/README.md index 8077118ebe..5fadfa813b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,132 @@ # User Guide +Hi This is Halloumi ^_^. +I can keep track of your tasks, deadlines and events for you, and am your own personal tasks manager. + ## Features -### Feature-ABC +1. **ToDo** : A simple task with only a description that. +2. **Deadline** : A task with a specific deadline that needs to be completed before that deadline. +3. **Event** : A task that is going to happen occur on a specific date. -Description of the feature. +## Usage -### Feature-XYZ +### `todo` - add a todo task into the task list. -Description of the feature. +using this keyword with a description will add a todo task in the list. -## Usage +Example of usage: + +`todo read book` + +Expected outcome: + +this will add the task of reading a book to the list. + +``` +New task added: +[T][ ] read book +``` + +### `deadline` - add a deadline task into the task list. + +using this keyword with a description and a date after "/by" will add a deadline task in the list. + +Example of usage: + +`deadline finish project /by 2022-06-10` + +Expected outcome: + +this will add the deadline task of finishing a project and set the deadline as June 10 2022. + +``` +New task added: +[D][ ] finish project (by: Jun 10 2022) +``` + +### `event` - add an event task into the task list. + +using this keyword with a description and a date after "/at" will add a event task in the list. + +Example of usage: + +`event go party /at 2022-06-10` + +Expected outcome: + +this will add the event task of going to a party and set the event date as June 10 2022. + +``` +New task added: +[E][ ] go party (at: June 10 2022) +``` + +### `mark` - marks a task as completed + +using this keyword with a task number from the list will mark that task as complete +Example of usage: + +`mark 6` + +Expected outcome: + +this will mark the 6th task in the list as complete + +``` +Good Job! ^_^ +Task number 6 has been marked as done! +[D][X] finish project (by: Jun 10 2022) +``` + +### `unmark` - marks a task as incompleted + +using this keyword with a task number from the list will mark that task as incomplete +Example of usage: + +`unmark 6` + +Expected outcome: + +this will mark the 6th task in the list as incomplete + +``` +I've unmarked task number 6 +Complete it soon! ^_^ +[D][ ] finish project (by: Jun 10 2022)) +``` +### `delete` - delete a task + +using this keyword with a task number from the list will delete that task from the list + +Example of usage: + +`delete 6` + +Expected outcome: + +this will delete the 6th task in the list + +``` +Noted. I've removed this task: +[D][ ] finish project (by: Jun 10 2022) +``` -### `Keyword` - Describe action +### `find` - find a task -Describe the action and its outcome. +using this keyword with a keyword will find the tasks where the description contain that keyword Example of usage: -`keyword (optional arguments)` +`find run` Expected outcome: -Description of the outcome. +this will find all the tasks where the description contains 'run' ``` -expected output +Here are the matching tasks: +1. [T][ ] run +2. [D][ ] run (by: Jan 2 2007) +3. [E][ ] run (at: Jan 2 2007) ``` diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..12f840d06c Binary files /dev/null and b/docs/Ui.png differ diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..c4192631f2 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file 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..b7c8c5dbf5 --- /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 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.class b/src/main/java/Duke.class new file mode 100644 index 0000000000..acbcbd380b Binary files /dev/null and b/src/main/java/Duke.class differ 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..6e864153e8 --- /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/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 0000000000..805bf723f9 --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,134 @@ +package duke; + +import duke.*; +import exceptions.DukeExceptions; +import exceptions.DukeInvalidInput; +import exceptions.DukeInvalidTodo; + +import java.io.*; +import java.util.Collections; +import java.util.Scanner; +import java.util.ArrayList; + +/** + * Main method containing the Chat Bot. + */ +public class Duke { + + static Storage storage = new Storage(); + static TaskList taskList = new TaskList(); + static ArrayList lists = new ArrayList(); + public Duke() { + try { + storage.loadFromFile(lists); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Method containing the main execution of the Chat Bot. + * @param + * @throws DukeExceptions + * @throws IOException + */ + public static String main(String input) throws DukeExceptions, IOException { + StringBuilder str = new StringBuilder(); + + + boolean isOver = false; + int num = 0; + + //Ui.enterHalloumi(str); + + + String[] textSplitOne = Parser.splitForwardSlash(input); + String[] textSplit = Parser.splitSpace(textSplitOne[0]); + String fullDesc = Parser.makeDesc(textSplit, textSplit.length); + try { + switch (textSplit[0]) { + case "bye": + num = Ui.exitHalloumi(str); + storage.writeToFile(lists); + break; + case "list": + Ui.printList(lists, lists.size(), str); + break; + case "mark": + for(int i = 1; i < textSplit.length; i++) { + taskList.mark(textSplit[i], lists, str); + + } + break; + case "unmark": + for(int i = 1; i < textSplit.length; i++) { + taskList.unmark(textSplit[i], lists, str); + + } + break; + case "delete" : + ArrayList array = new ArrayList<>(); + for(int i = 1; i < textSplit.length; i++) { + Integer integer = Integer.parseInt(textSplit[i]); + array.add(integer); + } + Collections.sort(array, Collections.reverseOrder()); +// System.out.println(array); + for(int i = 0; i < array.size(); i++) { +// System.out.println(textSplit[i]); + taskList.delete(String.valueOf(array.get(i)), lists, str); + } + break; + case "todo": + try { + if(fullDesc.equals(" ") || fullDesc.equals("")) { + throw new DukeInvalidTodo(); + } + } + catch(DukeInvalidTodo e) { + str.append(e.getMessage()); + } + taskList.todo(fullDesc, lists, str); + break; + case "event": + taskList.event(fullDesc, textSplitOne[1], lists, str); + break; + case "deadline": + taskList.deadline(fullDesc, textSplitOne[1], lists, str); + break; + case "find": + taskList.find(fullDesc, lists, str); + break; + default: + throw new DukeInvalidInput(); + } + } catch (DukeInvalidInput e) { + str.append(e.getMessage()); + } + + isOver = true; + + assert isOver; + return str.toString(); + } + + /** + * + * @param input the input from the user + * @return the response by the program + * @throws DukeExceptions + */ + + public String getResponse(String input) throws DukeExceptions, IOException { + try { + + return main(input); + } + catch (DukeExceptions e) { + return e.getMessage(); + } + + } + +} + diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..3b04bb6423 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,14 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + 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..cf0d05fb07 --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,34 @@ +package duke; +import java.io.IOException; + +import duke.javafx.MainWindow; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for duke.duke.Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke(); + + public Main() throws IOException { + } + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/views/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java new file mode 100644 index 0000000000..c9fd6b9b13 --- /dev/null +++ b/src/main/java/duke/Parser.java @@ -0,0 +1,57 @@ +package duke; + +/** + * Class for parsing through the input. + */ +public class Parser { + + /** + * Method to split the string input with a forward slash. + * @param input user input. + * @return String[] array after splitting. + */ + public static String[] splitForwardSlash(String input) { + return input.split("/"); + } + + /** + * Method to split the string input with a white space. + * @param input user input. + * @return String[] array after splitting. + */ + public static String[] splitSpace(String input) { + return input.split(" "); + } + + /** + * Method to split the string input with a bracket. + * @param input user input. + * @return String[] array after splitting + */ + public static String[] splitBracket(String input) { + return input.split("]"); + } + + /** + * Method to change string to int type. + * @param input user input. + * @return int after parsing through string. + */ + public static int stringToInt(String input) { + return Integer.parseInt(input); + } + + /** + * Method to make the description of tasks. + * @param text user input. + * @param len length of user input. + * @return full description of the task. + */ + public static String makeDesc(String[] text, int len) { + String newText = ""; + for (int i = 1; i < len; i++) { + newText = newText + text[i] + " "; + } + return newText.trim(); + } +} diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java new file mode 100644 index 0000000000..04a11b3a54 --- /dev/null +++ b/src/main/java/duke/Storage.java @@ -0,0 +1,86 @@ +package duke; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Class to save and load data. + */ +public class Storage { + + /** + * Method to write the task list to file. + * @param list provides data to write tasks to the file. + * @throws IOException + */ + public void writeToFile(ArrayList list) throws IOException { + try { + File f = new File("./data/duke.txt"); + boolean hasCreatedDirectory = f.getParentFile().mkdirs(); + boolean hasCreated = f.createNewFile(); + FileWriter writer = new FileWriter(f, false); + for (Task task : list) { + String fileInput; + Task ele = task; + if (ele instanceof ToDo) { + fileInput = "[T][" + ele.getStatusIcon() + "]/" + ele.description; + } else if (ele instanceof Deadline) { + Deadline deadL = (Deadline) ele; + fileInput = "[D][" + deadL.getStatusIcon() + "]/" + deadL.description + "/" + deadL.when; + } else { + Event eve = (Event) ele; + fileInput = "[E][" + eve.getStatusIcon() + "]/" + eve.description + "/" + eve.when; + } + writer.write(fileInput + "\n"); + writer.flush(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * Method to read tasks from the file. + * @param list data is written to this variable. + * @throws IOException + */ + public void loadFromFile(ArrayList list) throws IOException { + File f = new File("./data/duke.txt"); + boolean hasCreatedDirectory = f.getParentFile().mkdirs(); + boolean hasCreated = f.createNewFile(); + String input, desc; + Task t; + Scanner in = new Scanner(f); + while(in.hasNextLine()) { + input = in.nextLine(); + String[] tSplit = Parser.splitForwardSlash(input); + String[] splitT = Parser.splitBracket(tSplit[0]); + switch(splitT[0]) { + case "[T": + t = new ToDo(tSplit[1]); + break; + case "[E": + t = new Event(tSplit[1], LocalDate.parse(tSplit[2])); + break; + case "[D": + t = new Deadline(tSplit[1], LocalDate.parse(tSplit[2])); + break; + default: + t = new Task("eee"); + break; + } + if(splitT[1].equals("[X")) { + t.done(); + list.add(t); + } + else { + list.add(t); + } + } + in.close(); + } +} diff --git a/src/main/java/duke/Task.java b/src/main/java/duke/Task.java new file mode 100644 index 0000000000..9857a69d1f --- /dev/null +++ b/src/main/java/duke/Task.java @@ -0,0 +1,79 @@ +package duke; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * Class that contains information for all tasks. + */ +public class Task { + String description; + boolean isDone; + + public Task(String desc) { + description = desc; + isDone = false; + } + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + public String toString() { + return String.format("[%s] %s", getStatusIcon(), description); + } + + public void done() { + isDone = true; + } + + public void undo() { + isDone = false; + } +} + +/** + * Todo class that inherits from the Task class. + */ +class ToDo extends Task { + public ToDo(String desc) { + super(desc); + } + + public String toString() { + return "[T]" + super.toString(); + } +} + +/** + * Event class that inherits from the Task class. + */ +class Event extends Task { + LocalDate when; + + public Event(String desc, LocalDate date) { + super(desc); + when = date; + } + + public String toString() { + return "[E]" + super.toString() + " (at: " + + when.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")"; + } +} + + /** + * Deadline class that inherits from the Task class. + */ + class Deadline extends Task { + LocalDate when; + + public Deadline(String desc, LocalDate date) { + super(desc); + when = date; + } + + public String toString() { + return "[D]" + super.toString() + " (by: " + + when.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")"; + } + } diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java new file mode 100644 index 0000000000..1aa469143d --- /dev/null +++ b/src/main/java/duke/TaskList.java @@ -0,0 +1,128 @@ +package duke; + +import java.time.LocalDate; +import java.util.ArrayList; + +/** + * Class that contains all task related functions. + */ +public class TaskList { + + /** + * Method to mark task as done. + * @param task the task number as a string. + * @param list list containing all the tasks. + */ + public void mark(String task, ArrayList list, StringBuilder str) { + str.append(Ui.lineOne()); + str.append("Good Job! ^_^\n"); + str.append("Task number ").append(task).append(" has been marked as done!\n"); + int tNum = Integer.parseInt(task); + list.get(tNum - 1).done(); + str.append(list.get(tNum - 1)).append("\n"); + str.append(Ui.lineOne()); + } + + /** + * Method to unmark task as done. + * @param task the task number as a string. + * @param list list containing all the tasks. + */ + public void unmark(String task, ArrayList list, StringBuilder str) { + str.append(Ui.lineOne()); + str.append("I've unmarked task number ").append(task).append("\n"); + str.append("Complete it soon! ^_^\n"); + int tNo = Parser.stringToInt(task); + list.get(tNo - 1).undo(); + str.append(list.get(tNo - 1)).append("\n"); + str.append(Ui.lineOne()); + } + + /** + * Method to delete a task from the list. + * @param task the task number as a string. + * @param list list containing all the tasks. + */ + public void delete(String task, ArrayList list, StringBuilder str) { + str.append(Ui.lineOne()); + str.append("Noted. I've removed this task:\n"); + int t2No = Parser.stringToInt(task); + str.append(list.get(t2No - 1)).append("\n"); + list.remove(t2No - 1); + str.append(Ui.lineOne()); + } + + /** + * Method to create an event task and add it to the list. + * @param desc description of the event. + * @param at additional information. + * @param list list containing all the tasks. + */ + public void event(String desc, String at, ArrayList list, StringBuilder str) { + str.append(Ui.lineTwo()); + str.append("New task added:\n"); + String[] date = Parser.splitSpace(at); + Task t2 = new Event(desc, LocalDate.parse(date[1])); + list.add(t2); + str.append(t2).append("\n"); + str.append("You have ").append(list.size()).append(" tasks left now! ^_^\n"); + str.append(Ui.lineTwo()); + } + + /** + * Method to create a todo task and add it to the list. + * @param desc description of the event. + * @param list list containing all the tasks. + */ + public void todo(String desc, ArrayList list, StringBuilder str) { + str.append(Ui.lineTwo()); + str.append("New task added:\n"); + Task t = new ToDo(desc); + list.add(t); + str.append(t).append("\n"); + str.append("You have ").append(list.size()).append(" tasks left now! ^_^\n"); + str.append(Ui.lineTwo()); + } + + /** + * Method to create a deadline task and add it to the list. + * @param desc description of the event. + * @param by additional information + * @param list list containing all the tasks. + */ + public void deadline(String desc, String by, ArrayList list, StringBuilder str) { + str.append(Ui.lineTwo()); + str.append("New task added:\n"); + String[] date = Parser.splitSpace(by); + Task t2 = new Deadline(desc, LocalDate.parse(date[1])); + list.add(t2); + str.append(t2).append("\n"); + str.append("You have ").append(list.size()).append(" tasks left now! ^_^\n"); + str.append(Ui.lineTwo()); + } + + /** + * Method that returns a string. + * @return String + */ + public String returnMatching() { + String string = "Here are the matching tasks:\n"; + return string; + } + + /** + * Method to find a task. + * @param desc description of the task to be found. + * @param list list containing all the tasks. + */ + public void find(String desc, ArrayList list, StringBuilder str) { + str.append(Ui.lineTwo()); + str.append(returnMatching()); + for(int i = 0; i < list.size(); i++) { + if(list.get(i).description.contains(desc)) { + str.append(i+1).append(". ").append(list.get(i)).append("\n"); + } + } + str.append(Ui.lineTwo()); + } +} diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java new file mode 100644 index 0000000000..58e37ceda4 --- /dev/null +++ b/src/main/java/duke/Ui.java @@ -0,0 +1,67 @@ +package duke; + +import java.util.ArrayList; + +/** + * Class that contains UI related functions. + */ +public class Ui { + + /** + * Method to print divider of type *. + */ + public static String lineOne() { + return("***************************\n"); + } + + /** + * Method to print divider of type |-|. + */ + public static String lineTwo() { + return("|-|-|-|-|-|-|-|-|-|-|-|-|-|\n"); + } + + /** + * Method to print all the tasks currently in the list. + * @param list contains all the current tasks. + * @param size list size. + */ + public static void printList(ArrayList list, int size, StringBuilder str) { + str.append(lineOne()); + str.append("List: \n"); + if(list.isEmpty()) { + str.append("No tasks to complete! ^_^\n"); + return; + } + for (int i = 0; i < size; i++) { + str.append(i+1).append(". ").append(list.get(i)).append("\n"); + } + str.append(lineOne()); + } + + /** + * Method to print the start of program. + */ + public static void enterHalloumi(StringBuilder str) { + str.append(lineOne()); + str.append("Hi! I'm Halloumi ^_^\n"); + str.append("What do you need help with today?\n"); + str.append(lineOne()); + } + + public static String byeMessage() { + String string = "See you soon! Have a good day ^_^\n"; + return string; + } + + /** + * Method to exit the chatbot. + * @return an int to signify the end of the program. + */ + public static int exitHalloumi(StringBuilder str) { + str.append(lineOne()); + str.append(byeMessage()); + str.append(lineOne()); + return 1; + } +} diff --git a/src/main/java/duke/javafx/DialogBox.java b/src/main/java/duke/javafx/DialogBox.java new file mode 100644 index 0000000000..d8ca7c9689 --- /dev/null +++ b/src/main/java/duke/javafx/DialogBox.java @@ -0,0 +1,66 @@ +package duke.javafx; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.text.Font; + +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ +public class DialogBox extends HBox { + + private static final Font CHAT_FONT = Font.loadFont( + DialogBox.class.getResource("/fonts/tnr.ttf").toExternalForm(), 12); + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/views/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + dialog.setFont(CHAT_FONT); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/duke/javafx/MainWindow.java b/src/main/java/duke/javafx/MainWindow.java new file mode 100644 index 0000000000..8be7dc05c9 --- /dev/null +++ b/src/main/java/duke/javafx/MainWindow.java @@ -0,0 +1,61 @@ +package duke.javafx; +import duke.Duke; +import exceptions.DukeExceptions; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; + +import java.io.IOException; + +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + + private static final Font CHAT_FONT = Font.loadFont( + DialogBox.class.getResource("/fonts/tnr.ttf").toExternalForm(), 12); + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + userInput.setFont(CHAT_FONT); + sendButton.setFont(CHAT_FONT); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing duke.duke.Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() throws DukeExceptions, IOException { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/exceptions/DukeExceptions.java b/src/main/java/exceptions/DukeExceptions.java new file mode 100644 index 0000000000..e143662e7e --- /dev/null +++ b/src/main/java/exceptions/DukeExceptions.java @@ -0,0 +1,7 @@ +package exceptions; + +public class DukeExceptions extends Exception { + public DukeExceptions(String output) { + super(output); + } +} diff --git a/src/main/java/exceptions/DukeInvalidInput.java b/src/main/java/exceptions/DukeInvalidInput.java new file mode 100644 index 0000000000..9968966406 --- /dev/null +++ b/src/main/java/exceptions/DukeInvalidInput.java @@ -0,0 +1,7 @@ +package exceptions; + +public class DukeInvalidInput extends DukeExceptions { + public DukeInvalidInput() { + super("ENTER A VALID INPUT! ^_^"); + } +} diff --git a/src/main/java/exceptions/DukeInvalidTodo.java b/src/main/java/exceptions/DukeInvalidTodo.java new file mode 100644 index 0000000000..fb7c43afe5 --- /dev/null +++ b/src/main/java/exceptions/DukeInvalidTodo.java @@ -0,0 +1,7 @@ +package exceptions; + +public class DukeInvalidTodo extends DukeExceptions { + public DukeInvalidTodo() { + super("ENTER A DESCRIPTION! ^_^"); + } +} diff --git a/src/main/resources/fonts/tnr.ttf b/src/main/resources/fonts/tnr.ttf new file mode 100644 index 0000000000..5cdac9ca59 Binary files /dev/null and b/src/main/resources/fonts/tnr.ttf differ diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000..730ac16b0b Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png new file mode 100644 index 0000000000..2513a80e93 Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/views/DialogBox.fxml b/src/main/resources/views/DialogBox.fxml new file mode 100644 index 0000000000..ede775d4f9 --- /dev/null +++ b/src/main/resources/views/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/views/MainWindow.fxml b/src/main/resources/views/MainWindow.fxml new file mode 100644 index 0000000000..84c540a154 --- /dev/null +++ b/src/main/resources/views/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +