diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..2dc2d5c5ed --- /dev/null +++ b/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' + id 'org.openjfx.javafxplugin' version '0.0.7' +} + +group 'seedu.duke' +version '0.1.0' + +checkstyle { + toolVersion = '8.23' +} + +repositories { + mavenCentral() +} + +application { + // Change this to your main class. + mainClassName = "seedu.duke.Main" +} + +shadowJar { + archiveBaseName = "duke" + archiveVersion = "0.1.3" + archiveClassifier = null + archiveAppendix = null +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0' +} + +test { + useJUnitPlatform() +} + +javafx { + version = "11.0.2" + modules = [ 'javafx.controls', 'javafx.fxml' ] +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..b1a57ba6c0 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/README.md b/docs/README.md index fd44069597..11800ccfe3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,18 +3,27 @@ ## Features ### Feature 1 -Description of feature. +Enable adding of task, including todo, deadline and event + +### Feature 2 +User can view the list of tasks + +### Feature 3 +User can mark a task as done + +### Feature 4 +User can delete a task ## Usage ### `Keyword` - Describe action -Describe action and its outcome. - Example of usage: -`keyword (optional arguments)` +`todo read book` Expected outcome: -`outcome` +`"I have successfully added the following task + [T][x] read book + Now you have 1 task in your list` diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..ee9fb83df7 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..fc24e7a62d --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file diff --git a/docs/docs:Ui.png b/docs/docs:Ui.png new file mode 100644 index 0000000000..ee9fb83df7 Binary files /dev/null and b/docs/docs:Ui.png differ diff --git a/duke.jar b/duke.jar new file mode 100644 index 0000000000..224c8dfe3a Binary files /dev/null and b/duke.jar differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..87b738cbd0 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..8b5a27e4ef --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Sep 07 10:06:36 ICT 2019 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..af6708ff22 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..6d57edc706 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/seedu.duke.txt b/seedu.duke.txt new file mode 100644 index 0000000000..d3e990d0c4 --- /dev/null +++ b/seedu.duke.txt @@ -0,0 +1,4 @@ +T | 0 | read book +T | 0 | read book +T | 0 | go picnic +D | 0 | submit quiz | Monday diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..d1e92fe5db --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'duke' 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/seedu/duke/Command/AddCommand.java b/src/main/java/seedu/duke/Command/AddCommand.java new file mode 100644 index 0000000000..4e6d800717 --- /dev/null +++ b/src/main/java/seedu/duke/Command/AddCommand.java @@ -0,0 +1,49 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.TaskList.Task; + +import java.io.IOException; + +public class AddCommand extends Command { + + protected Task task; + protected TaskList taskList; + + public AddCommand(Task task) { + this.task = task; + } + + /** + * execute the command of adding task + * @param taskList + * @param storage + * @throws IOException + */ + @Override + public void execute(TaskList taskList, Storage storage) throws IOException { + taskList.addList(task); + this.taskList = taskList; + storage.save(taskList); + } + + @Override + public String getResponse() { + StringBuilder str = new StringBuilder(); + str.append("Got it. I've added this task:\n"); + str.append(task.toActionString() + "\n"); + str.append("Now you have " + taskList.list.size() + + " tasks in the list"); + return str.toString(); + } + /** + * + * @return boolean + * + */ + @Override + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/seedu/duke/Command/ByeCommand.java b/src/main/java/seedu/duke/Command/ByeCommand.java new file mode 100644 index 0000000000..4ca64ff03e --- /dev/null +++ b/src/main/java/seedu/duke/Command/ByeCommand.java @@ -0,0 +1,28 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; + +import java.io.IOException; + +public class ByeCommand extends Command { + + public ByeCommand() {} + + @Override + public void execute(TaskList t, Storage storage) throws IOException { + + } + + @Override + public String getResponse() { + return "Bye! Hope to see you again soon!"; + } + + + @Override + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/seedu/duke/Command/Command.java b/src/main/java/seedu/duke/Command/Command.java new file mode 100644 index 0000000000..a8628f2d4f --- /dev/null +++ b/src/main/java/seedu/duke/Command/Command.java @@ -0,0 +1,20 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; + +import java.io.IOException; + +public abstract class Command { + + protected boolean isExit = false; + + public Command() {} + + public abstract void execute(TaskList t, Storage storage) throws IOException; + + public abstract String getResponse(); + + public abstract boolean isExit(); +} diff --git a/src/main/java/seedu/duke/Command/DeleteCommand.java b/src/main/java/seedu/duke/Command/DeleteCommand.java new file mode 100644 index 0000000000..8b8d39a6a3 --- /dev/null +++ b/src/main/java/seedu/duke/Command/DeleteCommand.java @@ -0,0 +1,52 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.Task; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; + +import java.io.IOException; + +public class DeleteCommand extends Command { + + private int taskNo; + private TaskList taskList; + private Task task; + + public DeleteCommand(int taskNo) { + this.taskNo = taskNo; + } + + /** + * execute the command of deleting the task + * @param taskList + * @param storage + * @throws IOException + */ + @Override + public void execute(TaskList taskList, Storage storage) throws IOException { + this.task = taskList.list.get(taskNo - 1); + taskList.delete(taskNo); + this.taskList = taskList; + storage.save(taskList); + } + + @Override + public String getResponse() { + StringBuilder str = new StringBuilder(); + str.append("Noted. I've removed this task: \n"); + str.append(task.toActionString() + "\n"); + str.append("Now you have " + taskList.list.size() + + " tasks in the list."); + return str.toString(); + } + + /** + * check if it is exited + * @return boolean + */ + @Override + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/seedu/duke/Command/DoneCommand.java b/src/main/java/seedu/duke/Command/DoneCommand.java new file mode 100644 index 0000000000..842454be7e --- /dev/null +++ b/src/main/java/seedu/duke/Command/DoneCommand.java @@ -0,0 +1,43 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; + +import java.io.IOException; + +public class DoneCommand extends Command { + + private int taskNo; + private TaskList taskList; + + public DoneCommand(int taskNo) { + this.taskNo = taskNo; + } + + /** + * execute the command of marking a task as done + * @param taskList + * @param storage + * @throws IOException + */ + @Override + public void execute(TaskList taskList, Storage storage) throws IOException { + taskList.list.get(taskNo - 1).markAsDone(); + this.taskList = taskList; + storage.save(taskList); + } + + @Override + public String getResponse() { + StringBuilder str = new StringBuilder(); + str.append("Nice! I've marked this task as done:\n"); + str.append(taskList.list.get(taskNo - 1).toActionString()); + return str.toString(); + } + + @Override + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/seedu/duke/Command/FindCommand.java b/src/main/java/seedu/duke/Command/FindCommand.java new file mode 100644 index 0000000000..543016c1c8 --- /dev/null +++ b/src/main/java/seedu/duke/Command/FindCommand.java @@ -0,0 +1,40 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; +import seedu.duke.TaskList.Task; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +public class FindCommand extends Command{ + private String word; + private TaskList taskList; + + public FindCommand(String word) { + this.word = word; + } + + @Override + public void execute(TaskList taskList, Storage storage) throws IOException { + this.taskList = taskList; + } + + @Override + public String getResponse() { + StringBuilder str = new StringBuilder(); + str.append("Here are the matching tasks in your list:\n"); + for (Task task: taskList.contains(word)) { + str.append(task.toActionString() + "\n"); + } + return str.toString(); + } + + @Override + public boolean isExit() { + return false; + } + +} diff --git a/src/main/java/seedu/duke/Command/ListCommand.java b/src/main/java/seedu/duke/Command/ListCommand.java new file mode 100644 index 0000000000..40aa5a09b3 --- /dev/null +++ b/src/main/java/seedu/duke/Command/ListCommand.java @@ -0,0 +1,42 @@ +package seedu.duke.Command; + +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; + +import java.io.IOException; + +public class ListCommand extends Command { + + private TaskList taskList; + + public ListCommand() { + } + + /** + * execute the command of listing out all the task + * @param taskList + * @param storage + * @throws IOException + */ + @Override + public void execute(TaskList taskList, Storage storage) throws IOException { + this.taskList = taskList; + } + + @Override + public String getResponse() { + StringBuilder str = new StringBuilder(); + str.append("Here are the tasks in your list:\n"); + for (int i = 1;i <= taskList.list.size();i++) { + str.append(i + "." + + taskList.list.get(i - 1).toActionString() + "\n"); + } + return str.toString(); + } + + @Override + public boolean isExit() { + return isExit; + } +} diff --git a/src/main/java/seedu/duke/DialogBox.java b/src/main/java/seedu/duke/DialogBox.java new file mode 100644 index 0000000000..56854c340f --- /dev/null +++ b/src/main/java/seedu/duke/DialogBox.java @@ -0,0 +1,61 @@ +package seedu.duke; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java new file mode 100644 index 0000000000..8db41389ee --- /dev/null +++ b/src/main/java/seedu/duke/Duke.java @@ -0,0 +1,84 @@ +package seedu.duke; + +import seedu.duke.Command.Command; +import seedu.duke.Parser.Parser; +import seedu.duke.Storage.Storage; +import seedu.duke.TaskList.TaskList; +import seedu.duke.Ui.Ui; + + + +import java.io.IOException; + +public class Duke { + + private Storage storage; + private TaskList tasks; + private Ui ui; + + public Duke(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (IOException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } + } + + public Duke() { + this("seedu.duke.txt"); + } + + public void run() throws DukeException { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); // show the divider line ("_______") + Command c = Parser.parse(fullCommand); + c.execute(tasks, storage); + isExit = c.isExit(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + ui.showLine(); + } + } + } + + public static void main(String[] args) throws DukeException { + new Duke("seedu.duke.txt").run(); + } + + public String getResponse(String input) throws IOException { + Command c = Parser.parse(input); + c.execute(tasks, storage); + return c.getResponse(); + } + + public String getWelcome() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + return "Hello from\n" + logo; + } +} + + + + + + + + + + + + + + diff --git a/src/main/java/seedu/duke/DukeException.java b/src/main/java/seedu/duke/DukeException.java new file mode 100644 index 0000000000..574f622e90 --- /dev/null +++ b/src/main/java/seedu/duke/DukeException.java @@ -0,0 +1,10 @@ +package seedu.duke; + +public class DukeException extends Exception { + public DukeException(String s) { + super(s); + } + + public DukeException() { + } +} diff --git a/src/main/java/seedu/duke/Launcher.java b/src/main/java/seedu/duke/Launcher.java new file mode 100644 index 0000000000..9c10b25fb1 --- /dev/null +++ b/src/main/java/seedu/duke/Launcher.java @@ -0,0 +1,9 @@ +package seedu.duke; + +import javafx.application.Application; + +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} diff --git a/src/main/java/seedu/duke/Main.java b/src/main/java/seedu/duke/Main.java new file mode 100644 index 0000000000..3bccf61c12 --- /dev/null +++ b/src/main/java/seedu/duke/Main.java @@ -0,0 +1,32 @@ +package seedu.duke; + + +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke(); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/seedu/duke/MainWindow.java b/src/main/java/seedu/duke/MainWindow.java new file mode 100644 index 0000000000..c2ddb10431 --- /dev/null +++ b/src/main/java/seedu/duke/MainWindow.java @@ -0,0 +1,55 @@ +package seedu.duke; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; + + +import java.io.IOException; + +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/user.jpeg")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/duke.jpeg")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() throws 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/seedu/duke/Parser/Parser.java b/src/main/java/seedu/duke/Parser/Parser.java new file mode 100644 index 0000000000..9b589e173f --- /dev/null +++ b/src/main/java/seedu/duke/Parser/Parser.java @@ -0,0 +1,115 @@ +package seedu.duke.Parser; + +import seedu.duke.TaskList.Deadlines; +import seedu.duke.TaskList.Events; +import seedu.duke.TaskList.ToDos; +import seedu.duke.Command.Command; +import seedu.duke.Command.AddCommand; +import seedu.duke.Command.ByeCommand; +import seedu.duke.Command.DeleteCommand; +import seedu.duke.Command.DoneCommand; +import seedu.duke.Command.ListCommand; +import seedu.duke.Command.FindCommand; + +public class Parser { + public Parser() {} + + /** + * Parse in the full command and return the command accordingly + * @param fullCommand + * @return Command + */ + public static Command parse(String fullCommand) { + int i = fullCommand.indexOf(' '); + int j = fullCommand.indexOf("/"); + int length = fullCommand.length(); + String first = getFirstWord(fullCommand); + switch (first) { + case "delete": + return new DeleteCommand(Integer.parseInt(fullCommand.substring(i + 1))); + case "done": + return new DoneCommand(Integer.parseInt(fullCommand.substring(i + 1))); + case "list": + return new ListCommand(); + case "bye": + return new ByeCommand(); + case "find": + return new FindCommand(fullCommand.substring((i + 1))); + case "todo": + if (length == 4) { + System.out.println("OOPS!!! The description of a todo cannot be empty."); + return null; + } else { + ToDos taskTodo = new ToDos(fullCommand.substring(i + 1)); + taskTodo.setTaskType("T"); + return new AddCommand(taskTodo); + } + case "t": + if (length == 1) { + System.out.println("OOPS!!! The description of a todo cannot be empty."); + return null; + } else { + ToDos taskTodo = new ToDos(fullCommand.substring(i + 1)); + taskTodo.setTaskType("T"); + return new AddCommand(taskTodo); + } + case "deadline": + if (length == 8) { + System.out.println("OOPS!!! The description of a deadline cannot be empty."); + return null; + } else { + Deadlines taskDeadline = new Deadlines(fullCommand.substring(i + 1, j - 1)); + taskDeadline.setTime(fullCommand.substring(j + 4)); + taskDeadline.setTaskType("D"); + return new AddCommand(taskDeadline); + } + case "d": + if (length == 1) { + System.out.println("OOPS!!! The description of a deadline cannot be empty."); + return null; + } else { + Deadlines taskDeadline = new Deadlines(fullCommand.substring(i + 1, j - 1)); + taskDeadline.setTime(fullCommand.substring(j + 4)); + taskDeadline.setTaskType("D"); + return new AddCommand(taskDeadline); + } + case "event": + if (length == 5) { + System.out.println("OOPS!!! The description of an event cannot be empty."); + return null; + } else { + Events taskEvent = new Events(fullCommand.substring(i + 1, j - 1)); + taskEvent.setTime(fullCommand.substring(j + 4)); + taskEvent.setTaskType("E"); + return new AddCommand(taskEvent); + } + case "e": + if (length == 1) { + System.out.println("OOPS!!! The description of an event cannot be empty."); + return null; + } else { + Events taskEvent = new Events(fullCommand.substring(i + 1, j - 1)); + taskEvent.setTime(fullCommand.substring(j + 4)); + taskEvent.setTaskType("E"); + return new AddCommand(taskEvent); + } + default: + System.out.println("OOPS!!! I'm sorry, but I don't know what that means :-()"); + assert false : first; + return null; + } + } + + /** + * to get the first word in a string of input + * @param input + * @return String + */ + public static String getFirstWord(String input) { + if (input.indexOf(" ") > -1) { + return input.substring(0, input.indexOf(" ")); + } else { + return input; + } + } + } \ No newline at end of file diff --git a/src/main/java/seedu/duke/Storage/Storage.java b/src/main/java/seedu/duke/Storage/Storage.java new file mode 100644 index 0000000000..9c46f6a3fb --- /dev/null +++ b/src/main/java/seedu/duke/Storage/Storage.java @@ -0,0 +1,124 @@ +package seedu.duke.Storage; + +import seedu.duke.TaskList.TaskList; +import seedu.duke.TaskList.Task; +import seedu.duke.TaskList.ToDos; +import seedu.duke.TaskList.Deadlines; +import seedu.duke.TaskList.Events; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.Scanner; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.List; +import java.text.DateFormat; +import java.text.ParseException; + +public class Storage { + + public final String FILEPATH; + + public Storage(String filepath) { + this.FILEPATH = filepath; + } + + /** + * store text into a file + * @param filePath + * @param textToAdd + * @throws IOException + */ + public void writeToFile(String filePath, String textToAdd) throws IOException { + FileWriter fw = new FileWriter((filePath)); + fw.write(textToAdd); + fw.close(); + } + + /** + * append the text in a file without over-writing the content inside + * @param filePath + * @param textToAppend + * @throws IOException + */ + public void appendToFile(String filePath, String textToAppend) throws IOException { + FileWriter fw = new FileWriter(filePath, true); // create a FileWriter in append mode + fw.write(textToAppend); + fw.close(); + } + + /** + * save the tasks into the file + * @param taskList + * @throws IOException + */ + public void save(TaskList taskList) throws IOException { + if(taskList.list.size() != 0) { + writeToFile(FILEPATH, taskList.list.get(0).toString() + System.lineSeparator()); + + for (int i = 1; i < taskList.list.size(); i++) { + appendToFile(FILEPATH, taskList.list.get(i).toString() + System.lineSeparator()); + } + } + } + + /** + * to load the list from the file + * @return + * @throws IOException + */ + public ArrayList load() throws IOException { + ArrayList taskList = new ArrayList<>(100); + Path path = Paths.get(FILEPATH); + Scanner scanner = new Scanner(path); + while(scanner.hasNextLine()) { + String line = scanner.nextLine(); + Task task = getTask(line).get(); + taskList.add(task); + } + return taskList; + } + + /** + * to get the task from a string of input + * @param line + * @return + */ + private Optional getTask(String line) { + try { + List data = Stream.of(line.split("\\|")).map(String::trim).collect(Collectors.toList()); + Task task; + switch (data.get(0)) { + case "T": + task = new ToDos(data.get(2)); + task.setTaskType("T"); + break; + case "D": + task = new Deadlines(data.get(2)); + task.setTaskType("D"); + task.setTime(data.get(3)); + break; + case "E": + task = new Events(data.get(2)); + task.setTaskType("E"); + task.setTime(data.get(3)); + break; + default: + throw new ParseException(data.get(0) + " is not an acceptable argument", 0); + } + if (data.get(1).equals(1)) { + task.markAsDone(); + } + return Optional.of(task); + } catch (ParseException e) { + e.printStackTrace(); + return Optional.empty(); + } + } + } + diff --git a/src/main/java/seedu/duke/TaskList/Deadlines.java b/src/main/java/seedu/duke/TaskList/Deadlines.java new file mode 100644 index 0000000000..47d7568370 --- /dev/null +++ b/src/main/java/seedu/duke/TaskList/Deadlines.java @@ -0,0 +1,20 @@ +package seedu.duke.TaskList; + +import seedu.duke.TaskList.Task; + +public class Deadlines extends Task { + + public Deadlines(String description) { + super(description); + } + + @Override + public String toString() { + return "D | " + this.getStatusNumber() + " | " + this.description + " | " + this.time; + } + + @Override + public String toActionString() { + return "[" + this.taskType + "][" + this.getStatusIcon() + "] " + this.description + " (by: " + this.time + ")"; + } +} diff --git a/src/main/java/seedu/duke/TaskList/Events.java b/src/main/java/seedu/duke/TaskList/Events.java new file mode 100644 index 0000000000..ee34817456 --- /dev/null +++ b/src/main/java/seedu/duke/TaskList/Events.java @@ -0,0 +1,20 @@ +package seedu.duke.TaskList; + +import seedu.duke.TaskList.Task; + +public class Events extends Task { + + public Events(String description) { + super(description); + } + + @Override + public String toString() { + return "E | " + this.getStatusNumber() + " | " + this.description + " | " + this.time; + } + + @Override + public String toActionString() { + return "[" + this.taskType + "][" + this.getStatusIcon() + "] " + this.description + " (at: " + this.time + ")"; + } +} diff --git a/src/main/java/seedu/duke/TaskList/Task.java b/src/main/java/seedu/duke/TaskList/Task.java new file mode 100644 index 0000000000..8d1d5fe577 --- /dev/null +++ b/src/main/java/seedu/duke/TaskList/Task.java @@ -0,0 +1,96 @@ +package seedu.duke.TaskList; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class Task { + + public String taskType; + public String description; + public String time; + private boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getStatusIcon() { + return (isDone ? "\u2713" : "\u2718"); //return tick or X symbols + } + + public int getStatusNumber() { + if (isDone) { + return 1; + } else { + return 0; + } + } + + public void setTaskType(String type) { + this.taskType = type; + } + + public void markAsDone() { + this.isDone = true; + } + + public void setTime(String time) { + if (time.contains("/")) { + DateFormat df = new SimpleDateFormat("d/MM/yyyy HHmm"); + try { + Date output = df.parse(time); + String result = new SimpleDateFormat("d 'of' MMMM yyyy, hmm aa").format(output); + this.time = format(result); + } catch (ParseException e) { + e.printStackTrace(); + } + } else { + this.time = time; + } + } + + public String format(String strTime) { + String[] str = strTime.split(" "); + String day; + String aa; + int time; + + switch (Integer.parseInt(str[0])) { + case 1: + day = "1st"; + case 2: + day = "2nd"; + case 3: + day = "3rd"; + default: + day = str[0] + "th"; + } + + switch (str[5]) { + + case "AM": + aa = "am"; + default: + aa = "pm"; + } + + if (Integer.parseInt(str[4]) % 100 == 0) { + time = Integer.parseInt(str[4]) / 100; + } else { + time = Integer.parseInt(str[4]); + } + + return day + " " + str[1] + " " + str[2] + " " + str[3] + " " + time + aa; + } + + public String toString() { + return this.taskType; + } + + public String toActionString() { + return null; + } +} diff --git a/src/main/java/seedu/duke/TaskList/TaskList.java b/src/main/java/seedu/duke/TaskList/TaskList.java new file mode 100644 index 0000000000..de55be10c2 --- /dev/null +++ b/src/main/java/seedu/duke/TaskList/TaskList.java @@ -0,0 +1,33 @@ +package seedu.duke.TaskList; + +import java.io.IOException; +import java.util.ArrayList; + +public class TaskList { + public static ArrayList list = new ArrayList<>(100); + + public TaskList() {} + + public TaskList(ArrayList list) { + this.list = list; + } + + public void addList(Task t) { + list.add(t); + } + + public void delete(int taskNo) { + list.remove(taskNo - 1); + } + + public ArrayList contains(String word) { + ArrayList result = new ArrayList(); + for (Task task: list) { + if (task.description.contains(word)) { + result.add(task); + } + } + return result; + } +} + diff --git a/src/main/java/seedu/duke/TaskList/ToDos.java b/src/main/java/seedu/duke/TaskList/ToDos.java new file mode 100644 index 0000000000..40c27b0e91 --- /dev/null +++ b/src/main/java/seedu/duke/TaskList/ToDos.java @@ -0,0 +1,20 @@ +package seedu.duke.TaskList; + +import seedu.duke.TaskList.Task; + +public class ToDos extends Task { + + public ToDos(String description) { + super(description); + } + + @Override + public String toString() { + return "T | " + this.getStatusNumber() + " | " + this.description; + } + + @Override + public String toActionString() { + return "[" + this.taskType + "][" + this.getStatusIcon() + "] " + this.description; + } +} diff --git a/src/main/java/seedu/duke/Ui/Ui.java b/src/main/java/seedu/duke/Ui/Ui.java new file mode 100644 index 0000000000..d73d99f576 --- /dev/null +++ b/src/main/java/seedu/duke/Ui/Ui.java @@ -0,0 +1,112 @@ +package seedu.duke.Ui; + +import seedu.duke.DukeException; +import seedu.duke.TaskList.TaskList; +import seedu.duke.TaskList.Task; + +import java.io.InputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Scanner; + +public class Ui { + + public static final String LINE_PREFIX = "| "; + + private static final String LS = System.lineSeparator(); + + private static final String DIVIDER = "____________________________________________________________"; + + private final Scanner in; + private final PrintStream out; + + public Ui() { + this(System.in, System.out); + } + + public Ui(InputStream in, PrintStream out) { + this.in = new Scanner(in); + this.out = out; + } + + public String readCommand() throws DukeException { + String fullInputLine = in.nextLine(); + + while (shoudIgnore(fullInputLine)) { + fullInputLine = in.nextLine(); + } + return fullInputLine; + } + + private boolean shoudIgnore(String fullInputLine) { + return fullInputLine.trim().isEmpty(); + } + + public void showWelcome() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println("Hello from\n" + logo); + showToUser(DIVIDER, "Hello! I'm Duke\n" + "What can I do for you?", DIVIDER); + } + + public void showLine() { + showToUser(DIVIDER); + } + + public void showGoodbye() { + showToUser(DIVIDER, "Bye! Hope to see you again soon!", DIVIDER); + } + + public void showAddedMessage(TaskList taskList, Task task) { + showToUser("Got it. I've added this task:"); + showToUser(task.toActionString()); + showToUser("Now you have " + taskList.list.size() + + " tasks in the list"); + } + + public void showDeleteMessage(TaskList taskList, Task task) { + showToUser("Noted. I've removed this task: "); + showToUser(task.toActionString()); + showToUser("Now you have " + (taskList.list.size() - 1) + + " tasks in the list."); + } + + public void showDoneMessage(Task task) { + showToUser("Nice! I've marked this task as done:"); + showToUser(task.toActionString()); + } + + public void showError(String error) { + showToUser(error); + } + + public void showToUser (String... message) { + for (String m: message) { + out.println(m.replace("\n", LS)); + } + } + + public void showLoadingError() { + showToUser("There is something wrong."); + } + + public void showListMessage(TaskList taskList) { + showToUser("Here are the tasks in your list:"); + for(int i = 1;i <= taskList.list.size();i++) { + showToUser(i + "." + + taskList.list.get(i - 1).toActionString()); + } + } + + public void showFindMessage(ArrayList result) { + showToUser("Here are the matching tasks in your list:"); + for (Task task: result) { + showToUser(task.toActionString()); + } + } + +} + diff --git a/src/main/java/seedu/duke/duke.txt b/src/main/java/seedu/duke/duke.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/main/java/seedu/duke/duke.txt @@ -0,0 +1 @@ + diff --git a/src/main/resources/images/duke.jpeg b/src/main/resources/images/duke.jpeg new file mode 100644 index 0000000000..77db3af3bf Binary files /dev/null and b/src/main/resources/images/duke.jpeg differ diff --git a/src/main/resources/images/user.jpeg b/src/main/resources/images/user.jpeg new file mode 100644 index 0000000000..fc34e51f00 Binary files /dev/null and b/src/main/resources/images/user.jpeg differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..ede775d4f9 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..98061ad93c --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +