diff --git a/.classpath b/.classpath
new file mode 100644
index 000000000..e16509f76
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..097f9f98d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# Linux start script should use lf
+/gradlew text eol=lf
+
+# These are Windows script files and should use crlf
+*.bat text eol=crlf
+
diff --git a/.gitignore b/.gitignore
index 2873e189e..a189f150d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,9 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+
+# Ignore Gradle project-specific cache directory
+.gradle
+
+# Ignore Gradle build output directory
+build
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..c5f3f6b9c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.configuration.updateBuildConfiguration": "interactive"
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 000000000..6e145e1b5
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,35 @@
+plugins {
+ id 'java'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'com.google.code.gson:gson:2.10.1'
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs = ['src/main/java']
+ }
+ }
+}
+
+jar {
+ archiveFileName = "ip.jar"
+ from configurations.runtimeClasspath.collect {
+ it.isDirectory() ? it : zipTree(it)
+ }
+
+ from('lib') {
+ include '*.jar'
+ }
+
+ manifest {
+ attributes 'Main-Class': 'Duke'
+ }
+
+}
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 8077118eb..4d2392216 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,354 @@
# User Guide
-## Features
+## Contents
+> [Feature](#Feature)
+[Usage](#Usage)
-### Feature-ABC
+## Feature
-Description of the feature.
+### Local Data Saving
-### Feature-XYZ
+All the data that produced by the program is going to save in the 'AppData\LocalLow\EggyByte\iP\' under the current user folder.
+In this way, previous data can be used and no need for duplicated input.
-Description of the feature.
+### Error Handling
-## Usage
+All errors are handled with a customized type 'TipsException', which enables user to get both the description of the error and the possible solution.It's easy to solve errors by following the tips.
+
+### Cross Platform
+
+User of any platform that supports jar file can use it.
+
+### High Stability
+
+We have tested it for many extreme situation and fix numerous potential problems.Users can enjoy themselves using this jar.
+## Usage
### `Keyword` - Describe action
+### `Arguments` - Optional words that customize
+### Input:
+Mode:
+```
+keyword (arguments)
+```
+Example:
+```
+todo Read Books
+```
+### Outcome:
+Mode:
+```
+ ____________________________________________________________
+ [time][log level]
+ content
+ ____________________________________________________________
+```
+Example:
+```
+ ____________________________________________________________
+ [23:44:19][INFO]
+ Nice! I've marked this task as done:
+ [E][X] return books (from: Mar 12, 2019 13:34, to: Feb 13, 2024 22:12)
+ ____________________________________________________________
+```
+### Format
+#### Basic Rules
+1. Use '/' to split each arguments.
+2. Remember to comply with each format rules for the specific class.
+3. When meet with format problems, please check your input.If you don't know how to write a standard command, just modify the example input.
+#### Classes
+> [String](#String)
+[DateTime](#DateTime)
+[Date](#Date)
+
+##### String
+No any restriction.
+Just remind don't input '/', which will make misunderstanding.
+##### DateTime
+Only following format is supported:
+```
+yyyy-M-d HHmm
+yyyy-M-d H:m
+M-d-yyyy HHmm
+M-d-yyyy H:m
+MMM d, yyyy HH:mm
+```
+1. yyyy stands for year, which must be 4 characters.
+```2020```
+2. M stands for month, which can be 1 or 2 characters.
+```1```
+```11```
+3. MMM stands for month in English, which must be 3 characters.
+```Oct```
+```Apr```
+4. d stands for day, which can be 1 or 2 characters.
+```5```
+```28```
+5. H stands for hour, which can be 1 or 2 characters.
+```3```
+```18```
+6. HH stands for hour, which must be 2 characters.
+```03```
+```18```
+7. m stands for minute, which can be 1 or 2 characters.
+```6```
+```55```
+8. mm stands for minute, which must be 2 characters.
+```06```
+```55```
+
+##### Date
+Only following format is supported:
+```
+yyyy-M-d
+M-d-yyyy
+MMM d, yyyy
+```
+1. yyyy stands for year, which must be 4 characters.
+```2020```
+2. M stands for month, which can be 1 or 2 characters.
+```1```
+```11```
+3. MMM stands for month in English, which must be 3 characters.
+```Oct```
+```Apr```
+4. d stands for day, which can be 1 or 2 characters.
+```5```
+```28```
+
+### Valid Keywords and Arguments:
-Describe the action and its outcome.
+> [Todo](#Todo)
+[Deadline](#Deadline)
+[Event](#Event)
+[List](#List)
+[Mark](#Mark)
+[Unmark](#Unmark)
+[Delete](#Delete)
+[Activated](#Activated)
+[Find](#Find)
+[Bye](#Bye)
-Example of usage:
+#### Todo
+> Add a task into the current list with type of `Todo`.
-`keyword (optional arguments)`
+- Keywords: `todo`
+- Arguments:
+ - Length: 1
+ - Contents: [`description`]
+ - `description`: (String) used for describe the todo.
+- Example Input:
+```
+todo Read Books
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [01:03:03][INFO]
+ Got it. I've added this task:
+ [T][ ] Read Books
+ Now you have 3 tasks in the list.
+ ____________________________________________________________
+```
-Expected outcome:
+#### Deadline
+> Add a task into the current list with type of `Deadline`.
-Description of the outcome.
+- Keywords: `deadline`
+- Arguments:
+ - Length: 2
+ - Contents: [`description`, `by`]
+ - `description`: (String) used for describe the deadline.
+ - `by`: (DateTime) the end of the deadline.
+- Example Input:
+```
+deadline Read Books /by 1928-10-1 13:2
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [01:06:02][INFO]
+ Got it. I've added this task:
+ [D][ ] Read Books (by: Oct 1, 1928 13:02)
+ Now you have 4 tasks in the list.
+ ____________________________________________________________
+```
+#### Event
+> Add a task into the current list with type of `Event`.
+
+- Keywords: `event`
+- Arguments:
+ - Length: 3
+ - Contents: [`description`, `from`, `to`]
+ - `description`: (String) used for describe the event.
+ - `from`: (DateTime) the begin of the event.
+ - `to`: (DateTime) the end of the event.
+- Example Input:
+```
+event Read Bookss /from 1925-10-1 13:2 /to 1928-10-1 13:2
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [01:06:02][INFO]
+ Got it. I've added this task:
+ [D][ ] Read Books (by: Oct 1, 1928 13:02)
+ Now you have 4 tasks in the list.
+ ____________________________________________________________
+```
+
+#### List
+> Show all tasks in the current list.
+
+- Keywords: `list`
+- Arguments:
+ - Length: 0
+ - Contents: []
+- Example Input:
+```
+list
+```
+- Example Outcome:
```
-expected output
+ ____________________________________________________________
+ [13:41:45][INFO]
+ Here are the tasks in your list:
+ 1.[E][ ] return books (from: Mar 12, 2019 13:34, to: Feb 13, 2024 22:12)
+ 2.[T][X] yes!
+ 3.[T][ ] Read Books
+ 4.[D][ ] Read Books (by: Oct 1, 1928 13:02)
+ ____________________________________________________________
```
+
+#### Mark
+> Mark an existing task in the current list as done.
+
+- Keywords: `mark`
+- Arguments:
+ - Length: 1
+ - Contents: [`index`]
+ - `index`: (Integer) used for locate the task in the list.
+- Example Input:
+```
+mark 1
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [13:43:08][INFO]
+ Nice! I've marked this task as done:
+ [E][X] return books (from: Mar 12, 2019 13:34, to: Feb 13, 2024 22:12)
+ ____________________________________________________________
+```
+
+#### Unmark
+> Mark an existing task in the current list as undone.
+
+- Keywords: `unmark`
+- Arguments:
+ - Length: 1
+ - Contents: [`index`]
+ - `index`: (Integer) used for locate the task in the list.
+- Example Input:
+```
+mark 1
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [13:43:33][INFO]
+ OK, I've marked this task as not done yet:
+ [T][ ] yes!
+ ____________________________________________________________
+```
+
+#### Delete
+> Delete an existing task in the current list.
+
+- Keywords: `delete`
+- Arguments:
+ - Length: 1
+ - Contents: [`index`]
+ - `index`: (Integer) used for locate the task in the list.
+- Example Input:
+```
+delete 3
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [13:44:17][INFO]
+ Noted. I've removed this task:
+ [T][ ] Read Books
+ Now you have 3 tasks in the list.
+ ____________________________________________________________
+```
+
+#### Activated
+> Search all tasks in the current list with a filter of date.
+> Those tasks whose type are `Deadline` or `Event` that are still available on the input date will be shown.
+
+- Keywords: `activated`
+- Arguments:
+ - Length: 1
+ - Contents: [`date`]
+ - `date`: (Date) the date that you want to check.
+- Example Input:
+```
+activated 2020-1-1
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [13:55:14][INFO]
+ Here are the tasks that are still activated on Jan 1, 2020:
+ 1.[E][X] return books (from: Mar 12, 2019 13:34, to: Feb 13, 2024 22:12)
+ ____________________________________________________________
+```
+
+#### Find
+> Find all tasks in the current list with a filter of string.
+> Those tasks whose description contains the input string will be shown.
+
+- Keywords: `find`
+- Arguments:
+ - Length: 1
+ - Contents: [`partial description`]
+ - `partial description`: (String) used for targeting tasks.
+- Example Input:
+```
+find book
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [13:57:43][INFO]
+ Here are the tasks that have partial description with 'book':
+ 1.[E][X] return books (from: Mar 12, 2019 13:34, to: Feb 13, 2024 22:12)
+ ____________________________________________________________
+```
+
+
+#### Bye
+> Terminate the program and automatically save the date locally.
+
+- Keywords: `bye`
+- Arguments:
+ - Length: 0
+ - Contents: []
+- Example Input:
+```
+bye
+```
+- Example Outcome:
+```
+ ____________________________________________________________
+ [13:58:52][INFO]
+ Bye. Hope to see you again soon!
+ ____________________________________________________________
+
+ [13:58:52][IMPORTANT]
+ Your data has been saved at the path:
+ C:\Users\fDu.0ppa1\AppData\LocalLow\EggyByte\iP\Tasks.json
+```
\ No newline at end of file
diff --git a/docs/RunTest.cmd b/docs/RunTest.cmd
new file mode 100644
index 000000000..afcdb44b6
--- /dev/null
+++ b/docs/RunTest.cmd
@@ -0,0 +1,2 @@
+cd text-ui-test
+runtest.bat
diff --git a/docs/UploadCommand.cmd b/docs/UploadCommand.cmd
new file mode 100644
index 000000000..be4349558
--- /dev/null
+++ b/docs/UploadCommand.cmd
@@ -0,0 +1,18 @@
+rem customize the tag name and message here
+set TAG_NAME=A-JavaDoc
+set MESSAGE="Add JavaDoc comments"
+
+rem create a branch
+git branch branch-%TAG_NAME%
+git switch branch-%TAG_NAME%
+rem commit all changes
+git add .
+git commit -m %MESSAGE%
+git push origin branch-%TAG_NAME%
+
+rem delete the tag if there exist
+git tag -d %TAG_NAME%
+git push origin --delete %TAG_NAME%
+rem create the tag with certain branch
+git tag %TAG_NAME% branch-%TAG_NAME%
+git push origin %TAG_NAME%
\ No newline at end of file
diff --git a/gradlew b/gradlew
new file mode 100644
index 000000000..0adc8e1a5
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# 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 ;; #(
+ MSYS* | 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
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# 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"'
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 000000000..93e3f59f1
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@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=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+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 execute
+
+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
+
+: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 %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/lib/gson-2.10.1.jar b/lib/gson-2.10.1.jar
new file mode 100644
index 000000000..a88c5bd9b
Binary files /dev/null and b/lib/gson-2.10.1.jar differ
diff --git a/src/main/java/Duke.class b/src/main/java/Duke.class
new file mode 100644
index 000000000..468553484
Binary files /dev/null and b/src/main/java/Duke.class differ
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
index 5d313334c..bda55cc25 100644
--- a/src/main/java/Duke.java
+++ b/src/main/java/Duke.java
@@ -1,10 +1,79 @@
+import eggybyte.ip.data.task.Task;
+import eggybyte.ip.command.Command;
+import eggybyte.ip.command.CommandResult;
+import eggybyte.ip.data.RunningState;
+import eggybyte.ip.util.Parser;
+import eggybyte.ip.util.DataManager;
+import eggybyte.ip.util.Logger;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * The main class of the program.
+ */
public class Duke {
+ private static RunningState runningState = new RunningState(new ArrayList());
+ private static Boolean debugMode = false;
+ private static String savePath = "Tasks.json";
+
public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
+ initialize();
+
+ runCommandLoopUntilByeCommand();
+ }
+
+ /**
+ * Used for initialization.
+ */
+ private static void initialize() {
+ try {
+ Command.setRunningState(runningState);
+ Logger.debugMode = Parser.debugMode = debugMode;
+ DataManager.setRelativePath(savePath);
+ String dataJson = DataManager.readData();
+ ArrayList data = DataManager.convertFromJsonToTaskList(dataJson);
+ if (data != null) {
+ runningState.tasks = data;
+ }
+ Logger.showGreeting();
+ } catch (Exception exception) {
+ Logger.showLog(exception, true);
+ }
+ }
+
+ /**
+ * Read the user command and execute it, until the user issues the bye
+ * command.
+ */
+ private static void runCommandLoopUntilByeCommand() {
+ Scanner scanner = new Scanner(System.in);
+ do {
+ try {
+ String userCommandText = scanner.nextLine();
+ Command command = new Parser().parseCommand(userCommandText);
+ CommandResult commandResult = command.execute();
+ switch (commandResult.state) {
+ case success: {
+ Logger.showLog(commandResult.result, true);
+ break;
+ }
+ case fail: {
+ // Logger.showLog(commandResult.result, Logger.LogLevel.ERROR, true);
+ // break;
+ throw commandResult.exception;
+ }
+ }
+ } catch (Exception exception) {
+ Logger.showLog(exception, true);
+ }
+ } while (runningState.isRunning());
+
+ try {
+ DataManager.saveData(DataManager.convertToJson(runningState.tasks));
+ } catch (Exception exception) {
+ Logger.showLog(exception, true);
+ }
+ scanner.close();
}
}
diff --git a/src/main/java/eggybyte/ip/Main.java b/src/main/java/eggybyte/ip/Main.java
new file mode 100644
index 000000000..ddaf5544c
--- /dev/null
+++ b/src/main/java/eggybyte/ip/Main.java
@@ -0,0 +1,76 @@
+// package eggybyte.ip;
+
+// import eggybyte.ip.data.task.Task;
+// import eggybyte.ip.command.Command;
+// import eggybyte.ip.command.CommandResult;
+// import eggybyte.ip.data.RunningState;
+// import eggybyte.ip.util.Parser;
+// import eggybyte.ip.util.DataManager;
+// import eggybyte.ip.util.Logger;
+
+// import java.util.ArrayList;
+// import java.util.Scanner;
+
+// public class Main {
+// private static RunningState runningState = new RunningState(new
+// ArrayList());
+// private static Boolean debugMode = false;
+// private static String savePath = "Tasks.json";
+
+// public static void main(String[] args) {
+// initialize();
+
+// runCommandLoopUntilByeCommand();
+// }
+
+// private static void initialize() {
+// try {
+// Command.setRunningState(runningState);
+// Logger.debugMode = Parser.debugMode = debugMode;
+// DataManager.setRelativePath(savePath);
+// String dataJson = DataManager.readData();
+// ArrayList data = DataManager.convertFromJsonToTaskList(dataJson);
+// if (data != null) {
+// runningState.tasks = data;
+// }
+// Logger.showGreeting();
+// } catch (Exception exception) {
+// Logger.showLog(exception, true);
+// }
+// }
+
+// /**
+// * Reads the user command and executes it, until the user issues the bye
+// * command.
+// */
+// private static void runCommandLoopUntilByeCommand() {
+// Scanner scanner = new Scanner(System.in);
+// do {
+// try {
+// String userCommandText = scanner.nextLine();
+// Command command = new Parser().parseCommand(userCommandText);
+// CommandResult commandResult = command.execute();
+// switch (commandResult.state) {
+// case success: {
+// Logger.showLog(commandResult.result, true);
+// break;
+// }
+// case fail: {
+// // Logger.showLog(commandResult.result, Logger.LogLevel.ERROR, true);
+// // break;
+// throw commandResult.exception;
+// }
+// }
+// } catch (Exception exception) {
+// Logger.showLog(exception, true);
+// }
+// } while (runningState.isRunning());
+
+// try {
+// DataManager.saveData(DataManager.convertToJson(runningState.tasks));
+// } catch (Exception exception) {
+// Logger.showLog(exception, true);
+// }
+// scanner.close();
+// }
+// }
diff --git a/src/main/java/eggybyte/ip/command/ActivatedCommand.java b/src/main/java/eggybyte/ip/command/ActivatedCommand.java
new file mode 100644
index 000000000..19a5c5aec
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/ActivatedCommand.java
@@ -0,0 +1,85 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.Date;
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.*;
+
+import java.util.ArrayList;
+
+/**
+ * Commands for Searching Activated Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class ActivatedCommand extends Command {
+
+ public static final String COMMAND_WORD = "activated";
+ private static final int validArgumentAmount = 1;
+ private final Date date;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public ActivatedCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ date = new Date(arguments[0]);
+ }
+
+ @Override
+ public String customFunction() {
+ ArrayList filteredTasks = new ArrayList();
+ for (Task task : runningState.tasks) {
+ if (task instanceof Deadline) {
+ if (((Deadline) task).by.compareDate(date) != -1) {
+ filteredTasks.add(task);
+ }
+ }
+ if (task instanceof Event) {
+ if (((Event) task).from.compareDate(date) != 1 && ((Event) task).to.compareDate(date) != -1) {
+ filteredTasks.add(task);
+ }
+ }
+ }
+
+ if (filteredTasks.size() == 0) {
+ return "There are no activated tasks!";
+ }
+
+ String result = taskToString(filteredTasks, 0);
+ for (int i = 1; i < filteredTasks.size(); i++) {
+ result += "\n" + taskToString(filteredTasks, i);
+ }
+ return result;
+ }
+
+ /**
+ * Convert task to string
+ *
+ * @param tasks To make clear which tasks is going to be converted.
+ * @param index To make clear which element of the tasks is going to be
+ * converted, and the index is to be shown in the front.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ private String taskToString(ArrayList tasks, int index) {
+ Task task = tasks.get(index);
+ return " " + (index + 1) + "." + task.toString();
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(
+ "Here are the tasks that are still activated on " + date.toString() + ":\n" + content);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/AddCommand.java b/src/main/java/eggybyte/ip/command/AddCommand.java
new file mode 100644
index 000000000..5b6b01851
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/AddCommand.java
@@ -0,0 +1,42 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Task;
+
+/**
+ * Commands for Adding Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class AddCommand extends Command {
+
+ protected Task task;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public AddCommand(String type, int validArgumentAmount) throws TipsException {
+ super(type, validArgumentAmount);
+ }
+
+ @Override
+ public String customFunction() {
+ runningState.tasks.add(task);
+ return task.toString();
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult("Got it. I've added this task:\n "
+ + content
+ + "\nNow you have " + runningState.tasks.size() + " tasks in the list.");
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/ByeCommand.java b/src/main/java/eggybyte/ip/command/ByeCommand.java
new file mode 100644
index 000000000..07ee92336
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/ByeCommand.java
@@ -0,0 +1,41 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+
+/**
+ * Commands for Terminating the Program.
+ *
+ * @see #COMMAND_WORD
+ */
+public class ByeCommand extends Command {
+
+ public static final String COMMAND_WORD = "bye";
+ protected static final int validArgumentAmount = 0;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public ByeCommand(String[] arguments) throws Exception {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ }
+
+ @Override
+ public String customFunction() {
+ runningState.exit();
+ return "";
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(" Bye. Hope to see you again soon!");
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/Command.java b/src/main/java/eggybyte/ip/command/Command.java
new file mode 100644
index 000000000..cbb272844
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/Command.java
@@ -0,0 +1,100 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.RunningState;
+import eggybyte.ip.data.exception.TipsException;
+
+/**
+ * Basic Command Class.
+ */
+public class Command {
+ protected static RunningState runningState;
+ protected final String type;
+ protected final int validArgumentAmount;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public Command(String type, int validArgumentAmount) throws TipsException {
+ this.type = type;
+ this.validArgumentAmount = validArgumentAmount;
+ }
+
+ /**
+ * Executes the command.
+ *
+ * @return Command result that contains a description and maybe an exception.
+ */
+ public CommandResult execute() {
+ try {
+ return getCommandResult(customFunction());
+ } catch (Exception exception) {
+ return new CommandResult(exception);
+ }
+ }
+
+ /**
+ * Running State setter to make clear which state the command is executing for.
+ */
+ public static void setRunningState(RunningState runningState) {
+ Command.runningState = runningState;
+ }
+
+ /**
+ * To combine the result after execution and give the user a standard
+ * description.It must be implemented by child classes for customized use,
+ * otherwise it may throw a tips exception.
+ *
+ * @return A Command Result with a standard description or an unexpected
+ * exception.
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public CommandResult getCommandResult(String content) throws TipsException {
+ throw new TipsException("This method is to be implemented by child classes.",
+ "This is an internal error, please contact the programmer for further fix.");
+ }
+
+ /**
+ * A function that do specified things in each child command class.It must be
+ * implemented by child classes for customized use,otherwise it may throw a tips
+ * exception.
+ *
+ * @return A String that is raw to indicate the result, which maybe modified by
+ * the method getCommandResult().
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ protected String customFunction() throws TipsException {
+ throw new TipsException("This method is to be implemented by child classes.",
+ "This is an internal error, please contact the programmer for further fix.");
+ }
+
+ /**
+ * A function that used for checking the arguments' amount, to make sure no
+ * exception about using an exceeded array element may happen while constructing
+ * a new command.
+ *
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ protected void checkArguments(String[] arguments) throws TipsException {
+ String helpMessage = "The valid argument amount of '" + type + "' command is " + validArgumentAmount
+ + ", and you can check what each argument means in the user guide.";
+ if (arguments.length < validArgumentAmount) {
+ throw new TipsException("Not enough arguments input!", helpMessage);
+ }
+ if (arguments.length > validArgumentAmount) {
+ throw new TipsException("Too many arguments input!", helpMessage);
+ }
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/CommandResult.java b/src/main/java/eggybyte/ip/command/CommandResult.java
new file mode 100644
index 000000000..82937b859
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/CommandResult.java
@@ -0,0 +1,49 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+
+/**
+ * The Result after the Execution of the Command.
+ */
+public class CommandResult {
+
+ /**
+ * The feedback message to be shown to the user.
+ * Contains the execution result and exception if something unexpected happens.
+ * State indicates whether the command is successfully executed.
+ */
+ public final String result;
+ public final Exception exception;
+
+ public enum State {
+ success, fail
+ }
+
+ public final State state;
+
+ /**
+ * Creating a new Command Result in a simplified way.
+ * It's used when the command is successfully executed.
+ */
+ public CommandResult(String result) {
+ this(State.success, result, null);
+ }
+
+ /**
+ * Creating a new Command Result in a simplified way.
+ * It's used when something unexpected happens while the command is executed.
+ */
+ public CommandResult(Exception exception) {
+ this(State.success, exception.toString(), exception);
+ }
+
+ /**
+ * Creating a new Command Result with full arguments.
+ * It's used for a complete constuction.
+ */
+ public CommandResult(State state, String result, Exception exception) {
+ this.result = result;
+ this.state = state;
+ this.exception = exception;
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/DeadlineCommand.java b/src/main/java/eggybyte/ip/command/DeadlineCommand.java
new file mode 100644
index 000000000..3dfa7b350
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/DeadlineCommand.java
@@ -0,0 +1,31 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Deadline;
+
+/**
+ * Commands for Adding Deadling Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class DeadlineCommand extends AddCommand {
+ public static final String COMMAND_WORD = "deadline";
+ protected static final int validArgumentAmount = 2;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public DeadlineCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ task = new Deadline(arguments[0], arguments[1]);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/DeleteCommand.java b/src/main/java/eggybyte/ip/command/DeleteCommand.java
new file mode 100644
index 000000000..be681a4d2
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/DeleteCommand.java
@@ -0,0 +1,50 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+
+/**
+ * Commands for Deleting Existing Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class DeleteCommand extends Command {
+ public static final String COMMAND_WORD = "delete";
+ protected static final int validArgumentAmount = 1;
+ private int index;
+
+ /**
+ * Creates a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public DeleteCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ index = Integer.parseInt(arguments[0]);
+ }
+
+ @Override
+ public String customFunction() throws TipsException {
+ if (index - 1 >= runningState.tasks.size()) {
+ throw new TipsException("Exceeded index!", "The index " + index + " has exceeded the range of the list!"
+ + "\nTry enter a valid index that is not bigger than the length of the list.");
+ }
+ index -= 1;
+ String result = runningState.tasks.get(index).toString();
+ runningState.tasks.remove(index);
+ return result;
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult("Noted. I've removed this task:\n "
+ + content
+ + "\nNow you have " + runningState.tasks.size() + " tasks in the list.");
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/EventCommand.java b/src/main/java/eggybyte/ip/command/EventCommand.java
new file mode 100644
index 000000000..8e645772a
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/EventCommand.java
@@ -0,0 +1,31 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Event;
+
+/**
+ * Commands for Adding Event Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class EventCommand extends AddCommand {
+ public static final String COMMAND_WORD = "event";
+ protected static final int validArgumentAmount = 3;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public EventCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ task = new Event(arguments[0], arguments[1], arguments[2]);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/FindCommand.java b/src/main/java/eggybyte/ip/command/FindCommand.java
new file mode 100644
index 000000000..11a775d6d
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/FindCommand.java
@@ -0,0 +1,77 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.*;
+
+import java.util.ArrayList;
+
+/**
+ * Commands for Finding Specific Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class FindCommand extends Command {
+
+ public static final String COMMAND_WORD = "find";
+ private static final int validArgumentAmount = 1;
+ private final String partialDescription;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public FindCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ partialDescription = arguments[0];
+ }
+
+ @Override
+ public String customFunction() {
+ ArrayList filteredTasks = new ArrayList();
+ for (Task task : runningState.tasks) {
+ if (task.description.contains(partialDescription)) {
+ filteredTasks.add(task);
+ }
+ }
+
+ if (filteredTasks.size() == 0) {
+ return " There are no tasks with such partial description!";
+ }
+
+ String result = taskToString(filteredTasks, 0);
+ for (int i = 1; i < filteredTasks.size(); i++) {
+ result += "\n" + taskToString(filteredTasks, i);
+ }
+ return result;
+ }
+
+ /**
+ * Convert task to string
+ *
+ * @param tasks To make clear which tasks is going to be converted.
+ * @param index To make clear which element of the tasks is going to be
+ * converted, and the index is to be shown in the front.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ private String taskToString(ArrayList tasks, int index) {
+ Task task = tasks.get(index);
+ return " " + (index + 1) + "." + task.toString();
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(
+ "Here are the tasks that have partial description with '" + partialDescription + "':\n" + content);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/ListCommand.java b/src/main/java/eggybyte/ip/command/ListCommand.java
new file mode 100644
index 000000000..8a4201178
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/ListCommand.java
@@ -0,0 +1,67 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Task;
+
+/**
+ * Commands for Listing All Existing Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+ protected static final int validArgumentAmount = 0;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public ListCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ }
+
+ @Override
+ public String customFunction() {
+ if (runningState.tasks.size() == 0) {
+ return "The list is empty!";
+ }
+
+ String result = taskToString(0);
+ for (int i = 1; i < runningState.tasks.size(); i++) {
+ result += "\n" + taskToString(i);
+ }
+ return result;
+ }
+
+ /**
+ * Convert task to string
+ *
+ * @param tasks To make clear which tasks is going to be converted.
+ * @param index To make clear which element of the tasks is going to be
+ * converted, and the index is to be shown in the front.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ private String taskToString(int index) {
+ Task task = runningState.tasks.get(index);
+ return " " + (index + 1) + "." + task.toString();
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(
+ "Here are the tasks in your list:\n"
+ + content);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/MarkCommand.java b/src/main/java/eggybyte/ip/command/MarkCommand.java
new file mode 100644
index 000000000..efdb53bd5
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/MarkCommand.java
@@ -0,0 +1,50 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Task;
+import eggybyte.ip.data.task.Todo;
+
+/**
+ * Command for Marking an Existing Task as Done.
+ *
+ * @see #COMMAND_WORD
+ */
+public class MarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "mark";
+ protected static final int validArgumentAmount = 1;
+ private final int index;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public MarkCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ index = Integer.parseInt(arguments[0]) - 1;
+ }
+
+ @Override
+ public String customFunction() {
+ Task task = runningState.tasks.get(index);
+ if (task instanceof Todo) {
+ ((Todo) task).isDone = true;
+ }
+ return task.toString();
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(
+ "Nice! I've marked this task as done:\n "
+ + content);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/RepeatCommand.java b/src/main/java/eggybyte/ip/command/RepeatCommand.java
new file mode 100644
index 000000000..4f962141e
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/RepeatCommand.java
@@ -0,0 +1,41 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+
+/**
+ * [OBSOLETE]
+ * Command for Reapeating Undefined Commands.
+ */
+public class RepeatCommand extends Command {
+
+ public static final String COMMAND_WORD = "repeat";
+ protected static final int validArgumentAmount = 0;
+ private String input;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public RepeatCommand(String input, String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ this.input = input;
+ checkArguments(arguments);
+ }
+
+ @Override
+ public String customFunction() {
+ return "";
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(" " + input);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/TaskCommand.java b/src/main/java/eggybyte/ip/command/TaskCommand.java
new file mode 100644
index 000000000..3caab7544
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/TaskCommand.java
@@ -0,0 +1,37 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Task;
+
+/**
+ * Commands for Adding General Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class TaskCommand extends AddCommand {
+ public static final String COMMAND_WORD = "task";
+ protected static final int validArgumentAmount = 0;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public TaskCommand(String description, String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ // checkArguments(arguments);
+ task = new Task(description);
+ }
+
+ @Override
+ public String customFunction() {
+ runningState.tasks.add(task);
+ return task.toString();
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/TodoCommand.java b/src/main/java/eggybyte/ip/command/TodoCommand.java
new file mode 100644
index 000000000..7a795f4f7
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/TodoCommand.java
@@ -0,0 +1,31 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Todo;
+
+/**
+ * Commands for Adding Todo Tasks.
+ *
+ * @see #COMMAND_WORD
+ */
+public class TodoCommand extends AddCommand {
+ public static final String COMMAND_WORD = "todo";
+ protected static final int validArgumentAmount = 1;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specified arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public TodoCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ task = new Todo(arguments[0]);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/command/UnmarkCommand.java b/src/main/java/eggybyte/ip/command/UnmarkCommand.java
new file mode 100644
index 000000000..f4381a203
--- /dev/null
+++ b/src/main/java/eggybyte/ip/command/UnmarkCommand.java
@@ -0,0 +1,50 @@
+package eggybyte.ip.command;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Task;
+import eggybyte.ip.data.task.Todo;
+
+/**
+ * Command for Marking an Existing Task as Done.
+ *
+ * @see #COMMAND_WORD
+ */
+public class UnmarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "unmark";
+ protected static final int validArgumentAmount = 1;
+ private final int index;
+
+ /**
+ * Create a new Command.
+ *
+ * @param arguments The specifieds arguments will be used for creating command,
+ * it will automatically check whethere the arguments are
+ * valid.
+ * @see #validArgumentAmount
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public UnmarkCommand(String[] arguments) throws TipsException {
+ super(COMMAND_WORD, validArgumentAmount);
+ checkArguments(arguments);
+ index = Integer.parseInt(arguments[0]) - 1;
+ }
+
+ @Override
+ public String customFunction() {
+ Task task = runningState.tasks.get(index);
+ if (task instanceof Todo) {
+ ((Todo) task).isDone = false;
+ }
+ return task.toString();
+ }
+
+ @Override
+ public CommandResult getCommandResult(String content) {
+ return new CommandResult(
+ "OK, I've marked this task as not done yet:\n "
+ + content);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/data/Date.java b/src/main/java/eggybyte/ip/data/Date.java
new file mode 100644
index 000000000..932f39f98
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/Date.java
@@ -0,0 +1,67 @@
+package eggybyte.ip.data;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.util.Logger;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Customized class for showing date and parsing supported string to date.
+ */
+public class Date {
+ transient LocalDate date;
+
+ @SerializedName("rawData")
+ public String standardString;
+
+ private static DateTimeFormatter[] formatters = new DateTimeFormatter[] {
+ DateTimeFormatter.ofPattern("yyyy-M-d"),
+ DateTimeFormatter.ofPattern("M-d-yyyy"),
+ DateTimeFormatter.ofPattern("MMM d, yyyy", Locale.ENGLISH), };
+ private static DateTimeFormatter toStringFormatter = formatters[formatters.length - 1];
+
+ /**
+ * Modifying an existing date with a rawData String.
+ *
+ * @param rawData A String that needs to comply with a supported format and
+ * indicates a correct date that will be recorded by this Date
+ * instance.
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public void setRawData(String rawData) throws TipsException {
+ for (DateTimeFormatter formatter : formatters) {
+ try {
+ date = LocalDate.parse(rawData, formatter);
+ standardString = this.toString();
+ return;
+ } catch (Exception exception) {
+ }
+ }
+ throw new TipsException("Unable to parse date time!",
+ "Please check the date time string you have input, and make sure you are using the supported format!");
+ }
+
+ /**
+ * Create a new date.
+ *
+ * @param rawData A String that needs to comply with a supported format and
+ * indicates a correct date that will be recorded by this Date
+ * instance.
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public Date(String rawData) throws TipsException {
+ setRawData(rawData);
+ }
+
+ @Override
+ public String toString() {
+ return date.format(toStringFormatter);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/data/DateTime.java b/src/main/java/eggybyte/ip/data/DateTime.java
new file mode 100644
index 000000000..e7190a982
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/DateTime.java
@@ -0,0 +1,109 @@
+package eggybyte.ip.data;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.util.Logger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Customized class for showing date time and parsing supported string to date
+ * time.
+ */
+public class DateTime {
+ transient LocalDateTime dateTime;
+
+ @SerializedName("rawData")
+ public String standardString;
+
+ private static DateTimeFormatter[] formatters = new DateTimeFormatter[] {
+ DateTimeFormatter.ofPattern("yyyy-M-d HHmm"),
+ DateTimeFormatter.ofPattern("yyyy-M-d H:m"),
+ DateTimeFormatter.ofPattern("M-d-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("M-d-yyyy H:m"),
+ DateTimeFormatter.ofPattern("MMM d, yyyy HH:mm", Locale.ENGLISH), };
+ private static DateTimeFormatter toStringFormatter = formatters[formatters.length - 1];
+
+ /**
+ * Modifying an existing date time with a rawData String.
+ *
+ * @param rawData A String that needs to comply with a supported format and
+ * indicates a correct time that will be recorded by this Date
+ * instance.
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public void setRawData(String rawData) throws TipsException {
+ for (DateTimeFormatter formatter : formatters) {
+ try {
+ dateTime = LocalDateTime.parse(rawData, formatter);
+ standardString = this.toString();
+ return;
+ } catch (Exception exception) {
+ }
+ }
+ throw new TipsException("Unable to parse date time!",
+ "Please check the date time string you have input, and make sure you are using the supported format!");
+ }
+
+ /**
+ * Create a new date time.
+ *
+ * @param rawData A String that needs to comply with a supported format and
+ * indicates a correct time that will be recorded by this Date
+ * instance time.
+ * @throws TipsException Any excption will be throw in this type, which contains
+ * information about this exception and the possible
+ * solution.
+ */
+ public DateTime(String rawData) throws TipsException {
+ setRawData(rawData);
+ }
+
+ @Override
+ public String toString() {
+ return dateTime.format(toStringFormatter);
+ }
+
+ /**
+ * Compare the dates of this DateTime and another DateTime.
+ *
+ * @param dateTime The other DateTime that is used to compare.
+ * @return If the date of current instance is earlier,it returns -1.
+ * If the date of current instance is later,it returns 1.
+ * Otherwise(when they're at exactly the same date) it returns 0.
+ */
+ public int compareDate(DateTime dateTime) {
+ LocalDate date1 = this.dateTime.toLocalDate(), date2 = dateTime.dateTime.toLocalDate();
+ if (date1.isBefore(date2)) {
+ return 1;
+ }
+ if (date1.isAfter(date2)) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * Compare the dates of this DateTime and another Date.
+ *
+ * @param date The other Date that is used to compare.
+ * @return If the date of current instance is earlier,it returns -1.
+ * If the date of current instance is later,it returns 1.
+ * Otherwise(when they're at exactly the same date) it returns 0.
+ */
+ public int compareDate(Date date) {
+ LocalDate date1 = this.dateTime.toLocalDate(), date2 = date.date;
+ if (date1.isBefore(date2)) {
+ return -1;
+ }
+ if (date1.isAfter(date2)) {
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/eggybyte/ip/data/RunningState.java b/src/main/java/eggybyte/ip/data/RunningState.java
new file mode 100644
index 000000000..0f202b1cd
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/RunningState.java
@@ -0,0 +1,35 @@
+package eggybyte.ip.data;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.Task;
+
+import java.util.ArrayList;
+
+/**
+ * A class used for recording a state, contaning 'tasks' for a list of task that
+ * is going to be operated, and 'running' to indicate the status of the program.
+ */
+public class RunningState {
+ public ArrayList tasks;
+ private Boolean running;
+
+ public RunningState(ArrayList tasks) {
+ this.tasks = tasks;
+ this.running = true;
+ }
+
+ /**
+ * Terminate the current program.
+ * However, you may switch to another running state for further operation.
+ */
+ public void exit() {
+ running = false;
+ }
+
+ /**
+ * Running getter as it's a private variable.
+ */
+ public Boolean isRunning() {
+ return running;
+ }
+}
diff --git a/src/main/java/eggybyte/ip/data/exception/TipsException.java b/src/main/java/eggybyte/ip/data/exception/TipsException.java
new file mode 100644
index 000000000..9b2aabbec
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/exception/TipsException.java
@@ -0,0 +1,23 @@
+package eggybyte.ip.data.exception;
+
+/**
+ * Any excption will be throw in this type, which contains information about
+ * this exception and the possible solution.
+ */
+public class TipsException extends Exception {
+ public String error, tips;
+
+ /**
+ * Creating a new Tips Exception.
+ */
+ public TipsException(String error, String tips) {
+ this.error = error;
+ this.tips = tips;
+ }
+
+ @Override
+ public String toString() {
+ return "Error: " + error
+ + "\nTips: " + tips;
+ }
+}
diff --git a/src/main/java/eggybyte/ip/data/task/Deadline.java b/src/main/java/eggybyte/ip/data/task/Deadline.java
new file mode 100644
index 000000000..4264e4cb0
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/task/Deadline.java
@@ -0,0 +1,27 @@
+package eggybyte.ip.data.task;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.DateTime;
+
+/**
+ * A child class of Todo that has a new variable 'by' to show the end of the
+ * task.
+ */
+public class Deadline extends Todo {
+ protected final static String type = "D";
+ public final DateTime by;
+
+ /**
+ * Creating a new Event.
+ */
+ public Deadline(String description, String by) throws TipsException {
+ super(description, type);
+ this.by = new DateTime(by);
+ isDone = false;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " (by: " + by + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eggybyte/ip/data/task/Event.java b/src/main/java/eggybyte/ip/data/task/Event.java
new file mode 100644
index 000000000..e62bdf64e
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/task/Event.java
@@ -0,0 +1,27 @@
+package eggybyte.ip.data.task;
+
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.DateTime;
+
+/**
+ * A child class of Todo that has 2 new variables 'from' and 'to' to show the
+ * duration that the task is going to last.
+ */
+public class Event extends Todo {
+ protected final static String type = "E";
+ public DateTime from, to;
+
+ /**
+ * Creating a new Event.
+ */
+ public Event(String description, String from, String to) throws TipsException {
+ super(description, type);
+ this.from = new DateTime(from);
+ this.to = new DateTime(to);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " (from: " + from + ", to: " + to + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eggybyte/ip/data/task/Task.java b/src/main/java/eggybyte/ip/data/task/Task.java
new file mode 100644
index 000000000..ca8da6061
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/task/Task.java
@@ -0,0 +1,20 @@
+package eggybyte.ip.data.task;
+
+/**
+ * The base class for all tasks.
+ */
+public class Task {
+ public final String description;
+
+ /**
+ * Creating a new Task.
+ */
+ public Task(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eggybyte/ip/data/task/Todo.java b/src/main/java/eggybyte/ip/data/task/Todo.java
new file mode 100644
index 000000000..91ae2b1d6
--- /dev/null
+++ b/src/main/java/eggybyte/ip/data/task/Todo.java
@@ -0,0 +1,38 @@
+package eggybyte.ip.data.task;
+
+/**
+ * A child class of Task that has a new variable 'isDone' to show whether the
+ * task is done.
+ */
+public class Todo extends Task {
+ protected final String type;
+ public boolean isDone;
+
+ /**
+ * Creating a new Task.
+ */
+ public Todo(String description) {
+ this(description, "T");
+ }
+
+ /**
+ * Creating a new Task.
+ * It's used for child classes to avoid implemented duplicated function.
+ */
+ public Todo(String description, String type) {
+ super(description);
+ this.type = type;
+ isDone = false;
+ }
+
+ @Override
+ public String toString() {
+ String status = null;
+ if (isDone) {
+ status = "X";
+ } else {
+ status = " ";
+ }
+ return "[" + type + "][" + status + "] " + description;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eggybyte/ip/util/DataManager.java b/src/main/java/eggybyte/ip/util/DataManager.java
new file mode 100644
index 000000000..29c31fdfb
--- /dev/null
+++ b/src/main/java/eggybyte/ip/util/DataManager.java
@@ -0,0 +1,173 @@
+package eggybyte.ip.util;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.reflect.TypeToken;
+
+import eggybyte.ip.data.DateTime;
+import eggybyte.ip.data.exception.TipsException;
+import eggybyte.ip.data.task.*;
+import eggybyte.ip.util.Logger.LogLevel;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+
+/**
+ * A util used for managing the data of the program, using the io stream to
+ * save and read the data at a local address.
+ */
+public class DataManager {
+ private static String absolutePath;
+ private static String home = System.getProperty("user.home");
+
+ /**
+ * Set the relative path to make clear which exact address the data file is
+ * going to save and read.Using relative path may make the program easier to
+ * modify when you need to change the absolute saving path.
+ *
+ * @param relativePath The address containing the name of the data file under
+ * the data folder, ignoring the address of the data folder.
+ * @throws IOException
+ */
+ public static void setRelativePath(String relativePath) throws IOException {
+ String dataFolderPath = home + "\\AppData\\LocalLow\\EggyByte\\iP\\";
+ // Logger.customPrint(dataFolderPath);
+ Files.createDirectories(Paths.get(dataFolderPath));
+
+ absolutePath = dataFolderPath + relativePath;
+ // Logger.customPrint(absolutePath);
+ }
+
+ /**
+ * Read the data from the path that has been previously set.
+ *
+ * @throws IOException
+ */
+ public static String readData() throws IOException {
+ File file = new File(absolutePath);
+
+ // No existing file
+ if (file.createNewFile()) {
+ return "";
+ }
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+
+ String result = "", line;
+ while ((line = reader.readLine()) != null) {
+ result += line + "\n";
+ }
+ reader.close();
+ return result.trim();
+ }
+
+ /**
+ * Save the data to a file at the path that has been previously set.
+ *
+ * @param content The serialized data json that is going to be saved.
+ * @throws IOException
+ */
+ public static void saveData(String content) throws Exception {
+ File file = new File(absolutePath);
+ file.createNewFile();
+
+ FileWriter writer = new FileWriter(file);
+ writer.write(content);
+ writer.close();
+ Logger.showLog("Your data has been saved at the path:\n " + absolutePath, LogLevel.IMPORTANT, false);
+ }
+
+ /**
+ * Convert a json String to a CustomType that is set by the user.
+ *
+ * @param content A valid json String indicates a CustomType instance.
+ */
+ public static CustomType convertFromJson(String json) {
+ Type type = new TypeToken() {
+ }.getType();
+
+ Gson gson = new Gson();
+
+ return gson.fromJson(json, type);
+ }
+
+ /**
+ * Convert a json String to an ArrayList.
+ *
+ * @param content A valid json String indicates an ArrayList.
+ */
+ public static ArrayList convertFromJsonToTaskList(String json) {
+ Type type = new TypeToken>() {
+ }.getType();
+
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Task.class, new TaskAdapter())
+ .registerTypeAdapter(DateTime.class, new DateTimeAdapter())
+ .create();
+
+ return gson.fromJson(json, type);
+ }
+
+ /**
+ * Serialize any object to json.
+ *
+ * @param object Any variable that you want to serialize.
+ */
+ public static String convertToJson(Object object) {
+ Gson gson = new Gson();
+ return gson.toJson(object);
+ }
+
+ /**
+ * Used for deserializing Task with a custom rule.
+ */
+ private static class TaskAdapter implements JsonDeserializer {
+ @Override
+ public Task deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+
+ String type = jsonObject.get("type").getAsString();
+ switch (type) {
+ case "T":
+ return context.deserialize(jsonObject, Todo.class);
+ case "D":
+ return context.deserialize(jsonObject, Deadline.class);
+ case "E":
+ return context.deserialize(jsonObject, Event.class);
+ default:
+ throw new JsonParseException("Unknown task type: " + type);
+ }
+ }
+ }
+
+ /**
+ * Used for deserializing DateTime with a custom rule.
+ */
+ private static class DateTimeAdapter implements JsonDeserializer {
+ @Override
+ public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
+ JsonObject jsonObject = json.getAsJsonObject();
+
+ String rawData = jsonObject.get("rawData").getAsString();
+ try {
+ return new DateTime(rawData);
+ } catch (TipsException e) {
+ Logger.showLog(e.toString(), LogLevel.DEBUG, false);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/eggybyte/ip/util/Logger.java b/src/main/java/eggybyte/ip/util/Logger.java
new file mode 100644
index 000000000..6fb0a8504
--- /dev/null
+++ b/src/main/java/eggybyte/ip/util/Logger.java
@@ -0,0 +1,89 @@
+package eggybyte.ip.util;
+
+import eggybyte.ip.data.exception.TipsException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * An util used for showing log with a more standard way.
+ */
+public class Logger {
+ public static Boolean debugMode = true;
+
+ /**
+ * Simply show greeting.
+ */
+ public static void showGreeting() {
+ showLog(" Hello! I'm EggyByte!\n What can I do for you?", true);
+ }
+
+ public enum LogLevel {
+ DEBUG, WARNING, INFO, ERROR, FATAL, IMPORTANT
+ }
+
+ /**
+ * Simply showing an object and choose whether to show 2 lines to clamp it.
+ *
+ * @param object The object you want to show.
+ * @param showLine Indicate whether to show lines.
+ */
+ public static void showLog(Object object, Boolean showLine) {
+ LogLevel logLevel = debugMode ? LogLevel.DEBUG : LogLevel.INFO;
+ String content = object.toString();
+ if (object instanceof Exception) {
+ logLevel = LogLevel.ERROR;
+ content = ((Exception) object).getMessage();
+ }
+ if (object instanceof TipsException) {
+ logLevel = LogLevel.ERROR;
+ content = object.toString();
+ }
+ showLog(content, logLevel, showLine);
+ }
+
+ /**
+ * Showing a message and choose whether to show 2 lines to clamp it, and also
+ * need to choose the LogLevel of this information.
+ *
+ * @param content The message you want to show.
+ * @param logLevel The essence level of your message.
+ * @param showLine Indicate whether to show lines.
+ */
+ public static void showLog(String content, LogLevel logLevel, Boolean showLine) {
+ if (showLine) {
+ customPrint("____________________________________________________________");
+ }
+ customPrint(
+ "[" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) +
+ "][" + logLevel + "]");
+ String[] lines = content.split("\n");
+ for (String line : lines) {
+ customPrint(" " + line);
+ }
+ if (showLine) {
+ customPrint("____________________________________________________________\n");
+ }
+ }
+
+ /**
+ * Printing with a tab in front of the content to make it more easy to
+ * distinguish the different lines and meaning.
+ */
+ public static void customPrint(String content) {
+ System.out.println(" " + content);
+ }
+
+ /**
+ * Printing all elements in an array.
+ * It's usually used in debuging.
+ */
+ public static void printArray(Type[] array) {
+ int length = array.length;
+ String result = "Array Length : " + length + "\n[";
+ for (int i = 0; i < length; i++) {
+ result += array[i].toString() + (i < length - 1 ? ", " : "");
+ }
+ result += "]";
+ showLog(result, false);
+ }
+}
diff --git a/src/main/java/eggybyte/ip/util/Parser.java b/src/main/java/eggybyte/ip/util/Parser.java
new file mode 100644
index 000000000..6917ca609
--- /dev/null
+++ b/src/main/java/eggybyte/ip/util/Parser.java
@@ -0,0 +1,79 @@
+package eggybyte.ip.util;
+
+import eggybyte.ip.command.*;
+
+/**
+ * An util used for parsing the input to a valid command and executing it.
+ */
+public class Parser {
+ public static Boolean debugMode = false;
+
+ /**
+ * Parse user's input into command for execution.
+ *
+ * @param userInput The full input of user.
+ * @return The command based on the user input.
+ * @throws Exception
+ */
+ public Command parseCommand(String userInput) throws Exception {
+ String[] words = userInput.trim().split(" ", 2); // split the input into command and arguments
+ if (words.length == 0) {
+ throw new Exception("Empty input is invalid!");
+ }
+
+ final String commandWord = words[0];
+ final String argumentString = userInput.replaceFirst(commandWord, "").trim();
+ final String[] arguments = getArguments(argumentString);
+ if (debugMode) {
+ Logger.printArray(arguments);
+ }
+
+ try {
+ switch (commandWord) {
+ case TodoCommand.COMMAND_WORD:
+ return new TodoCommand(arguments);
+ case DeadlineCommand.COMMAND_WORD:
+ return new DeadlineCommand(arguments);
+ case EventCommand.COMMAND_WORD:
+ return new EventCommand(arguments);
+ case DeleteCommand.COMMAND_WORD:
+ return new DeleteCommand(arguments);
+ case MarkCommand.COMMAND_WORD:
+ return new MarkCommand(arguments);
+ case UnmarkCommand.COMMAND_WORD:
+ return new UnmarkCommand(arguments);
+ case ListCommand.COMMAND_WORD:
+ return new ListCommand(arguments);
+ case ActivatedCommand.COMMAND_WORD:
+ return new ActivatedCommand(arguments);
+ case FindCommand.COMMAND_WORD:
+ return new FindCommand(arguments);
+ case ByeCommand.COMMAND_WORD:
+ return new ByeCommand(arguments);
+ default:
+ throw new Exception("I'm sorry, but I don't know what that means :-(");
+ }
+ } catch (Exception exception) {
+ throw exception;
+ }
+ }
+
+ /**
+ * Parse arguments in the context of the argumentString.
+ *
+ * @param argumentString The remaining string that doesn't contain the command
+ * word.
+ * @return A String[] that contains all the argument that is split by '/'.
+ */
+ private String[] getArguments(String argumentString) {
+ if (argumentString.length() == 0) {
+ return new String[0];
+ }
+ String[] parts = argumentString.split("/");
+ parts[0] = parts[0].trim();
+ for (int i = 1; i < parts.length; i++) {
+ parts[i] = parts[i].replaceFirst(parts[i].split(" ")[0], "").trim();
+ }
+ return parts;
+ }
+}
\ No newline at end of file
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e..82db62068 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,84 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+ ____________________________________________________________
+ Hello! I'm EggyByte!
+ What can I do for you?
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Got it. I've added this task:
+ [T][ ] borrow book
+ Now you have 1 tasks in the list.
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Here are the tasks in your list:
+ 1.[T][ ] borrow book
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Got it. I've added this task:
+ [D][ ] return book (by: Sunday)
+ Now you have 2 tasks in the list.
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Got it. I've added this task:
+ [E][ ] project meeting (from: Mon 2pm to:4pm)
+ Now you have 3 tasks in the list.
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Nice! I've marked this task as done:
+ [T][X] borrow book
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Here are the tasks in your list:
+ 1.[T][X] borrow book
+ 2.[D][ ] return book (by: Sunday)
+ 3.[E][ ] project meeting (from: Mon 2pm to:4pm)
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Nice! I've marked this task as done:
+ [D][X] return book (by: Sunday)
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Nice! I've marked this task as done:
+ [E][X] project meeting (from: Mon 2pm to:4pm)
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Got it. I've added this task:
+ [T][ ] test yes!
+ Now you have 4 tasks in the list.
+ ____________________________________________________________
+
+ ____________________________________________________________
+ OK, I've marked this task as not done yet:
+ [T][ ] borrow book
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Noted. I've removed this task:
+ [E][X] project meeting (from: Mon 2pm to:4pm)
+ Now you have 3 tasks in the list.
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Error: Exceeded index!
+ Tips: The index 999 has exceeded the range of the list!
+ Try enter a valid index that is not bigger than the length of the list.
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Here are the tasks in your list:
+ 1.[T][ ] borrow book
+ 2.[D][X] return book (by: Sunday)
+ 3.[T][ ] test yes!
+ ____________________________________________________________
+
+ ____________________________________________________________
+ Bye. Hope to see you again soon!
+ ____________________________________________________________
diff --git a/text-ui-test/data/Tasks.txt b/text-ui-test/data/Tasks.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb..3a537b1f1 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,14 @@
+todo borrow book
+list
+deadline return book /by Sunday
+event project meeting /from Mon 2pm /to 4pm
+mark 1
+list
+mark 2
+mark 3
+todo test yes!
+unmark 1
+delete 3
+delete 999
+list
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 087374464..8a5f00959 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -1,5 +1,8 @@
@ECHO OFF
+REM set the classpath to include directories and JAR files
+set CLASSPATH=..\src\main\java;..\lib\*
+
REM create bin directory if it doesn't exist
if not exist ..\bin mkdir ..\bin
@@ -7,7 +10,7 @@ REM delete output from previous run
if exist ACTUAL.TXT del ACTUAL.TXT
REM compile the code into the bin folder
-javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java
+javac -cp %CLASSPATH% -Xlint:none -d ..\bin ..\src\main\java\*.java
IF ERRORLEVEL 1 (
echo ********** BUILD FAILURE **********
exit /b 1
@@ -18,4 +21,4 @@ REM run the program, feed commands from input.txt file and redirect the output t
java -classpath ..\bin Duke < input.txt > ACTUAL.TXT
REM compare the output to the expected output
-FC ACTUAL.TXT EXPECTED.TXT
+@REM FC ACTUAL.TXT EXPECTED.TXT