diff --git a/.gitignore b/.gitignore index 99712178bf..18b977dea0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ src/main/resources/docs/ .DS_Store *.iml bin/ + +# ignore output +*.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..f5c99a7f66 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java \ No newline at end of file diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..df98b61614 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: main + diff --git a/TestOutput.txt b/TestOutput.txt new file mode 100644 index 0000000000..8a4092417a --- /dev/null +++ b/TestOutput.txt @@ -0,0 +1,2 @@ +T | 0 | read book +E | 0 | party | 04/07/2019 2359 \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..9a3f1f0490 --- /dev/null +++ b/build.gradle @@ -0,0 +1,70 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' + //id 'org.openjfx.javafxplugin' version '0.0.7' +} + +shadowJar { + archiveBaseName = "duke" + archiveVersion = "0.2" + archiveClassifier = null + archiveAppendix = null +} + +checkstyle { + toolVersion = '8.23' +} + +repositories { + mavenCentral() +} + +//javafx { +// version = "11.0.2" +// modules = [ 'javafx.controls', 'javafx.fxml' ] +//} + +run { + standardInput = System.in +} + +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' + implementation group: 'org.openjfx', name: 'javafx-media', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-media', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-media', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-web', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-web', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-web', version: javaFxVersion, classifier: 'linux' + testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0' +} + +test { + useJUnitPlatform() +} + +group 'seedu.duke' +version '0.1.0' + +repositories { + mavenCentral() +} + +application { + // Change this to your main class. + mainClassName = "Launcher" + } 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..2356b15aba 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,19 +2,172 @@ ## Features -### Feature 1 -Description of feature. +## Tasks +Allows to save, edit & follow up on daily tasks. -## Usage +### Commands -### `Keyword` - Describe action +### `todo` -Describe action and its outcome. +Adds a todo task. +
Input in the format: __todo < task >__ Example of usage: -`keyword (optional arguments)` +`todo read book` Expected outcome: -`outcome` + Got it. I've added this task: + [T][:(] read book + Now you have 1 tasks in the list. + +### `deadline` + +Adds a task with deadline. +
Input text in the format: __deadline < task > /by < DD/MM/YYYY HHMM>__ + +Example of usage: + +`deadline submission /by 02/03/2019 1800` + +Expected outcome: + + Got it. I've added this task: + [D][:(] submission (by: 02/03/2019 1800) + Now you have 2 tasks in the list. + +### `event` + +Adds a event task. +
Input text in the format: __event < task > /at < DD/MM/YYYY HHMM>__ + +Example of usage: + +`event party /at 5/6/2012 1400` + +Expected outcome: + + Got it. I've added this task: + [E][:(] party (at: 05/06/2012 1400) + Now you have 3 tasks in the list. + +### `list` + +shows all tasks +
Input text: __list__ + +Example of usage: + +`list` + +Expected outcome: + + 1. [T][:(] read book + 2. [D][:(] submission (by: 02/03/2019 1800) + 3. [E][:(] party (at: 05/06/2012 1400) + +### `done` + +marks task as done +
Input text: __done < digit of task in list >__ + +Example of usage: + +`done 1` + +Expected outcome: + + Nice! I've marked this task as done: + [T][:)] read book + +### `delete` + +deletes the task from the list of tasks +
Input text: __delete < digit of task in list >__ + +Example of usage: + +`delete 2` + +Expected outcome: + + Noted. I've removed this task: + [D][:(] submission (by: 02/03/2019 1800) + Now you have 2 tasks in the list. + +### `find` + +finds tasks that contain user's input and returns a list of results +
Input text: __find < word to find >__ + +Example of usage: + +`find book` + +Expected outcome: + + Here are the matching tasks in your list: + 1. [T][:)] read book + +## Expenses +Allows to save & follow up on daily expenses. + +### Commands + +### `spent` + +allows user to input expenditures and tag it if required +
Input text: __spent < item > /for < cost > # < tag name >__ + +Example of usage: + +`spent eggs /for 8.99 #food` + +Expected outcome: + + Your expense has been added to the list: + (ID: 1) eggs | 8.99 + +### `expenses` + +states all expenses in a list format +
Input text: __expenses__ + +Example of usage: + +`expenses` + +Expected outcome: + + #food + 1. (ID: 1) eggs | 8.99 + + #studies + 1. (ID: 2) pencils | 0.99 + + Total: 9.98 + +### `deleteExpense` + +allows user to delete an expense from the list by using its ID +
Input text: __deleteExpense < ID of the expense >__ + +Example of usage: + +`deleteExpense 2` + +Expected outcome: + + Noted. I've removed this expense: + (ID: 2) pencils | 0.99 + Now you have 1 expenses in the list. + +### `bye` + +close the app bye typing in 'bye' + +Example of usage: +`bye` + +#That's All! Have Fun Now!!! \ No newline at end of file diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..dec1e7bcd2 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..2f7efbeab5 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ 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..5c2d1cf016 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..c0a21e5112 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Sep 04 15:08:22 SGT 2019 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.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..8e25e6c19d --- /dev/null +++ b/gradlew @@ -0,0 +1,188 @@ +#!/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, 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..9618d8d960 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,100 @@ +@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 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/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..0a9ba44a3f --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'duke' +include 'resources' + diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java new file mode 100644 index 0000000000..974a3b59d0 --- /dev/null +++ b/src/main/java/DialogBox.java @@ -0,0 +1,66 @@ +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 java.io.IOException; +import java.util.Collections; + +/** + * An example of a custom control using FXML. + */ +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + 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); + } + + /** + * creates a flipped DialogBox. + * + * @param text is the string output that Duke says + * + * @param img is the image of Duke + * + * @return the DialogBox to be shown + */ + 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.java b/src/main/java/Duke.java index 5d313334cc..3b5963be1c 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,116 @@ +import exceptions.DukeException; +import utilities.ExpenseList; +import utilities.Ui; +import utilities.Parser; +import utilities.Storage; +import utilities.TaskList; + + +/** + * Main entry point. + */ public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + + private Storage storage; + private TaskList tasks; + private Ui ui; + private ExpenseList expenses; + + + /** + * constructor for Duke. + * + * @param filePath is the filename of the text file + * + * @throws Exception in case file is not able to load + */ + private Duke(String filePath) throws Exception { + ui = new Ui(); + storage = new Storage(filePath); + expenses = new ExpenseList(); + try { + tasks = new TaskList(storage.load()); + expenses = new ExpenseList(storage.loadExpenses()); + } catch (DukeException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } } + + /** + * constructor for Duke when launcher is running. + * + * @throws Exception in case file is unable to load + */ + Duke() throws Exception { + ui = new Ui(); + storage = new Storage("data/DukeOutput.txt"); + expenses = new ExpenseList(storage.loadExpenses()); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } + } + + + /** + * asks ui to run the application. + */ + private void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); + command.Command c = Parser.parse(fullCommand); + //c.execute(tasks, ui, storage); + String result = c.executeAsString(tasks, ui, storage, expenses); + System.out.println(result); + isExit = c.isExit(); + } catch (DukeException e) { + ui.showError(e.getMessage()); + } catch (Exception e) { + System.out.println(e.getMessage()); + } finally { + ui.showLine(); + } + } + } + + + /** + * entry point of Duke. + * + * @param args are standard feature + * + * @throws Exception in case file is not found + */ + public static void main(String[] args) throws Exception { + new Duke("data/DukeOutput.txt").run(); + System.exit(0); + } + + + /** + * to get response the display it. + * + * @param input is the input command + * + * @return the String output message + */ + String getResponse(String input) { + try { + command.Command c = Parser.parse(input); + return c.executeAsString(tasks, ui, storage, expenses); + } catch (DukeException e) { + return ui.showErrorFX(e.getMessage()); + } catch (Exception e) { + return e.getMessage(); + } + } + + } diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java new file mode 100644 index 0000000000..57071d9c70 --- /dev/null +++ b/src/main/java/Launcher.java @@ -0,0 +1,11 @@ +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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..f578f7960d --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Launcher + diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000000..c6eec61797 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,33 @@ +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +import java.io.IOException; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke(); + + public Main() throws Exception { + } + + @Override + public void start(Stage stage) throws Exception { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + stage.setTitle("BabyDuke"); + 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/MainWindow.java b/src/main/java/MainWindow.java new file mode 100644 index 0000000000..9e2d6b8331 --- /dev/null +++ b/src/main/java/MainWindow.java @@ -0,0 +1,53 @@ +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; + +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/pic1.jpg")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/pic2.jpg")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) throws Exception { + 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() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + if (response.equals("Bye. Hope to see you again soon!")) { + System.exit(0); + } + } +} \ No newline at end of file diff --git a/src/main/java/command/ByeCommand.java b/src/main/java/command/ByeCommand.java new file mode 100644 index 0000000000..3e6793e20c --- /dev/null +++ b/src/main/java/command/ByeCommand.java @@ -0,0 +1,21 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class ByeCommand extends Command { + public ByeCommand(String command) { + super(command); + } + + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) { + return ui.showConclusionFX(); + } + + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java new file mode 100644 index 0000000000..fcb214d85c --- /dev/null +++ b/src/main/java/command/Command.java @@ -0,0 +1,19 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public abstract class Command { + protected String command; + + Command(String command) { + this.command = command; + } + + public abstract String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) + throws Exception; + + public abstract boolean isExit(); +} diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java new file mode 100644 index 0000000000..b0b2e44d89 --- /dev/null +++ b/src/main/java/command/DeleteCommand.java @@ -0,0 +1,46 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class DeleteCommand extends Command { + + public DeleteCommand(String command) { + super(command); + } + + /** + * Executes deletion. + * + * @param tasks is TaskList of tasks + * + * @param ui returns strings + * + * @param storage is for output + * + * @return the final string command that has to be printed + */ + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) { + String[]splitWords = command.split(" "); + + try { + int val = Integer.parseInt(splitWords[1]); + assert val <= (tasks.size()) : "Enter a smaller number"; + String result = ui.deleteMessageFX(val - 1, tasks); + tasks.remove(val - 1); + storage.updateFile(tasks, expenses); + return result; + } catch (AssertionError f) { + return f.getMessage(); + } catch (Exception e) { + return "File not found"; + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/DeleteExpenseCommand.java b/src/main/java/command/DeleteExpenseCommand.java new file mode 100644 index 0000000000..c4ed5ab98e --- /dev/null +++ b/src/main/java/command/DeleteExpenseCommand.java @@ -0,0 +1,35 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class DeleteExpenseCommand extends Command { + public DeleteExpenseCommand(String command) { + super(command); + } + + @Override + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) throws Exception { + String[]splitWords = command.split(" "); + + try { + int val = Integer.parseInt(splitWords[1]); + assert val <= (expenses.size()) : "Enter a smaller number"; + String result = ui.deleteExpenseMessage(val - 1, expenses); + expenses.remove(val - 1); + storage.updateFile(tasks, expenses); + return result; + } catch (AssertionError f) { + return f.getMessage(); + } catch (Exception e) { + return "File not found"; + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/DoneCommand.java b/src/main/java/command/DoneCommand.java new file mode 100644 index 0000000000..0f53433925 --- /dev/null +++ b/src/main/java/command/DoneCommand.java @@ -0,0 +1,41 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class DoneCommand extends Command { + public DoneCommand(String command) { + super(command); + } + + /** + * Executes the change of tasks status to done. + * + * @param tasks is the taskList of tasks + * + * @param ui prints the return statements + * + * @param storage prints to the output + * + * @return the command to be printed + */ + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) { + String[]splitWords = command.split(" "); + + try { + int val = Integer.parseInt(splitWords[1]); + tasks.taskDone(val - 1); + storage.updateFile(tasks, expenses); + return ui.doneMessageFX(val - 1, tasks); + } catch (Exception e) { + return "Error, you have entered an invalid number"; + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/ExpenseCommand.java b/src/main/java/command/ExpenseCommand.java new file mode 100644 index 0000000000..1de827e5df --- /dev/null +++ b/src/main/java/command/ExpenseCommand.java @@ -0,0 +1,32 @@ +package command; + +import expense.RandomExpense; +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class ExpenseCommand extends Command { + + public ExpenseCommand(String command) { + super(command); + } + + @Override + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) { + try { + String mainCommand = command.substring(6); + RandomExpense item = new RandomExpense(mainCommand); + expenses.add(item); + storage.updateFile(tasks, expenses); + return ui.expenseMessage(item); + } catch (Exception e) { + return "Enter proper format"; + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/ExpenseListCommand.java b/src/main/java/command/ExpenseListCommand.java new file mode 100644 index 0000000000..9049465ce8 --- /dev/null +++ b/src/main/java/command/ExpenseListCommand.java @@ -0,0 +1,23 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class ExpenseListCommand extends Command { + + public ExpenseListCommand(String command) { + super(command); + } + + @Override + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) throws Exception { + return ui.expenseListMessage(expenses); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java new file mode 100644 index 0000000000..1efa4f3ff9 --- /dev/null +++ b/src/main/java/command/FindCommand.java @@ -0,0 +1,44 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class FindCommand extends Command { + public FindCommand(String command) { + super(command); + } + + /** + * Executes finding process. + * + * @param tasks is the taskList of tasks + * + * @param ui prints the output + * + * @param storage is for output file + * + * @return the output to be printed on screen + */ + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) { + String[]splitWords = command.split(" ",2); + String wordToFind = splitWords[1]; + TaskList findResults = new TaskList(); + + for (int i = 0; i < tasks.size(); i++) { + String taskCommand = tasks.get(i).getCommand(); + if (taskCommand.contains(wordToFind)) { + findResults.add(tasks.get(i)); + } + } + + return ui.findCommandFX(findResults); + + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/HelloCommand.java b/src/main/java/command/HelloCommand.java new file mode 100644 index 0000000000..59588bbaf1 --- /dev/null +++ b/src/main/java/command/HelloCommand.java @@ -0,0 +1,22 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class HelloCommand extends Command { + public HelloCommand(String command) { + super(command); + } + + @Override + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) throws Exception { + return ui.showWelcomeFX(); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java new file mode 100644 index 0000000000..d69e710de1 --- /dev/null +++ b/src/main/java/command/ListCommand.java @@ -0,0 +1,32 @@ +package command; + +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class ListCommand extends Command { + public ListCommand(String command) { + super(command); + } + + /** + * executes the printing of list. + * + * @param tasks is the taskList of tasks + * + * @param ui prints the output + * + * @param storage manages the output file + * + * @return the output String + */ + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) { + return ui.listCommandFX(tasks); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/command/TaskCommand.java b/src/main/java/command/TaskCommand.java new file mode 100644 index 0000000000..a9005085f4 --- /dev/null +++ b/src/main/java/command/TaskCommand.java @@ -0,0 +1,59 @@ +package command; + +import exceptions.DukeException; +import task.Deadline; +import task.Event; +import task.ToDo; +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; +import utilities.Ui; + +public class TaskCommand extends Command { + public TaskCommand(String command) { + super(command); + } + + /** + * executes creating tasks. + * + * @param tasks is the taskList of tasks + * + * @param ui prints the output + * + * @param storage manages the output file + * + * @return the string output that is to be printed + * + * @throws DukeException when incorrect command is inputted + */ + public String executeAsString(TaskList tasks, Ui ui, Storage storage, ExpenseList expenses) throws DukeException { + try { + String[]splitWords = command.trim().split("\\s",2); + + if (splitWords[0].equals("todo")) { + ToDo.createTodo(command, tasks, storage, expenses); + } else if (splitWords[0].equals("deadline")) { + Deadline.createDeadline(command, tasks, storage, expenses); + } else if (splitWords[0].equals("event")) { + Event.createEvent(command, tasks, storage, expenses); + } else { + throw new DukeException(""); + } + + return "Got it. I've added this task:" + "\n" + tasks.printLatest() + + "\n" + "Now you have " + tasks.size() + " tasks in the list."; + + } catch (IllegalArgumentException e) { + return "☹ OOPS!!! The date format is wrong."; + } catch (DukeException e) { + throw new DukeException(""); + } + } + + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/exceptions/DukeException.java b/src/main/java/exceptions/DukeException.java new file mode 100644 index 0000000000..21b8700036 --- /dev/null +++ b/src/main/java/exceptions/DukeException.java @@ -0,0 +1,15 @@ +package exceptions; + +/** + * Exception class for incorrect date format. + */ +public class DukeException extends Exception { + /** + * exception for incorrect date format. + * + * @param message is dummy parameter + */ + public DukeException(String message) { + super(message); + } +} diff --git a/src/main/java/expense/Expense.java b/src/main/java/expense/Expense.java new file mode 100644 index 0000000000..60e7489fe1 --- /dev/null +++ b/src/main/java/expense/Expense.java @@ -0,0 +1,45 @@ +package expense; + +public abstract class Expense { + private String command; + private double cost; + private String tagName; + private int id; + + /** + * constructor. + * + * @param command is the command + */ + public Expense(String command) { + this.command = command; + } + + /** + * to change cost. + * + * @param newCost is the new price + */ + public abstract void editExpense(int newCost); + + /** + * to get cost. + * + * @return the cost + */ + public abstract double getCost(); + + /** + * to return output to be printed. + */ + @Override + public abstract String toString(); + + public abstract String toSubString(); + + public abstract String getTagName(); + + public abstract void setId(int x); + + public abstract int getId(); +} diff --git a/src/main/java/expense/RandomExpense.java b/src/main/java/expense/RandomExpense.java new file mode 100644 index 0000000000..57d139bed8 --- /dev/null +++ b/src/main/java/expense/RandomExpense.java @@ -0,0 +1,73 @@ +package expense; + +public class RandomExpense extends Expense { + private double cost; + private String name; + private String tagName; + private int id; + + /** + * Constructor. + * + * @param command is the user input + * + * @throws Exception if user does not enter number in the proper format + */ + public RandomExpense(String command) throws Exception { + super(command); + + String[]nameAndCost = command.split("#", 2); + try { + tagName = nameAndCost[1]; + } catch (ArrayIndexOutOfBoundsException e) { + tagName = "not Tagged"; + } + + String[]parts = nameAndCost[0].split("/for", 2); + + + try { + cost = Double.parseDouble(parts[1]); + } catch (NumberFormatException e) { + throw new Exception(); + } + name = parts[0]; + + } + + public void editExpense(int newCost) { + cost = newCost; + } + + @Override + public double getCost() { + return cost; + } + + @Override + public String toString() { + return name + " | " + cost + " | " + tagName; + } + + @Override + public String toSubString() { + return "(ID: " + id + ") " + name + " | " + cost; + } + + @Override + public String getTagName() { + return tagName; + } + + @Override + public void setId(int x) { + id = x; + } + + @Override + public int getId() { + return id; + } + + +} diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java new file mode 100644 index 0000000000..29dc2386d7 --- /dev/null +++ b/src/main/java/task/Deadline.java @@ -0,0 +1,122 @@ +package task; + +import exceptions.DukeException; +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * contains actions for deadline task. + */ +public class Deadline extends Task { + + private String midCommand; + private String formattedDate; + + /** + * constructor for child class. + * + * @param command is the user input string + * + * @throws IllegalArgumentException in case date is not entered in the correct format + */ + public Deadline(String command) throws IllegalArgumentException { + super(command); + this.done = false; + String[]splitUpDate = command.split("/",2); + + try { + SimpleDateFormat ft = new SimpleDateFormat("dd/MM/yyyy HHmm"); + Date formalDate = ft.parse(splitUpDate[1].substring(3)); + + DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HHmm"); + formattedDate = dateFormat.format(formalDate); + + } catch (Exception e) { + throw new IllegalArgumentException(); + } + + midCommand = splitUpDate[0]; + } + + /** + * to print for "list" command. + * + * @return string in the format required + */ + public String printer() { + if (done) { + return "[D][:)] " + midCommand + "(by: " + formattedDate + ")"; + } else { + return "[D][:(] " + midCommand + "(by: " + formattedDate + ")"; + } + } + + /** + * to print for text file. + * + * @return string in the format required + */ + public String printToOutput() { + if (done) { + return "D | 1 | " + midCommand + " | " + formattedDate; + } else { + return "D | 0 | " + midCommand + " | " + formattedDate; + } + } + + /** + * read user input as convert it into a Task. + * + * @param s is the user input string + * + * @return a Task.Task (Task.Deadline) object + */ + public static Task outputAsDeadline(String s) { + String[]segments = s.split("\\|"); + String taskCommand = segments[2].trim() + " /by: " + segments[3].trim(); + Deadline newTask = new Deadline(taskCommand); + + if (segments[1].equals(" 1 ")) { + newTask.taskDone(); + } + + return newTask; + } + + /** + * Creates a new deadline task. + * + * @param command is the user string input to be processed + * + * @throws DukeException in case user inputs in an incorrect format + */ + public static void createDeadline(String command, TaskList tasks, Storage storage, ExpenseList expenses) + throws DukeException { + String[]splitWords = command.trim().split("\\s",2); + String midCommand = splitWords[1].trim(); + + try { + if (midCommand.length() != 0) { + tasks.add(new Deadline(midCommand)); + storage.updateFile(tasks, expenses); + } else { + throw new Exception(); + } + } catch (Exception e) { + throw new DukeException(""); + } + } + + /** + * marks when task is done. + */ + public void taskDone() { + done = true; + } +} + diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java new file mode 100644 index 0000000000..db5c9e6cc9 --- /dev/null +++ b/src/main/java/task/Event.java @@ -0,0 +1,120 @@ +package task; + +import exceptions.DukeException; +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * contains actions for event task. + */ +public class Event extends Task { + private String midCommand; + private String formattedDate; + + /** + * constructor for child class. + * + * @param command is the user input string + * + * @throws IllegalArgumentException in case date is not entered in the correct format + */ + public Event(String command) throws IllegalArgumentException { + super(command); + this.done = false; + String[]splitUpDate = command.split("/",2); + + try { + SimpleDateFormat ft = new SimpleDateFormat("dd/MM/yyyy HHmm"); + Date formalDate = ft.parse(splitUpDate[1].substring(3)); + + DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HHmm"); + formattedDate = dateFormat.format(formalDate); + + } catch (Exception e) { + throw new IllegalArgumentException(); + } + + midCommand = splitUpDate[0]; + } + + /** + * to print for "list" command. + * + * @return string in the format required + */ + public String printer() { + if (done) { + return "[E][:)] " + midCommand + "(at: " + formattedDate + ")"; + } else { + return "[E][:(] " + midCommand + "(at: " + formattedDate + ")"; + } + } + + /** + * to print for text file. + * + * @return string in the format required + */ + public String printToOutput() { + if (done) { + return "E | 1 | " + midCommand + " | " + formattedDate; + } else { + return "E | 0 | " + midCommand + " | " + formattedDate; + } + } + + /** + * read user input as convert it into a Task. + * + * @param s is the user input string + * + * @return a Task.Task (Task.Event) object + */ + public static Task outputAsEvent(String s) { + String[]segments = s.split("\\|"); + String taskCommand = segments[2].trim() + " /at: " + segments[3].trim(); + Event newTask = new Event(taskCommand); + + if (segments[1].equals(" 1 ")) { + newTask.taskDone(); + } + + return newTask; + } + + /** + * Creates a new event task. + * + * @param command is the user string input to be processed + * + * @throws DukeException in case user inputs in an incorrect format + */ + public static void createEvent(String command, TaskList tasks, Storage storage, ExpenseList expense) + throws DukeException { + String[]splitWords = command.trim().split("\\s",2); + String midCommand = splitWords[1].trim(); + + try { + if (midCommand.length() != 0) { + tasks.add(new Event(midCommand)); + storage.updateFile(tasks, expense); + } else { + throw new Exception(); + } + } catch (Exception e) { + throw new DukeException(""); + } + } + + /** + * marks if task is done. + */ + public void taskDone() { + done = true; + } +} diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java new file mode 100644 index 0000000000..42f6791066 --- /dev/null +++ b/src/main/java/task/Task.java @@ -0,0 +1,60 @@ +package task; + +/** + * parent class for all tasks. + */ +public class Task { + + protected String command; + Boolean done; + + /** + * constructor. + * + * @param command is the user input string + */ + Task(String command) { + this.command = command; + this.done = false; + } + + /** + * prints a task in the format required for a list. + * + * @return the string to print + */ + public String printer() { + if (done) { + return "[✓] " + command; + } else { + return "[✗]" + command; + } + } + + /** + * dummy method to be overriden by child class. + * + * @return dummy String + */ + public String printToOutput() { + return ""; + } + + + /** + * marks the task as done. + * + */ + public void taskDone() { + done = true; + } + + /** + * to get the command. + * + * @return command + */ + public String getCommand() { + return command; + } +} \ No newline at end of file diff --git a/src/main/java/task/ToDo.java b/src/main/java/task/ToDo.java new file mode 100644 index 0000000000..e849f639c6 --- /dev/null +++ b/src/main/java/task/ToDo.java @@ -0,0 +1,96 @@ +package task; + +import exceptions.DukeException; +import utilities.ExpenseList; +import utilities.Storage; +import utilities.TaskList; + +/** + * contains actions for the todo task. + */ +public class ToDo extends Task { + /** + * constructor for the child class. + * + * @param command is the user input + */ + public ToDo(String command) { + super(command); + this.done = false; + } + + /** + * to print for "list" command. + * + * @return string in the format required + */ + public String printer() { + if (done) { + return "[T][:)] " + command; + } else { + return "[T][:(] " + command; + } + } + + /** + * to print for text file. + * + * @return string in the format required + */ + public String printToOutput() { + if (done) { + return "T | 1 | " + command; + } else { + return "T | 0 | " + command; + } + } + + /** + * read user input as convert it into a Task. + * + * @param command is the user input string + * + * @return a Task.Task (Task.ToDo) object + */ + public static Task outputAsToDo(String command) { + String[]segments = command.split("\\|"); + ToDo newTask = new ToDo(segments[2].trim()); + + if (segments[1].equals(" 1 ")) { + newTask.taskDone(); + } + + return newTask; + } + + /** + * Creates a new Task.ToDo task. + * + * @param command is the user string input to be processed + * + * @throws DukeException in case user inputs in an incorrect format + */ + public static void createTodo(String command, TaskList tasks, Storage storage, ExpenseList expenses) + throws DukeException { + String[]splitWords = command.trim().split("\\s",2); + String midCommand = splitWords[1].trim(); + + try { + if (midCommand.length() != 0) { + tasks.add(new ToDo(midCommand)); + storage.updateFile(tasks, expenses); + } else { + throw new DukeException(""); + } + } catch (Exception e) { + throw new DukeException(""); + } + } + + /** + * marks task as done. + */ + public void taskDone() { + done = true; + } +} diff --git a/src/main/java/utilities/ExpenseList.java b/src/main/java/utilities/ExpenseList.java new file mode 100644 index 0000000000..4c9eb2be8e --- /dev/null +++ b/src/main/java/utilities/ExpenseList.java @@ -0,0 +1,165 @@ +package utilities; + +import expense.Expense; + +import java.util.ArrayList; + +/** + * to store the expenses. + */ +public class ExpenseList { + + private ArrayList list; + private ArrayList> subLists = new ArrayList<>(); + + /** + * Constructor if there is a existing list. + * + * @param list is the list input + */ + public ExpenseList(ArrayList list) { + this.list = list; + this.setIDs(); + for (Expense x: list) { + editSubList(x); + } + } + + /** + * Constructor if there is no existing list. + */ + public ExpenseList() { + list = new ArrayList(); + subLists = new ArrayList<>(); + } + + /** + * to edit the list of tagged lists when new item is added. + * + * @param item is the new expense added + */ + private void editSubList(Expense item) { + boolean isPresent = false; + for (ArrayList e: subLists) { + if (e.get(0).getTagName().equals(item.getTagName())) { + e.add(item); + isPresent = true; + break; + } + } + if (!isPresent) { + ArrayList newList = new ArrayList<>(); + newList.add(item); + subLists.add(newList); + } + } + + /** + * To print for DukeOutput.txt + * + * @return a string to be printed in output + */ + public String toString() { + StringBuilder s = new StringBuilder(); + for (int i = 1; i <= list.size(); i++) { + if (i == (list.size())) { + s.append(i).append(". ").append(list.get(i - 1).toString()); + } else { + s.append(i).append(". ").append(list.get(i - 1).toString()).append("\n"); + } + } + return s.toString(); + } + + /** + * To print the tagged subLists in CLI. + * + * @return the string to be printed + */ + public String toSubString() { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < subLists.size(); i++) { + s.append("#").append(subLists.get(i).get(0).getTagName()).append("\n"); + for (int j = 0; j < subLists.get(i).size(); j++) { + if (i == (subLists.size() - 1) && (j == (subLists.get(i).size() - 1))) { + s.append(j + 1).append(". ").append(subLists.get(i).get(j).toSubString()); + } else if (j == (subLists.get(i).size() - 1)) { + s.append(j + 1).append(". ").append(subLists.get(i).get(j).toSubString()).append("\n").append("\n"); + } else { + s.append(j + 1).append(". ").append(subLists.get(i).get(j).toSubString()).append("\n"); + } + } + } + return s.toString(); + } + + /** + * add new expense to the lists. + * + * @param item is the new expense + */ + public void add(Expense item) { + list.add(item); + this.setIDs(); + editSubList(item); + } + + /** + * to calculate size of list. + * + * @return int value of size + */ + public int size() { + return list.size(); + } + + /** + * calculates the total expenses spent. + * + * @return the total expenses as a double + */ + public double totalValue() { + double total = 0; + + for (int i = 0; i < list.size(); i++) { + total += list.get(i).getCost(); + } + + return total; + } + + /** + * to retrieve expense object from list. + * + * @param i is the digit of object in the list. + * + * @return the expense object + */ + public Expense get(int i) { + return list.get(i); + } + + /** + * remove Expense object from list. + * + * @param i is the digit of object in the list + */ + public void remove(int i) { + list.remove(i); + subLists.clear(); + this.setIDs(); + for (Expense x: list) { + editSubList(x); + } + } + + /** + * change iD of object when changes occur to the list. + */ + public void setIDs() { + for (int i = 0; i < list.size(); i++) { + list.get(i).setId(i + 1); + } + } + +} diff --git a/src/main/java/utilities/Parser.java b/src/main/java/utilities/Parser.java new file mode 100644 index 0000000000..16c9fbc9ff --- /dev/null +++ b/src/main/java/utilities/Parser.java @@ -0,0 +1,74 @@ +package utilities; + +import command.ByeCommand; +import command.DeleteCommand; +import command.DeleteExpenseCommand; +import command.DoneCommand; +import command.ExpenseCommand; +import command.Command; +import command.ExpenseListCommand; +import command.FindCommand; +import command.HelloCommand; +import command.ListCommand; +import command.TaskCommand; + +/** + * Utilities.Parser processes the user commands and carries out the specific functions on it. + */ +public class Parser { + + /** + * Class Constructor. + */ + public Parser() { + } + + /** + * Interprets the command and calls the executing class. + * + * @param command is the string input + * + * @return the command as an object + */ + public static Command parse(String command) { + String[]words = command.split(" "); + if (command.equals("bye")) { + return new ByeCommand(command); + } else if ((words.length == 2) && (words[0].equals("done")) && (isNumeric(words[1]))) { + return new DoneCommand(command); + } else if ((words.length == 2) && (words[0].equals("delete")) && (isNumeric(words[1]))) { + return new DeleteCommand(command); + } else if (command.equals("list")) { + return new ListCommand(command); + } else if (words[0].equals("find")) { + return new FindCommand(command); + } else if (words[0].equals("spent")) { + return new ExpenseCommand(command); + } else if (words[0].equals("expenses")) { + return new ExpenseListCommand(command); + } else if ((words.length == 2) && (words[0].equals("deleteExpense")) && (isNumeric(words[1]))) { + return new DeleteExpenseCommand(command); + } else if (words[0].equals("hello")) { + return new HelloCommand(command); + } else { + return new TaskCommand(command); + } + + } + + /** + * determines whether parameter is an integer. + * + * @param str takes in the string that will be checked + * + * @return boolean value + */ + public static boolean isNumeric(String str) { + try { + Integer.parseInt(str); + return true; + } catch (NumberFormatException e) { + return false; + } + } +} diff --git a/src/main/java/utilities/Storage.java b/src/main/java/utilities/Storage.java new file mode 100644 index 0000000000..baec30a8de --- /dev/null +++ b/src/main/java/utilities/Storage.java @@ -0,0 +1,135 @@ +package utilities; + + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import expense.Expense; +import expense.RandomExpense; +import task.Deadline; +import task.Event; +import task.Task; +import task.ToDo; + +/** + * Utilities.Storage enables data to be retrieved and stored in the text file. + */ +public class Storage { + + private String filename; + private String lineBreak = "_____EXPENSES_____"; + private File file; + + /** + * constructor. + * + * @param filename is the name of hard drive file to output to + */ + public Storage(String filename) { + this.filename = filename; + + File directory = new File(String.valueOf(Path.of(filename).getParent())); + if (!directory.isDirectory()) { + directory.mkdirs(); + } + + this.file = new File(filename); + if (!file.isFile()) { + try { + file.createNewFile(); + } catch (IOException e) { + System.out.println("Error reading file"); + } + } + } + + /** + * to load the list from output. + * + * @return ArrayList of tasks to create Utilities.TaskList + * + * @throws Exception in case BufferedReader is unable to read the filename + */ + public ArrayList load() throws Exception { + ArrayList list = new ArrayList<>(); + + FileReader filereader = new FileReader(file); + BufferedReader br = new BufferedReader(filereader); + String lineToRead; + lineToRead = br.readLine(); + + while ((lineToRead!=null) && (!lineToRead.equals(lineBreak))) { + if ((!lineToRead.equals("")) && (lineToRead.charAt(0) == 'T')) { + Task newTask = ToDo.outputAsToDo(lineToRead); + list.add(newTask); + } else if ((!lineToRead.equals("")) && (lineToRead.charAt(0) == 'D')) { + Task newTask = Deadline.outputAsDeadline(lineToRead); + list.add(newTask); + } else if ((!lineToRead.equals("")) && (lineToRead.charAt(0) == 'E')) { + Task newTask = Event.outputAsEvent(lineToRead); + list.add(newTask); + } + lineToRead = br.readLine(); + } + return list; + + } + + /** + * to load expenses from output. + * + * @return the arrayList of expenses + */ + public ArrayList loadExpenses() { + ArrayList list = new ArrayList<>(); + + try { + BufferedReader br = Files.newBufferedReader(Paths.get(filename)); + String lineToRead; + boolean isExpense = false; + + while ((lineToRead = br.readLine()) != null) { + if (isExpense) { + String name = lineToRead.substring(3).split("\\|", 3)[0].trim(); + String cost = lineToRead.substring(3).split("\\|", 3)[1].trim(); + String tagName = lineToRead.substring(3).split("\\|", 3)[2].trim(); + String command = name + "/for" + cost + " #" + tagName; + list.add(new RandomExpense(command)); + } else if (lineToRead.equals(lineBreak)) { + isExpense = true; + } + } + } catch (IOException e) { + System.out.println("File not read"); + } catch (Exception e) { + return list; + } + + return list; + } + + /** + * prints list on the output text file. + * + * @param tasks is the list of tasks to be printed + * + * @param expenses is the list of expenses + * @throws FileNotFoundException in case filename is not found + */ + public void updateFile(TaskList tasks, ExpenseList expenses) throws FileNotFoundException { + PrintStream outputTo = new PrintStream(filename); + outputTo.println(tasks.printForOutput()); + outputTo.println(lineBreak); + outputTo.println(expenses.toString()); + outputTo.close(); + } + +} diff --git a/src/main/java/utilities/TaskList.java b/src/main/java/utilities/TaskList.java new file mode 100644 index 0000000000..5a46ea19f0 --- /dev/null +++ b/src/main/java/utilities/TaskList.java @@ -0,0 +1,123 @@ +package utilities; + +import task.Task; +import java.util.ArrayList; + +/** + * An arrayList like feature which contains the tasks and also carries out certain activities on it. + */ +public class TaskList { + + private ArrayList list; + + /** + * constructor if the hard drive already contains tasks. + * + * @param list is the input list from hard drive + */ + public TaskList(ArrayList list) { + this.list = list; + } + + /** + * constructor in case the hard drive file is not read. + */ + public TaskList() { + this.list = new ArrayList<>(); + } + + /** + * outputs the tasks on the list in the format for hard drive output. + * + * @return the string which then will be outputted on the text file + */ + public String printForOutput() { + StringBuilder result = new StringBuilder(); + + for (int i = 1; i <= list.size(); i++) { + if (i == list.size()) { + result.append(list.get(i - 1).printToOutput()); + } else { + result.append(list.get(i - 1).printToOutput()).append("\n"); + } + } + + return result.toString(); + } + + /** + * marks the specific task as done. + * + * @param x is the digit of the task to be marked as done + */ + public void taskDone(int x) { + list.get(x).taskDone(); + } + + /** + * returns the task details in a list-printable format. + * + * @param x is the digit of task to be printed + * + * @return the task details + */ + public String taskPrint(int x) { + return list.get(x).printer(); + } + + /** + * to print latest task addition to the list. + * + * @return task details in string format + */ + public String printLatest() { + return list.get(list.size() - 1).printer(); + } + + /** + * size of the ArrayList. + * + * @return int value of the size + */ + public Integer size() { + return list.size(); + } + + /** + * remove task from list. + * + * @param x is the digit of the task in the list + */ + public void remove(int x) { + list.remove(x); + } + + /** + * adds a new task to the list. + * + * @param newTask is the Task.Task to add + */ + public void add(Task newTask) { + list.add(newTask); + } + + /** + * to retrieve the Task.Task. + * + * @param x is the digit of the task in the list + * + * @return the Task.Task + */ + public Task get(int x) { + return list.get(x); + } + + /** + * checks whether list is empty. + * + * @return boolean value + */ + public boolean isEmpty() { + return list.isEmpty(); + } +} diff --git a/src/main/java/utilities/Ui.java b/src/main/java/utilities/Ui.java new file mode 100644 index 0000000000..e1efb57193 --- /dev/null +++ b/src/main/java/utilities/Ui.java @@ -0,0 +1,187 @@ +package utilities; + +import expense.Expense; + +import java.util.Scanner; + +/** + * Shows the interface. + */ +public class Ui { + + private final String lineBorder = "____________________________________________________________"; + + /** + * prints welcome message. + */ + public void showWelcome() { + final String Duke_Logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + + System.out.println("Hello from\n" + Duke_Logo); + System.out.println(lineBorder + "\n" + "Hello! I'm Duke" + "\n" + + "What can I do for you?" + "\n" + lineBorder); + } + + /** + * returns welcome message as string. + * + * @return welcome message string + */ + public String showWelcomeFX() { + final String Duke_Logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + + return "Hello from\n" + Duke_Logo + lineBorder + "\n" + "Hello! I'm Duke" + "\n" + + "What can I do for you?" + "\n" + lineBorder; + } + + /** + * to print conclusion message. + * + * @return message as String + */ + public String showConclusionFX() { + return "Bye. Hope to see you again soon!"; + } + + /** + * to read command input. + * + * @return user input string + */ + public String readCommand() { + Scanner sc = new Scanner(System.in); + return sc.nextLine(); + } + + /** + * to print the long border line. + */ + public void showLine() { + System.out.println(lineBorder); + } + + /** + * to print error message. + * + * @param message is default parameter + */ + public void showError(String message) { + System.out.println("OOPS!!! I'm sorry, but I don't know what that means :-()"); + } + + /** + * to return error as String. + * + * @param message is default parameter + * + * @return the error message + */ + public String showErrorFX(String message) { + return "OOPS!!! I'm sorry, but I don't know what that means :-()"; + } + + /** + * prints error message if file is not available. + */ + public void showLoadingError() { + System.out.println("File not available"); + } + + /** + * return done message as String. + * @param n is the digit of task to be done + * + * @param tasks is the TaskList + * + * @return the output String + */ + public String doneMessageFX(int n, TaskList tasks) { + return "Nice! I've marked this task as done: \n" + tasks.taskPrint(n); + } + + /** + * to return the delete message as String. + * + * @param n is the digit of task to be deleted + * + * @param tasks is the TaskList + * + * @return the delete message + */ + public String deleteMessageFX(int n, TaskList tasks) { + return "Noted. I've removed this task:" + "\n" + tasks.taskPrint(n) + + "\n" + "Now you have " + (tasks.size() - 1) + " tasks in the list."; + } + + /** + * return list as String. + * + * @param tasks is the TaskList + * + * @return the list + */ + public String listCommandFX(TaskList tasks) { + StringBuilder s = new StringBuilder(); + for (int i = 1; i <= tasks.size(); i++) { + if (i == tasks.size()) { + s.append(i).append(". ").append(tasks.get(i - 1).printer()); + } else { + s.append(i).append(". ").append(tasks.get(i - 1).printer()).append("\n"); + } + } + return s.toString(); + } + + /** + * return find results as String. + * + * @param tasks is the TaskList + * + * @return find results + */ + public String findCommandFX(TaskList tasks) { + if (tasks.isEmpty()) { + return "Sorry, we couldn't find any results!"; + } else { + return "Here are the matching tasks in your list:" + "\n" + this.listCommandFX(tasks); + } + } + + /** + * to print expense message. + * + * @param item is the expense object + * + * @return string to be printed + */ + public String expenseMessage(Expense item) { + return "Your expense has been added to the list: " + "\n" + item.toString(); + } + + public String expenseListMessage(ExpenseList list) { + return list.toSubString() + "\n" + "\n" + "Total: " + String.format("%.2f", list.totalValue()); + } + + /** + * to return the expense delete message as String. + * + * @param n is the digit of task to be deleted + * + * @param expenses is the ExpenseList + * + * @return the delete message + */ + public String deleteExpenseMessage(int n, ExpenseList expenses) { + return "Noted. I've removed this expense:" + "\n" + expenses.get(n).toString() + + "\n" + "Now you have " + (expenses.size() - 1) + " expenses in the list."; + } + +} diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000..d893658717 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..3c82f45461 Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/images/pic1.jpg b/src/main/resources/images/pic1.jpg new file mode 100644 index 0000000000..d04336d323 Binary files /dev/null and b/src/main/resources/images/pic1.jpg differ diff --git a/src/main/resources/images/pic2.jpg b/src/main/resources/images/pic2.jpg new file mode 100644 index 0000000000..1638298e7d Binary files /dev/null and b/src/main/resources/images/pic2.jpg differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..a1bb1a6ae5 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..175709c531 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/DeadlineTest.java b/src/test/java/DeadlineTest.java new file mode 100644 index 0000000000..af3bef37dc --- /dev/null +++ b/src/test/java/DeadlineTest.java @@ -0,0 +1,13 @@ +import exceptions.DukeException; +import task.Deadline; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DeadlineTest { + @Test + public void printToOutputTest() throws DukeException { + assertEquals("D | 0 | submission | 04/07/2019 2359", + new Deadline("submission /by 4/7/2019 2359").printToOutput()); + } +} diff --git a/src/test/java/ParserTest.java b/src/test/java/ParserTest.java new file mode 100644 index 0000000000..c009ed2c4d --- /dev/null +++ b/src/test/java/ParserTest.java @@ -0,0 +1,11 @@ +import utilities.Parser; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ParserTest { + @Test + public void isNumericTest() { + assertEquals(true, Parser.isNumeric("1")); + } +} diff --git a/src/test/java/StorageTest.java b/src/test/java/StorageTest.java new file mode 100644 index 0000000000..3a64635586 --- /dev/null +++ b/src/test/java/StorageTest.java @@ -0,0 +1,22 @@ +import org.junit.jupiter.api.Test; +import task.Event; +import task.Task; +import task.ToDo; +import utilities.Storage; +import utilities.TaskList; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StorageTest { + @Test + public void loadTest() throws Exception { + ArrayList testTask = new ArrayList<>(); + testTask.add(new ToDo(" read book")); + testTask.add(new Event(" party /at 04/07/2019 2359")); + + assertEquals(new TaskList(testTask).printForOutput(), + new TaskList(new Storage("TestOutput.txt").load()).printForOutput()); + } +}