+ * Control is then passed to MainManager.runEventDriver().
+ *
+ * @see MainManager#runEventDriver()
+ */
+public class WellNus {
+ private static final String BYE_MESSAGE = "Thank you for using WellNUS++! See you again soon Dx";
+ private static final String GREETING_MESSAGE = "Very good day to you! Welcome to ";
+ private static final String NEWLINE = System.lineSeparator();
+ private final TextUi textUi;
+ private final MainManager mainManager;
+
+ /**
+ * Initialises an instance of WellNUS++, which needs TextUi
+ * and MainManager
.
+ */
+ public WellNus() {
+ this.textUi = new TextUi();
+ this.mainManager = new MainManager();
+ }
+
+ private static String getWellNusLogo() {
+ return NEWLINE
+ + ",--. ,--. ,--.,--.,--. ,--.,--. ,--. ,---. | | | | " + NEWLINE
+ + "| | | | ,---. | || || ,'.| || | | |' .-',---| |---.,---| |---. " + NEWLINE
+ + "| |.'.| || .-. :| || || |' ' || | | |`. `-.'---| |---''---| |---' " + NEWLINE
+ + "| ,'. |\\ --.| || || | ` |' '-' '.-' | | | | | " + NEWLINE
+ + "'--' '--' `----'`--'`--'`--' `--' `-----' `-----' `--' `--' " + NEWLINE;
+ }
+
+ private void byeUser() {
+ this.getTextUi().printOutputMessage(WellNus.BYE_MESSAGE);
+ }
+
+ /**
+ * Calls MainManager to read and execute the user's commands.
+ *
+ * @see Manager#runEventDriver()
+ */
+ private void executeUserCommands() {
+ this.getMainManager().runEventDriver();
+ }
+
+ private MainManager getMainManager() {
+ return this.mainManager;
+ }
+
+ private TextUi getTextUi() {
+ return this.textUi;
+ }
+
+ private void greet() {
+ this.getTextUi().printOutputMessage(WellNus.GREETING_MESSAGE + WellNus.NEWLINE
+ + WellNus.getWellNusLogo());
+ }
+
+ /**
+ * Executes the WellNus application and provides the user with our features.
+ *
+ * @param args Commandline arguments passed to the WellNus Java ARchive
+ */
+ public static void main(String[] args) {
+ new WellNus().start();
+ }
+
+ /**
+ * Starts up WellNUS++: Greets the user, reads for commands until a exit command is given,
+ * and bids the user goodbye.
+ *
+ * The bulk of the work is done in executeUserCommands(), which delegates control to the
+ * appropriate Manager.
+ *
+ * @see Manager#runEventDriver()
+ */
+ public void start() {
+ this.greet();
+ this.executeUserCommands();
+ this.byeUser();
+ }
+
+}
+
diff --git a/src/main/java/wellnus/atomichabit/command/AddCommand.java b/src/main/java/wellnus/atomichabit/command/AddCommand.java
new file mode 100644
index 0000000000..17c1ff800b
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/command/AddCommand.java
@@ -0,0 +1,183 @@
+package wellnus.atomichabit.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import wellnus.atomichabit.feature.AtomicHabit;
+import wellnus.atomichabit.feature.AtomicHabitList;
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.atomichabit.feature.AtomicHabitUi;
+import wellnus.command.Command;
+import wellnus.exception.AtomicHabitException;
+import wellnus.exception.BadCommandException;
+
+
+/**
+ * The AddCommand class is a command class that adds a new atomic habit to an AtomicHabitList.
+ * After that, print a message telling the user what the new habit added is
+ *
+ * @throws AtomicHabitException If the habit already exists in the list
+ */
+ @Override
+ public void execute() throws AtomicHabitException {
+ try {
+ validateCommand(super.getArguments());
+ } catch (BadCommandException badCommandException) {
+ this.getTextUi().printErrorFor(badCommandException, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ assert super.getArguments().containsKey(COMMAND_KEYWORD) : COMMAND_KEYWORD_ASSERTION;
+ String name = super.getArguments().get(AddCommand.COMMAND_NAME_ARGUMENT);
+ if (hasDuplicate(name, atomicHabits.getAllHabits())) {
+ throw new AtomicHabitException(DUPLICATE_HABIT_MESSAGE);
+ }
+ AtomicHabit habit = new AtomicHabit(name);
+ this.getAtomicHabits().addAtomicHabit(habit);
+ String messageToUser = FEEDBACK_STRING_ONE + System.lineSeparator();
+ messageToUser += String.format("'%s' %s", habit, FEEDBACK_STRING_TWO);
+ getTextUi().printOutputMessage(messageToUser);
+ }
+
+ /**
+ * Validate the arguments and payloads from a commandMap generated by CommandParser.
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * This command is interactive, so user will continue providing arguments via
+ * further prompts provided.
+ */
+ @Override
+ public void execute() throws AtomicHabitException {
+ try {
+ validateCommand(super.getArguments());
+ } catch (BadCommandException badCommandException) {
+ getTextUi().printErrorFor(badCommandException, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ try {
+ if (getAtomicHabits().getAllHabits().isEmpty()) {
+ getTextUi().printOutputMessage(FEEDBACK_EMPTY_LIST_UPDATE);
+ return;
+ }
+ int index = this.getIndexFrom(super.getArguments()) - INDEX_OFFSET;
+ AtomicHabit habitToDelete = getAtomicHabits().getHabitByIndex(index);
+ atomicHabits.deleteAtomicHabit(habitToDelete);
+ String stringOfDeletedHabit = habitToDelete + " " + "[" + habitToDelete.getCount() + "]" + " "
+ + FEEDBACK_STRING_TWO
+ + LINE_SEPARATOR;
+ getTextUi().printOutputMessage(FEEDBACK_STRING + LINE_SEPARATOR
+ + stringOfDeletedHabit);
+ } catch (NumberFormatException numberFormatException) {
+ LOGGER.log(Level.INFO, LOG_STR_INPUT_NOT_INTEGER);
+ throw new AtomicHabitException(FEEDBACK_INDEX_NOT_INTEGER_ERROR);
+ } catch (IndexOutOfBoundsException e) {
+ LOGGER.log(Level.INFO, LOG_INDEX_OUT_OF_BOUNDS);
+ throw new AtomicHabitException(FEEDBACK_INDEX_OUT_OF_BOUNDS_ERROR);
+ } catch (BadCommandException badCommandException) {
+ getTextUi().printErrorFor(badCommandException, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+
+ /**
+ * Validate the arguments and payloads from a commandMap generated by CommandParser.
+ * If no exceptions are thrown, arguments are valid.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws BadCommandException If the arguments have any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * Prints a brief description of all of Atomic Habit WellNus' supported commands if
+ * the basic 'help' command was issued.
+ * Prints a detailed description of a specific feature if the specialised
+ * 'help' command was issued.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException exception) {
+ getTextUi().printErrorFor(exception, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ this.printHelpMessage();
+ }
+
+ /**
+ * Checks whether the given arguments are valid for our help command.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser using user's command
+ * @throws BadCommandException If the command is invalid
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * If no exceptions are thrown, arguments are valid.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws BadCommandException If the commandMap has any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * This command is interactive, so user will continue providing arguments via
+ * further prompts provided.
+ */
+ @Override
+ public void execute() throws AtomicHabitException {
+ try {
+ validateCommand(super.getArguments());
+ } catch (BadCommandException badCommandException) {
+ getTextUi().printErrorFor(badCommandException, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ try {
+ if (getAtomicHabits().getAllHabits().isEmpty()) {
+ getTextUi().printOutputMessage(FEEDBACK_EMPTY_LIST_UPDATE);
+ return;
+ }
+ int changeCount = DEFAULT_INCREMENT;
+ boolean hasLevelUp = false;
+ if (super.getArguments().containsKey(UpdateCommand.COMMAND_INCREMENT_ARGUMENT)) {
+ changeCount = this.getIncrementCountFrom(super.getArguments());
+ }
+ int index = this.getIndexFrom(super.getArguments()) - INDEX_OFFSET;
+ AtomicHabit habit = getAtomicHabits().getHabitByIndex(index);
+ if (changeCount == 0) {
+ getTextUi().printOutputMessage(FEEDBACK_CHANGE_COUNT_ZERO);
+ return;
+ }
+ if (changeCount > ZERO) {
+ habit.increaseCount(changeCount);
+ // Add XP for completing atomic habits as an incentive
+ hasLevelUp = gamificationData.addXp(
+ changeCount * NUM_OF_XP_PER_INCREMENT);
+ } else {
+ if (getPositive(changeCount) > habit.getCount()) {
+ throw new AtomicHabitException(FEEDBACK_DECREMENT_ERROR);
+ }
+ habit.decreaseCount(getPositive(changeCount));
+ }
+ String stringOfUpdatedHabit = (index + 1) + DOT + habit + " " + "[" + habit.getCount() + "]"
+ + LINE_SEPARATOR;
+ printFeedback(stringOfUpdatedHabit, changeCount);
+ if (hasLevelUp) {
+ // Congratulate the user about levelling up
+ GamificationUi.printCelebrateLevelUp();
+ }
+ } catch (NumberFormatException numberFormatException) {
+ LOGGER.log(Level.INFO, LOG_STR_INPUT_NOT_INTEGER);
+ throw new AtomicHabitException(FEEDBACK_INDEX_NOT_INTEGER_ERROR);
+ } catch (IndexOutOfBoundsException indexOutOfBoundsException) {
+ LOGGER.log(Level.INFO, LOG_INDEX_OUT_OF_BOUNDS);
+ throw new AtomicHabitException(FEEDBACK_INDEX_OUT_OF_BOUNDS_ERROR);
+ } catch (BadCommandException badCommandException) {
+ getTextUi().printErrorFor(badCommandException, COMMAND_INVALID_COMMAND_NOTE);
+ } catch (StorageException storageException) {
+ getTextUi().printErrorFor(storageException, STORE_GAMIF_DATA_FAILED_NOTE_MESSAGE);
+ }
+ }
+
+ /**
+ * Validate the arguments and payloads from a commandMap generated by CommandParser.
+ * If no exceptions are thrown, arguments are valid.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws BadCommandException If the arguments have any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * Each Command is initialised with the arguments issued
+ * by the user. Execute the specified Command by calling
+ * execute().
+ * Child classes must provide the static isValidCommand() method for checking whether a set of
+ * arguments are valid for a given command.
+ */
+public abstract class Command {
+ private static final String ARGUMENT_DELIMITER = "--";
+ private static final String DELIMITER_FOR_WORDS = " ";
+ private static final String WEIRD_ARGUMENTS_GIVEN = "Weird arguments given for command, cannot continue";
+ // Key: An argument's name. Value: An argument's provided value from the user
+ private final HashMap
+ * May throw Exceptions if command fails.
+ *
+ * @throws WellNusException If command fails
+ */
+ public abstract void execute() throws WellNusException;
+
+ /**
+ * Very basic specialised toString() method for commands that returns
+ * a formatted list of all arguments issued by the user.
+ * Example:
+ * For the
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ *
+ * In short, user input is a list of commands, each command containing arguments and payloads.
+ * CommandParser should be used to break down raw user input into
+ * logical
+ * This function handles some adversarial user input.
+ * There are 2 possible adversarial inputs that this function checks for:
+ *
+ * 1. Whitespace/Empty Arguments: `cmd payload -- payload1 -- `
+ * 2. Missing main argument: `--argument payload`
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * Prints a brief description of all of WellNus' supported commands if
+ * the basic 'help' command was issued.
+ * Prints a detailed description of a specific feature if the specialised
+ * 'help' command was issued.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException exception) {
+ getTextUi().printErrorFor(exception, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ this.printHelpMessage();
+ }
+
+ /**
+ * Checks whether the given arguments are valid for our help command.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser using user's command
+ * @throws BadCommandException If the command is invalid
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * It runs an event driver, matches user input to the selected feature
+ * and executes its instance to launch the feature Manager.
+ */
+public class MainManager extends Manager {
+ public static final String FEATURE_HELP_DESCRIPTION = "WellNUS++ is a Command Line Interface (CLI)"
+ + " app for you to keep track, manage and improve your physical and mental wellness.";
+ protected static final String EXIT_COMMAND_KEYWORD = "exit";
+ protected static final String HELP_COMMAND_KEYWORD = "help";
+ private static final String COMMAND_IS_BLANK_MESSAGE = "Command is blank - please check user input code for "
+ + "MainManager.";
+ private static final String COMMAND_IS_NULL_MESSAGE = "Command is null - please check user input code for "
+ + "MainManager.";
+ private static final String FEATURE_NAME = "main";
+ private static final String GREETING_MESSAGE = "Enter a command to start using WellNUS++! Try 'help' "
+ + "if you're new, or just unsure.";
+ private static final String INVALID_COMMAND_MESSAGE = "Invalid command issued!";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String INVALID_COMMAND_ADDITIONAL_MESSAGE =
+ "Supported features: " + LINE_SEPARATOR
+ + "Access Atomic Habit: hb" + LINE_SEPARATOR
+ + "Access Self Reflection: reflect" + LINE_SEPARATOR
+ + "Access Focus Timer: ft" + LINE_SEPARATOR
+ + "Access Gamification: gamif" + LINE_SEPARATOR
+ + "Help command: help" + LINE_SEPARATOR
+ + "Exit program: exit";
+ private static final String INVALID_FEATURE_KEYWORD_MESSAGE = "Feature keyword can't be empty dear";
+ private static final int NUM_OF_ARGUMENTS = 1;
+ private static final String INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to '%s'!";
+ private static final String UNNECESSARY_PAYLOAD_MESSAGE = "Invalid payload given to '%s', drop the '%s' "
+ + "and try again!";
+ private static final String WELLNUS_FEATURE_NAME = "";
+ private final ArrayList
+ * If an unrecognised command is given, a warning is printed on the user's screen.
+ */
+ private void executeCommands() {
+ if (!hasExecutedCommands) {
+ this.setSupportedFeatureManagers();
+ hasExecutedCommands = true;
+ }
+ boolean isExit = false;
+ CommandParser parser = new CommandParser();
+ while (!isExit) {
+ try {
+ String nextCommand = this.getTextUi().getCommand();
+ validate(nextCommand);
+ // nextCommand now guaranteed to be a supported feature/main command
+ String featureKeyword = parser.getMainArgument(nextCommand);
+ Optional
+ * This exception should only be used within the AtomicHabit package.
+ * It differentiates between WellNUS and regular Java exceptions,
+ * which allows better pinpointing of errors to the AtomicHabit logic.
+ */
+public class AtomicHabitException extends WellNusException {
+ public AtomicHabitException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/wellnus/exception/BadCommandException.java b/src/main/java/wellnus/exception/BadCommandException.java
new file mode 100644
index 0000000000..be574061c7
--- /dev/null
+++ b/src/main/java/wellnus/exception/BadCommandException.java
@@ -0,0 +1,14 @@
+package wellnus.exception;
+
+/**
+ * BadCommandException is thrown when a conceptual/logical error occurs in Command (sub)classes
+ * This exception should be used in classes extending from the Command class.
+ * It differentiates between WellNUS and regular Java exceptions,
+ * allowing better pinpointing of errors to Command subclasses.
+ */
+public class BadCommandException extends WellNusException {
+ public BadCommandException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/wellnus/exception/FocusException.java b/src/main/java/wellnus/exception/FocusException.java
new file mode 100644
index 0000000000..b894589349
--- /dev/null
+++ b/src/main/java/wellnus/exception/FocusException.java
@@ -0,0 +1,13 @@
+package wellnus.exception;
+
+/**
+ * FocusException is thrown when a conceptual/logical error occurs in Focus.
+ *
+ * This exception is only thrown within the functional code for Focus.
+ */
+public class FocusException extends WellNusException {
+ public FocusException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/wellnus/exception/GamificationException.java b/src/main/java/wellnus/exception/GamificationException.java
new file mode 100644
index 0000000000..a410bda0cb
--- /dev/null
+++ b/src/main/java/wellnus/exception/GamificationException.java
@@ -0,0 +1,14 @@
+package wellnus.exception;
+
+/**
+ * Category of Exceptions related to the gamification feature.
+ */
+public class GamificationException extends WellNusException {
+ /**
+ * Returns an instance of the GamificationException.
+ * @param message Error message to display on the user's screen
+ */
+ public GamificationException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/wellnus/exception/ReflectionException.java b/src/main/java/wellnus/exception/ReflectionException.java
new file mode 100644
index 0000000000..c5138c3a6b
--- /dev/null
+++ b/src/main/java/wellnus/exception/ReflectionException.java
@@ -0,0 +1,12 @@
+package wellnus.exception;
+
+/**
+ * ReflectionException is thrown when a conceptual/logical error occurs in the reflection package
+ * This exception should only be used within the reflection package.
+ */
+public class ReflectionException extends WellNusException {
+ public ReflectionException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/wellnus/exception/StorageException.java b/src/main/java/wellnus/exception/StorageException.java
new file mode 100644
index 0000000000..a77e9e31bb
--- /dev/null
+++ b/src/main/java/wellnus/exception/StorageException.java
@@ -0,0 +1,12 @@
+package wellnus.exception;
+
+/**
+ * StorageException is thrown when a conceptual/logical error occurs in Storage.
+ *
+ * This exception is only thrown within the functional code for Storage.
+ */
+public class StorageException extends Exception {
+ public StorageException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/wellnus/exception/TokenizerException.java b/src/main/java/wellnus/exception/TokenizerException.java
new file mode 100644
index 0000000000..07a38da65b
--- /dev/null
+++ b/src/main/java/wellnus/exception/TokenizerException.java
@@ -0,0 +1,15 @@
+package wellnus.exception;
+
+/**
+ * Category of Exceptions related to the
+ * This exception may be thrown for any general error in WellNUS++'s execution.
+ * It is meant to differentiate between Java exceptions to allow better pinpointing of errors.
+ */
+public class WellNusException extends Exception {
+ public WellNusException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/CheckCommand.java b/src/main/java/wellnus/focus/command/CheckCommand.java
new file mode 100644
index 0000000000..7f0c8953ab
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/CheckCommand.java
@@ -0,0 +1,135 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * Represents a command to check the time left in the current session.
+ */
+public class CheckCommand extends Command {
+
+ public static final String COMMAND_DESCRIPTION = "check - Check the time left in the current countdown."
+ + "Only usable when a countdown is not finished!";
+ public static final String COMMAND_USAGE = "usage: check";
+ public static final String COMMAND_KEYWORD = "check";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'check'";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'check'!";
+ private static final String CHECK_OUTPUT = "Time left: ";
+ private static final String ERROR_COUNTDOWN_NOT_RUNNING = "Nothing to check - the countdown has not started yet!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "check command " + COMMAND_USAGE;
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'check'!";
+ private final Session session;
+ private final FocusUi focusUi;
+
+ /**
+ * Constructs a CheckCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public CheckCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * These parameters are: cycles, work time, break time, long break time.
+ */
+public class ConfigCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "config - Change the number of cycles "
+ + "and length of your work, break and longbreak timings!";
+ public static final String COMMAND_USAGE = "usage: config [--cycle number] [--work minutes] "
+ + "[--break minutes] [--longbreak minutes]";
+ protected static final String COMMAND_KEYWORD = "config";
+ protected static final String ARGUMENT_CYCLE = "cycle";
+ protected static final String ARGUMENT_WORK = "work";
+ protected static final String ARGUMENT_BREAK = "break";
+ protected static final String ARGUMENT_LONG_BREAK = "longbreak";
+ private static final String PRINT_CONFIG_MESSAGE = "Okay, here's your configured session details!"
+ + System.lineSeparator();
+ private static final String PRINT_CONFIG_CYCLES = "Cycles: ";
+ private static final String PRINT_CONFIG_WORK = "Work: ";
+ private static final String PRINT_CONFIG_BREAK = "Break: ";
+ private static final String PRINT_CONFIG_LONG_BREAK = "Long break: ";
+ private static final String SINGLE_SPACE_PAD = " ";
+ private static final String PRINT_CONFIG_MINS = "minutes";
+ private static final String PRINT_CONFIG_MIN = "minute";
+ private static final String EMPTY_STRING = "";
+ private static final int COMMAND_MAX_NUM_ARGUMENTS = 5;
+ private static final int COMMAND_MIN_NUM_ARGUMENTS = 1;
+ private static final int MAX_MINUTES = 60;
+ private static final int MIN_MINUTES = 1;
+ private static final int MAX_CYCLES = 5;
+ private static final int MIN_CYCLES = 2;
+ private static final String ASSERT_STRING_INPUT_NOT_NULL = "String input should not be null!";
+ private static final String ERROR_NOT_A_NUMBER = "Invalid payload given in 'config', expected a valid integer!";
+ private static final String ERROR_LARGE_CYCLES = "Invalid cycle payload given in 'config', the maximum cycles you "
+ + "can set is " + MAX_CYCLES + "!";
+ private static final String ERROR_LESS_EQUAL_MIN_CYCLES = "Invalid cycle payload given in 'config', the minimum "
+ + "cycles you can set is " + MIN_CYCLES + "!";
+ private static final String ERROR_LARGE_MINUTES = "Invalid minutes payload given in 'config', the maximum time "
+ + "you can set is " + MAX_MINUTES + "!";
+ private static final String ERROR_LESS_EQUAL_MIN_MINUTES = "Invalid minutes payload given in 'config', the minimum "
+ + "time you can set is " + MIN_MINUTES + "!";
+ private static final String ERROR_LONGBREAK_LARGER = "Invalid new 'config'! Your long break time, %s min "
+ + "should be greater or equal to your "
+ + "longbreak timing, %s min!";
+ private static final String COMMAND_INVALID_ARGUMENTS = "Invalid arguments given to 'config'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'config'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "config command " + COMMAND_USAGE;
+ private static final String ASSERT_MISSING_KEYWORD = "Missing command keyword";
+ private static final Logger LOGGER = WellNusLogger.getLogger("ConfigCommandLogger");
+ private static final String LOG_VALIDATION_ASSUMPTION_FAIL = "New cycle/break/work time is assumed to "
+ + "have passed the validation bounds and type checking, but has"
+ + "unexpectedly failed the second redundant check! This may be a developer error.";
+ private static final String ERROR_SESSION_STARTED = "Cannot config the session as it has already started."
+ + System.lineSeparator()
+ + "If you want to reconfigure, `stop` the session and then `config`!";
+
+ private final FocusUi focusUi;
+ private final Session session;
+ private int newCycle;
+ private int newWork;
+ private int newBreak;
+ private int newLongBreak;
+
+
+ /**
+ * Builds an instance of ConfigCommand to allow modification of the common Session attributes
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public ConfigCommand(HashMap
+ * May throw Exceptions if command fails.
+ *
+ * @throws wellnus.exception.WellNusException If command fails
+ */
+ @Override
+ public void execute() throws WellNusException {
+ if (!session.isSessionReady()) {
+ focusUi.printOutputMessage(ERROR_SESSION_STARTED);
+ return;
+ }
+ HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * Prints a brief description of all of Focus Timer WellNus' supported commands if
+ * the basic 'help' command was issued.
+ * Prints a detailed description of a specific feature if the specialised
+ * 'help' command was issued.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException exception) {
+ getFocusUi().printErrorFor(exception, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ this.printHelpMessage();
+ }
+
+ /**
+ * Checks whether the given arguments are valid for our help command.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser using user's command
+ * @throws BadCommandException If the command is invalid
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * Used to determine whether to print the seconds left and accept any user input.
+ *
+ * @return boolean Representing if the countdown timer is printing.
+ */
+ public boolean isCountdownPrinting() {
+ return (minutes == DEFAULT_STOP_TIME
+ && seconds <= COUNTDOWN_PRINT_START_TIME
+ && seconds != DEFAULT_STOP_TIME);
+ }
+
+ /**
+ * This method will return the status of the countdown timer.
+ *
+ * @return true if the countdown timer has completed, false otherwise
+ */
+ public boolean getIsCompletedCountdown() {
+ return isCompletedCountdown.get();
+ }
+
+ /**
+ * This method will stop the countdown timer and stops the background thread.
+ */
+ public void setStop() {
+ // timer is only initialised in start() method, so calling setStop() leads
+ // to a crash. Catch this mistake with an assertion
+ assert timer != null : STOP_BEFORE_START_ASSERTION;
+ isCompletedCountdown.set(true);
+ isRunClock.set(false);
+ timer.cancel();
+ timer.purge();
+ }
+
+ /**
+ * This method will allow the countdown timer to count down.
+ * It does so by allowing the minutes and seconds to be decremented.
+ */
+ public void setStart() {
+ isRunClock.set(true);
+ }
+
+ /**
+ * This method will pause the countdown timer.
+ */
+ public void setPause() {
+ isRunClock.set(false);
+ }
+
+ /**
+ * This method will return the status of the countdown timer.
+ *
+ * @return true if the countdown timer is running, false otherwise
+ */
+ public boolean getIsRunning() {
+ return isRunClock.get();
+ }
+
+ /**
+ * This method will return the current minutes of the countdown timer.
+ *
+ * @return the minutes of the countdown timer
+ */
+ public int getMinutes() {
+ return this.minutes;
+ }
+
+ /**
+ * This method will return the current seconds of the countdown timer.
+ *
+ * @return the seconds of the countdown timer
+ */
+ public int getSeconds() {
+ return this.seconds;
+ }
+
+ /**
+ * This method will return the description of the countdown timer.
+ *
+ * @return the description of the countdown timer
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /**
+ * This method will return the ready status of the session
+ *
+ * Only the last countdown timer object in a session can have this as true.
+ * The last countdown timer object will be 'ready' only if it is not counting down.
+ * This is meant to help the Session manage starting new sessions.
+ *
+ * @return boolean representing the ready state of the session
+ */
+ public boolean getIsReady() {
+ return isReady;
+ }
+
+ public void setIsReady(boolean isReady) {
+ this.isReady = isReady;
+ }
+
+}
diff --git a/src/main/java/wellnus/focus/feature/FocusManager.java b/src/main/java/wellnus/focus/feature/FocusManager.java
new file mode 100644
index 0000000000..b5dd7a42f8
--- /dev/null
+++ b/src/main/java/wellnus/focus/feature/FocusManager.java
@@ -0,0 +1,211 @@
+package wellnus.focus.feature;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.WellNusException;
+import wellnus.focus.command.CheckCommand;
+import wellnus.focus.command.ConfigCommand;
+import wellnus.focus.command.HelpCommand;
+import wellnus.focus.command.HomeCommand;
+import wellnus.focus.command.NextCommand;
+import wellnus.focus.command.PauseCommand;
+import wellnus.focus.command.ResumeCommand;
+import wellnus.focus.command.StartCommand;
+import wellnus.focus.command.StopCommand;
+import wellnus.manager.Manager;
+
+/**
+ * Represents a class to run the event driver for the Focus Timer.
+ * This class will be called by the main manager.
+ * It will match the user input to the correct command and execute it.
+ */
+//@@author YongbinWang
+public class FocusManager extends Manager {
+
+ public static final String FEATURE_HELP_DESCRIPTION = "ft - Focus Timer - Set a configurable 'Pomodoro' timer "
+ + "with work and rest cycles to keep yourself focused and productive!";
+ public static final String FEATURE_NAME = "ft";
+ private static final String START_COMMAND_KEYWORD = "start";
+ private static final String PAUSE_COMMAND_KEYWORD = "pause";
+ private static final String RESUME_COMMAND_KEYWORD = "resume";
+ private static final String NEXT_COMMAND_KEYWORD = "next";
+ private static final String CONFIG_COMMAND_KEYWORD = "config";
+ private static final String HOME_COMMAND_KEYWORD = "home";
+ private static final String STOP_COMMAND_KEYWORD = "stop";
+ private static final String CHECK_COMMAND_KEYWORD = "check";
+ private static final String HELP_COMMAND_KEYWORD = "help";
+ private static final String UNKNOWN_COMMAND_MESSAGE = "Invalid command issued!";
+ private static final String FOCUS_TIMER_GREET = "Welcome to Focus Timer." + System.lineSeparator()
+ + "Start a focus session with `start`, or `config` the session first!";
+ private static final String FOCUS_GREETING_LOGO = " /$$$$$$$$ "
+ + System.lineSeparator()
+ +
+ "| $$_____/ " + System.lineSeparator()
+ +
+ "| $$ /$$$$$$ /$$$$$$$ /$$ /$$ /$$$$$$$" + System.lineSeparator()
+ +
+ "| $$$$$ /$$__ $$ /$$_____/| $$ | $$ /$$_____/ " + System.lineSeparator()
+ +
+ "| $$__/| $$ \\ $$| $$ | $$ | $$| $$$$$$ " + System.lineSeparator()
+ +
+ "| $$ | $$ | $$| $$ | $$ | $$ \\____ $$" + System.lineSeparator()
+ +
+ "| $$ | $$$$$$/| $$$$$$$| $$$$$$/ /$$$$$$$/" + System.lineSeparator()
+ +
+ "|__/ \\______/ \\_______/ \\______/ |_______/" + System.lineSeparator();
+ private static final String COMMAND_KEYWORD_ASSERTION = "The key cannot be null"
+ + ", check user-guide for valid commands";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "Supported commands in Focus Timer: " + LINE_SEPARATOR
+ + "check command " + CheckCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "config command " + ConfigCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "next command " + NextCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "pause command " + PauseCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "resume command " + ResumeCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "start command " + StartCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "stop command " + StopCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "help command " + HelpCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "home command " + HomeCommand.COMMAND_USAGE;
+ private static final String HOME_USAGE = "home command " + HomeCommand.COMMAND_USAGE;
+ private static final String CONFIG_USAGE = "config command " + ConfigCommand.COMMAND_USAGE;
+ private final FocusUi focusUi;
+ private final Session session;
+
+ /**
+ * Constructs a FocusManager object.
+ * Initialise a session and textUi.
+ * Session will be passed into different commands to be utilised.
+ */
+ public FocusManager() {
+ this.focusUi = new FocusUi();
+ this.focusUi.setCursorName(FEATURE_NAME);
+ this.session = new Session();
+ }
+
+ /**
+ * Parses the given command from the user.
+ * Determines the correct Command subclass that can handle its execution.
+ *
+ * @param commandString Full command issued by the user
+ * @return Command object that can execute the user's command
+ * @throws BadCommandException If an unknown command was issued by the user
+ */
+ private Command getCommandFor(String commandString) throws BadCommandException {
+ HashMap
+ * In this scenario, we define invalid phase to be the point where
+ * the countdown prints (10,9,...,1) on screen.
+ * Otherwise, it is valid.
+ *
+ * @return User input command with leading/dangling whitespace being removed
+ */
+ public String getCommand(Session session) {
+ Scanner scanner = getScanner();
+ // User tries to input a command in the invalid phase
+ if (isBlocking(session)) {
+ return waitAndGetCommand(session, scanner);
+ }
+ // User entered command during valid phase
+ String userCommand = getCommandWithCursor(PRINT_CURSOR, scanner);
+ // Edge case: User maliciously waits until the countdown print starts before pressing enter.
+ // In this case, the command should NOT be processed until the countdown phase is over.
+ if (isBlocking(session)) {
+ return waitAndGetCommand(session, scanner);
+ }
+ return userCommand;
+ }
+
+ private void printLogo(String logo) {
+ System.out.print(logo);
+ }
+
+ protected void printLogoWithSeparator(String logo) {
+ printSeparator();
+ printLogo(logo);
+ }
+
+ /**
+ * User has entered a command whilst the countdown printing phase is ongoing.
+ *
+ * Waits for the printing phase to end, flush stdin and then gets the command without printing a cursor
+ * as the Countdown will automatically print a cursor on completion.
+ *
+ * @param session the session containing all the countdowns
+ * @param scanner the scanner reading user input
+ * @return String User input command with leading/dangling whitespace being removed
+ */
+ private String waitAndGetCommand(Session session, Scanner scanner) {
+ waitForCountdownPrint(session);
+ flushInput(scanner);
+ // Get command without printing a cursor
+ return getCommandWithCursor(NO_PRINT_CURSOR, scanner);
+ }
+
+ /**
+ * This method is the lowest abstraction layer, talking to
+ *
+ * @param isPrintCursor the session containing all the countdowns
+ * @param scanner the scanner reading user input
+ * @return User input command with leading/dangling whitespace being removed
+ */
+ private String getCommandWithCursor(boolean isPrintCursor, Scanner scanner) {
+ String userCommand = "";
+ if (isPrintCursor) {
+ printCursor();
+ }
+ try {
+ String inputLine = scanner.nextLine();
+ userCommand = inputLine.trim();
+ } catch (BufferOverflowException bufferOverFlowException) {
+ LOGGER.log(Level.INFO, BUFFER_OVERFLOW_MSG);
+ printErrorFor(bufferOverFlowException, BUFFER_OVERFLOW_MSG);
+ } catch (NoSuchElementException noElementException) {
+ LOGGER.log(Level.INFO, NO_INPUT_ELEMENT_MSG);
+ printErrorFor(noElementException, NO_INPUT_ELEMENT_MSG);
+ }
+ return userCommand;
+ }
+
+ /**
+ * Checks if the countTimer is in an invalid phase which
+ * means that all stdin should be blocked.
+ *
+ * @param session the session containing all the countdowns
+ * @return boolean Representing the countdown state
+ */
+ public boolean isBlocking(Session session) {
+ return session.getCurrentCountdown().isCountdownPrinting() && session.isSessionCounting();
+ }
+
+ /**
+ * If the countdown is in its printing phase, wait for it to finish first
+ * The while loop is designed as such for checkstyle correctness (no empty body)
+ */
+ private void waitForCountdownPrint(Session session) {
+ while (true) {
+ if (!(isBlocking(session))) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Flush all input from Stdin.
+ *
+ * Gracefully rejects all overzealous input from users, cleaning the input buffer.
+ *
+ * @param scanner Reference to the scanner being used by the UI
+ */
+ private void flushInput(Scanner scanner) {
+ // Implement a timeout to avoid hanging
+ long flushStartTime = System.currentTimeMillis();
+ while (scanner.hasNextLine()
+ && System.currentTimeMillis() - flushStartTime < FLUSH_DELAY_TIMING) {
+ // Discard extraneous input
+ scanner.nextLine();
+ }
+ }
+
+}
diff --git a/src/main/java/wellnus/focus/feature/Session.java b/src/main/java/wellnus/focus/feature/Session.java
new file mode 100644
index 0000000000..0abb5e38ab
--- /dev/null
+++ b/src/main/java/wellnus/focus/feature/Session.java
@@ -0,0 +1,226 @@
+package wellnus.focus.feature;
+
+import java.util.ArrayList;
+
+//@@author YongbinWang
+
+/**
+ * Represents a session of Countdown objects.
+ * A session is a sequence of Countdown objects represented by an ArrayList.
+ * We define a Session to have 4 states.
+ *
+ * A session can only be ready if the current index is the last timer
+ * and the last timer's isReady is true, which can only occur if it is not counting down.
+ *
+ * @return boolean Representing the ready state of the session.
+ */
+ public boolean isSessionReady() {
+ return getCurrentCountdownIndex() == session.size() - INCREMENT && getCurrentCountdown().getIsReady();
+ }
+
+ /**
+ * Check if the session is in its Counting state.
+ *
+ * @return boolean Representing if the session's countdown is underway.
+ */
+ public boolean isSessionCounting() {
+ Countdown countdown = getCurrentCountdown();
+ return countdown.getIsRunning() && !countdown.getIsCompletedCountdown();
+ }
+
+ /**
+ * Check if the session is in its Waiting state.
+ *
+ * @return boolean Representing if the session's countdown is done and is waiting for a next command.
+ */
+ public boolean isSessionWaiting() {
+ Countdown countdown = getCurrentCountdown();
+ return !countdown.getIsRunning() && countdown.getIsCompletedCountdown();
+ }
+
+ /**
+ * Check if the session is in its Paused state.
+ *
+ * @return boolean Representing if the session's countdown is paused.
+ */
+ public boolean isSessionPaused() {
+ Countdown countdown = getCurrentCountdown();
+ return !countdown.getIsRunning() && !countdown.getIsCompletedCountdown() && !countdown.getIsReady();
+ }
+
+ /**
+ * Starts the timer for the current countdown and increment the index if needed.
+ */
+ public void startTimer() {
+ checkPrevCountdown();
+ getCurrentCountdown().start();
+ getCurrentCountdown().setStart();
+ }
+
+ /**
+ * Method to (re) initialise a session when start or stop command is executed.
+ */
+ public void initialiseSession() {
+ session.clear();
+ fillSession();
+ primeSessionIsReady();
+ }
+
+ /**
+ * Sets the isReady flag in the session for the last countdown object to be true
+ */
+ private void primeSessionIsReady() {
+ int lastIndex = session.size() - INCREMENT;
+ session.get(lastIndex).setIsReady(true);
+ }
+
+ //@@author YongbinWang
+
+ /**
+ * Check if the session has any countdowns in its list
+ *
+ * @return boolean Representing if there is any countdown
+ */
+ public boolean hasAnyCountdown() {
+ return session.size() > 0;
+ }
+
+ /**
+ * Method to reset the current countdown index back to 0.
+ * This method is called when the user wants to stop an ongoing session.
+ */
+ public void resetCurrentCountdownIndex() {
+ currentCountdownIndex = session.size() - INCREMENT;
+ primeSessionIsReady();
+ }
+
+ public int getWork() {
+ return work;
+ }
+
+ public void setWork(int newWork) {
+ this.work = newWork;
+ }
+
+ public int getCycle() {
+ return cycle;
+ }
+
+ public void setCycle(int newCycles) {
+ this.cycle = newCycles;
+ fillSession();
+ resetCurrentCountdownIndex();
+ }
+
+ public int getBrk() {
+ return brk;
+ }
+
+ public void setBrk(int newBrk) {
+ this.brk = newBrk;
+ }
+
+ public int getLongBrk() {
+ return longBrk;
+ }
+
+ public void setLongBrk(int newLongBrk) {
+ this.longBrk = newLongBrk;
+ }
+
+}
diff --git a/src/main/java/wellnus/gamification/GamificationManager.java b/src/main/java/wellnus/gamification/GamificationManager.java
new file mode 100644
index 0000000000..c01e40464f
--- /dev/null
+++ b/src/main/java/wellnus/gamification/GamificationManager.java
@@ -0,0 +1,123 @@
+package wellnus.gamification;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.StorageException;
+import wellnus.exception.TokenizerException;
+import wellnus.exception.WellNusException;
+import wellnus.gamification.command.HelpCommand;
+import wellnus.gamification.command.HomeCommand;
+import wellnus.gamification.command.StatsCommand;
+import wellnus.gamification.util.GamificationData;
+import wellnus.gamification.util.GamificationStorage;
+import wellnus.gamification.util.GamificationUi;
+import wellnus.manager.Manager;
+
+/**
+ * Manager for the gamification feature. Entry point for this class is the runEventDriver() method.
+ */
+public class GamificationManager extends Manager {
+ public static final String FEATURE_NAME = "gamif";
+ public static final String FEATURE_HELP_DESCRIPTION = "gamif - Gamification - Gamification gives you the "
+ + "motivation to continue improving your wellness by rewarding you for your efforts!";
+ private static final String CLEAN_DATA_FILE_ERROR_MESSAGE = "Gamification data file may remain corrupted.";
+ private static final String COMMAND_HELP = "help";
+ private static final String COMMAND_HOME = "home";
+ private static final String COMMAND_STATS = "stats";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String UNRECOGNISED_COMMAND_ERROR = "Invalid command issued!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE =
+ "Supported commands in Gamification: " + LINE_SEPARATOR
+ + "stats command " + StatsCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "help command " + HelpCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "home command " + HomeCommand.COMMAND_USAGE;
+ private static final String STATS_USAGE = "stats command " + StatsCommand.COMMAND_USAGE;
+ private static final String HOME_USAGE = "home command " + HomeCommand.COMMAND_USAGE;
+ private static final String LOAD_GAMIF_DATA_ERROR_MESSAGE = "Previous gamification data will not be restored.";
+ private GamificationData gamificationData;
+ private final GamificationUi gamificationUi;
+
+ /**
+ * Returns an instance of the GamificationManager.
+ */
+ public GamificationManager() {
+ this.gamificationUi = new GamificationUi();
+ try {
+ GamificationStorage gamificationStorage = new GamificationStorage();
+ this.gamificationData = gamificationStorage.loadData();
+ } catch (StorageException loadDataException) {
+ gamificationUi.printErrorFor(loadDataException, LOAD_GAMIF_DATA_ERROR_MESSAGE);
+ this.gamificationData = new GamificationData();
+ } catch (TokenizerException loadDataException) {
+ gamificationUi.printErrorFor(loadDataException, LOAD_GAMIF_DATA_ERROR_MESSAGE);
+ try {
+ GamificationStorage gamificationStorage = new GamificationStorage();
+ gamificationStorage.cleanDataFile();
+ } catch (StorageException storageException) {
+ gamificationUi.printErrorFor(storageException, CLEAN_DATA_FILE_ERROR_MESSAGE);
+ }
+ this.gamificationData = new GamificationData();
+ }
+ }
+
+ private Command getCommandFor(String command) throws BadCommandException {
+ HashMap
+ * Prints a brief description of all Gamification WellNus' supported commands if
+ * the basic 'help' command was issued.
+ * Prints a detailed description of a specific feature if the specialised
+ * 'help' command was issued.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException exception) {
+ gamificationUi.printErrorFor(exception, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ this.printHelpMessage();
+ }
+
+ /**
+ * Checks whether the given arguments are valid for our help command.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser using user's command
+ * @throws BadCommandException If the command is invalid
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * May throw Exceptions if command fails.
+ *
+ * @throws WellNusException If the home command fails
+ */
+ @Override
+ public void execute() throws WellNusException {
+ validateCommand(getArguments());
+ GamificationUi.printGoodbye();
+ }
+
+ /**
+ * Validate the arguments given by the user.
+ * Each feature consists of multiple
+ * Each manager may also support entering other features
+ * via
+ * The manager should run an event driver (infinite loop) and is in charge
+ * of a Feature's input, output, 'business' logic and graceful termination.
+ */
+public abstract class Manager {
+
+ protected CommandParser commandParser;
+
+ /**
+ * Construct a feature Manager to handle control flow for the given feature.
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * Prints a brief description of all Reflection WellNus' supported commands if
+ * the basic 'help' command was issued.
+ * Prints a detailed description of a specific feature if the specialised
+ * 'help' command was issued.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException exception) {
+ getTextUi().printErrorFor(exception, COMMAND_INVALID_COMMAND_NOTE);
+ return;
+ }
+ this.printHelpMessage();
+ }
+
+ /**
+ * Checks whether the given arguments are valid for our help command.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser using user's command
+ * @throws BadCommandException If the command is invalid
+ */
+ @Override
+ public void validateCommand(HashMap
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ * For example, for the 'add' command in AtomicHabit package:
+ */
+public class AddCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "add - Add a habit to your habit tracker.";
+ public static final String COMMAND_USAGE = "usage: add --name (your habit name)";
+ public static final String COMMAND_KEYWORD = "add";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'add'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'add'!";
+ private static final String COMMAND_EMPTY_NAME = "Invalid habit name given to 'add'!";
+ private static final String DUPLICATE_HABIT_MESSAGE = "You already have this habit in your list!"
+ + " Use 'update' instead.";
+ private static final String COMMAND_INVALID_HABIT_NAME_MESSAGE = "Invalid habit name given to 'add'!"
+ + System.lineSeparator()
+ + "Habit name should not contain only numbers and symbols!";
+ private static final String COMMAND_NAME_ARGUMENT = "name";
+ private static final String COMMAND_KEYWORD_ASSERTION = "The key should be add.";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 2;
+ private static final String REGEX_NUMBER_AND_SYMBOL_ONLY_PATTERN = "^[\\d\\p{Punct}\\p{S}]*$";
+ private static final String COMMAND_WRONG_KEYWORD_MESSAGE = "Invalid command issued, expected 'add'!";
+ private static final String FEEDBACK_STRING_ONE = "Yay! You have added a new habit:";
+ private static final String FEEDBACK_STRING_TWO = "was successfully added";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "add command " + COMMAND_USAGE;
+ private final AtomicHabitList atomicHabits;
+ private final AtomicHabitUi atomicHabitUi;
+
+ /**
+ * Constructs an AddCommand object.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser.
+ * @param atomicHabits The AtomicHabitList object to add the habit to.
+ */
+ public AddCommand(HashMap
+ *
+ * If no exceptions are thrown, command is valid.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws BadCommandException If the arguments have any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
+
+
diff --git a/src/main/java/wellnus/atomichabit/command/DeleteCommand.java b/src/main/java/wellnus/atomichabit/command/DeleteCommand.java
new file mode 100644
index 0000000000..115f713d03
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/command/DeleteCommand.java
@@ -0,0 +1,204 @@
+package wellnus.atomichabit.command;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.atomichabit.feature.AtomicHabit;
+import wellnus.atomichabit.feature.AtomicHabitList;
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.atomichabit.feature.AtomicHabitUi;
+import wellnus.command.Command;
+import wellnus.common.WellNusLogger;
+import wellnus.exception.AtomicHabitException;
+import wellnus.exception.BadCommandException;
+import wellnus.ui.TextUi;
+
+/**
+ * The DeleteCommand class is a command class that deletes a habit
+ * has been preformed.
+ */
+public class DeleteCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "delete - Delete the habit you don't want to continue.";
+ public static final String COMMAND_USAGE = "usage: delete --id habit-index";
+ public static final String COMMAND_KEYWORD = "delete";
+ private static final String COMMAND_INDEX_ARGUMENT = "id";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 2;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'delete'";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'delete'!";
+ private static final String FEEDBACK_STRING = "The following habit has been deleted:";
+ private static final String FEEDBACK_STRING_TWO = "has been successfully deleted";
+ private static final String FEEDBACK_INDEX_NOT_INTEGER_ERROR = "Invalid index payload given in 'delete', expected "
+ + "a valid integer";
+ private static final String FEEDBACK_INDEX_OUT_OF_BOUNDS_ERROR = "Invalid index payload given in 'delete', "
+ + "index is out of range!";
+ private static final String FEEDBACK_EMPTY_LIST_UPDATE = "There are no habits to delete! "
+ + "Please `add` a habit first!";
+ private static final int INDEX_OFFSET = 1;
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String DELETE_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'delete'";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "delete command " + COMMAND_USAGE;
+ private static final Logger LOGGER = WellNusLogger.getLogger("DeleteAtomicHabitLogger");
+ private static final String LOG_STR_INPUT_NOT_INTEGER = "Input string is not an integer."
+ + "This should be properly handled";
+
+ private static final String LOG_INDEX_OUT_OF_BOUNDS = "Input index is out of bounds."
+ + "This should be properly handled";
+ private final AtomicHabitList atomicHabits;
+ private final AtomicHabitUi atomicHabitUi;
+
+ /**
+ * Constructs an DeleteCommand object with the given arguments and AtomicHabitList.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser.
+ * @param atomicHabits The AtomicHabitList object containing habit to be deleted.
+ */
+ public DeleteCommand(HashMap
+ *
+ * @param inputStream An InputStream object representing the input stream to be used.
+ * @param arguments Argument-Payload map generated by CommandParser.
+ * @param atomicHabits The AtomicHabitList object containing habit to be deleted.
+ */
+ public DeleteCommand(InputStream inputStream, HashMap
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+}
diff --git a/src/main/java/wellnus/atomichabit/command/HelpCommand.java b/src/main/java/wellnus/atomichabit/command/HelpCommand.java
new file mode 100644
index 0000000000..4a1e28bcb2
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/command/HelpCommand.java
@@ -0,0 +1,198 @@
+package wellnus.atomichabit.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.atomichabit.feature.AtomicHabitUi;
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+
+
+/**
+ * Implementation of Atomic Habit WellNus' help
command. Explains to the user what commands are supported
+ * by Atomic Habit and how to use each command.
+ */
+
+public class HelpCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "help - Get help on what commands can be used "
+ + "in Atomic Habit WellNUS++";
+ public static final String COMMAND_USAGE = "usage: help [command-to-check]";
+ private static final String BAD_ARGUMENTS_MESSAGE = "Invalid arguments given to 'help'!";
+ private static final String COMMAND_KEYWORD = "help";
+ private static final String NO_FEATURE_KEYWORD = "";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String HELP_PREAMBLE = "Input `help` to see all available commands." + LINE_SEPARATOR
+ + "Input `help [command-to-check] to get usage help for a specific command." + LINE_SEPARATOR
+ + "Here are all the commands available for you!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'help'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "help command " + COMMAND_USAGE;
+ private static final String PADDING = " ";
+ private static final String DOT = ".";
+ private static final int ONE_OFFSET = 1;
+ private static final int EXPECTED_PAYLOAD_SIZE = 1;
+ private final AtomicHabitUi atomicHabitUi;
+
+ /**
+ * Initialises a HelpCommand Object using the command arguments issued by the user.
+ *
+ * @param arguments Command arguments issued by the user
+ */
+ public HelpCommand(HashMap
+ * If the commandToSearch does not exist, help will print an unknown command
+ * error message.
+ */
+ public void printSpecificHelpMessage(String commandToSearch) {
+ switch (commandToSearch) {
+ case AddCommand.COMMAND_KEYWORD:
+ printUsageMessage(AddCommand.COMMAND_DESCRIPTION, AddCommand.COMMAND_USAGE);
+ break;
+ case DeleteCommand.COMMAND_KEYWORD:
+ printUsageMessage(DeleteCommand.COMMAND_DESCRIPTION, DeleteCommand.COMMAND_USAGE);
+ break;
+ case HelpCommand.COMMAND_KEYWORD:
+ printUsageMessage(HelpCommand.COMMAND_DESCRIPTION, HelpCommand.COMMAND_USAGE);
+ break;
+ case HomeCommand.COMMAND_KEYWORD:
+ printUsageMessage(HomeCommand.COMMAND_DESCRIPTION, HomeCommand.COMMAND_USAGE);
+ break;
+ case ListCommand.COMMAND_KEYWORD:
+ printUsageMessage(ListCommand.COMMAND_DESCRIPTION, ListCommand.COMMAND_USAGE);
+ break;
+ case UpdateCommand.COMMAND_KEYWORD:
+ printUsageMessage(UpdateCommand.COMMAND_DESCRIPTION, UpdateCommand.COMMAND_USAGE);
+ break;
+ default:
+ BadCommandException unknownCommand = new BadCommandException(COMMAND_INVALID_PAYLOAD);
+ atomicHabitUi.printErrorFor(unknownCommand, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+
+ private void printUsageMessage(String commandDescription, String usageString) {
+ String message = commandDescription + System.lineSeparator() + usageString;
+ atomicHabitUi.printOutputMessage(message);
+ }
+
+ @Override
+ protected String getCommandKeyword() {
+ return HelpCommand.COMMAND_KEYWORD;
+ }
+
+ @Override
+ protected String getFeatureKeyword() {
+ return HelpCommand.NO_FEATURE_KEYWORD;
+ }
+
+ /**
+ * Executes the issued help command.
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/atomichabit/command/HomeCommand.java b/src/main/java/wellnus/atomichabit/command/HomeCommand.java
new file mode 100644
index 0000000000..6a6e18293a
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/command/HomeCommand.java
@@ -0,0 +1,138 @@
+package wellnus.atomichabit.command;
+
+import java.util.HashMap;
+
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.atomichabit.feature.AtomicHabitUi;
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.WellNusException;
+
+/**
+ * The HomeCommand class is a command class that returns user back to the main WellNUS++ program.
+ */
+public class HomeCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "home - Return back to the main menu of WellNUS++.";
+ public static final String COMMAND_USAGE = "usage: home";
+ public static final String COMMAND_KEYWORD = "home";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'home'!";
+ private static final String COMMAND_INVALID_ARGUMENTS = "Invalid arguments given to 'home'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'home'!";
+ private static final String HOME_MESSAGE = "Thank you for using atomic habits. Do not forget about me!";
+ private final AtomicHabitUi atomicHabitUi;
+
+ /**
+ * Constructs an HomeCommand object.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser.
+ */
+ public HomeCommand(HashMap
+ *
+ * The validation logic and strictness is up to the implementer.
+ *
+ * As a guideline, isValidCommand
should minimally:
+ *
+ * Additionally, payload value cleanup (such as trimming) is also possible.
+ * As Java is pass (object reference) by value, any changes made to commandMap
+ * will persist out of the function call.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws BadCommandException if the commandMap has any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
+
+
diff --git a/src/main/java/wellnus/atomichabit/command/ListCommand.java b/src/main/java/wellnus/atomichabit/command/ListCommand.java
new file mode 100644
index 0000000000..9321e5d810
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/command/ListCommand.java
@@ -0,0 +1,149 @@
+package wellnus.atomichabit.command;
+
+import java.util.HashMap;
+
+import wellnus.atomichabit.feature.AtomicHabit;
+import wellnus.atomichabit.feature.AtomicHabitList;
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.atomichabit.feature.AtomicHabitUi;
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+
+
+/**
+ * The ListCommand class is a command class that lists all atomic habit in AtomicHabitList.
+ */
+public class ListCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "list - Lists out all the habits in your tracker.";
+ public static final String COMMAND_USAGE = "usage: list";
+ public static final String COMMAND_KEYWORD = "list";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'list'!";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'list'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'list'!";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String FIRST_STRING = "Here is the current accumulation of your atomic habits!"
+ + LINE_SEPARATOR + "Keep up the good work and you will develop a helpful habit in no time";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "list command " + COMMAND_USAGE;
+ private static final String EMPTY_LIST_MESSAGE = "You have no habits in your list!"
+ + LINE_SEPARATOR
+ + "Start adding some habits by using 'add'!";
+ private final AtomicHabitList atomicHabits;
+ private final AtomicHabitUi atomicHabitUi;
+
+ /**
+ * Constructs an ListCommand object.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser.
+ * @param atomicHabits The AtomicHabitList object to get all atomic habit.
+ */
+ public ListCommand(HashMap
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_USAGE;
+ }
+}
+
diff --git a/src/main/java/wellnus/atomichabit/command/UpdateCommand.java b/src/main/java/wellnus/atomichabit/command/UpdateCommand.java
new file mode 100644
index 0000000000..9f621b639f
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/command/UpdateCommand.java
@@ -0,0 +1,296 @@
+package wellnus.atomichabit.command;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.atomichabit.feature.AtomicHabit;
+import wellnus.atomichabit.feature.AtomicHabitList;
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.atomichabit.feature.AtomicHabitUi;
+import wellnus.command.Command;
+import wellnus.common.WellNusLogger;
+import wellnus.exception.AtomicHabitException;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.StorageException;
+import wellnus.gamification.util.GamificationData;
+import wellnus.gamification.util.GamificationUi;
+
+/**
+ * The UpdateCommand class is a command class that updates the number of times a habit
+ * has been preformed.
+ */
+public class UpdateCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "update - Update how many times you've done a habit.";
+ public static final String COMMAND_USAGE = "usage: update --id habit-index [--by increment_number]";
+ public static final String COMMAND_KEYWORD = "update";
+ private static final String COMMAND_INCREMENT_ARGUMENT = "by";
+ private static final String COMMAND_INDEX_ARGUMENT = "id";
+ private static final int COMMAND_MIN_NUM_OF_ARGUMENTS = 2;
+ private static final int COMMAND_MAX_NUM_OF_ARGUMENTS = 3;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'update'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "update command " + COMMAND_USAGE;
+ private static final String COMMAND_INVALID_ARGUMENT_MESSAGE = "Invalid arguments given to 'update'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'update'!";
+ private static final String COMMAND_INVALID_PAYLOAD_INCREMENT = "Invalid payload given in 'update' "
+ + "command!";
+ private static final String DOT = ".";
+ private static final int DEFAULT_INCREMENT = 1;
+ private static final int ZERO = 0;
+ private static final String FEEDBACK_INDEX_NOT_INTEGER_ERROR = "Invalid payload given in 'update' command, "
+ + "expected a valid integer!";
+ private static final String FEEDBACK_INDEX_OUT_OF_BOUNDS_ERROR = "Invalid payload given in 'update' command, "
+ + "index is out of range!";
+ private static final String FEEDBACK_DECREMENT_ERROR = "Invalid decrement payload given in 'update' command, "
+ + "decrement value is out of range!";
+ private static final String FEEDBACK_STRING_INCREMENT = "The following habit has been incremented! "
+ + "Keep up the good work!";
+ private static final String FEEDBACK_STRING_DECREMENT = "The following habit has been decremented.";
+ private static final String FEEDBACK_EMPTY_LIST_UPDATE = "There are no habits to update! "
+ + "Please `add` a habit first!";
+ private static final String FEEDBACK_CHANGE_COUNT_ZERO = "Invalid count integer, updating by 0 is not allowed!";
+ private static final int INDEX_OFFSET = 1;
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final int MINIMUM_INCREMENT = 1;
+ private static final String UPDATE_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'update'!";
+ private static final String UPDATE_INVALID_INCREMENT_COUNT = "Invalid increment payload given in 'update' command, "
+ + "increment with minimum of 1 is expected!";
+ private static final String STORE_GAMIF_DATA_FAILED_NOTE_MESSAGE = "Error saving to storage!";
+ private static final Logger LOGGER = WellNusLogger.getLogger("UpdateAtomicHabitLogger");
+ private static final String LOG_STR_INPUT_NOT_INTEGER = "Input string is not an integer."
+ + "This should be properly handled";
+ private static final String LOG_INDEX_OUT_OF_BOUNDS = "Input index is out of bounds."
+ + "This should be properly handled";
+ private static final int NUM_OF_XP_PER_INCREMENT = 1;
+ private final AtomicHabitList atomicHabits;
+ private final GamificationData gamificationData;
+ private final AtomicHabitUi atomicHabitUi;
+
+ /**
+ * Constructs an UpdateCommand object with the given arguments and AtomicHabitList.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser.
+ * @param atomicHabits The AtomicHabitList object containing habit to be updates.
+ */
+ public UpdateCommand(HashMap
+ *
+ * @param inputStream An InputStream object representing the input stream to be used.
+ * @param arguments Argument-Payload map generated by CommandParser.
+ * @param atomicHabits The AtomicHabitList object containing habit to be updates.
+ */
+ public UpdateCommand(InputStream inputStream, HashMap
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+}
diff --git a/src/main/java/wellnus/atomichabit/feature/AtomicHabit.java b/src/main/java/wellnus/atomichabit/feature/AtomicHabit.java
new file mode 100644
index 0000000000..8c8d100581
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/feature/AtomicHabit.java
@@ -0,0 +1,56 @@
+package wellnus.atomichabit.feature;
+
+/**
+ * Class to represent a unique atomic habit that the user will practice
+ * It contains primarily the description of the habit and the count of the habit
+ */
+public class AtomicHabit {
+ private final String description;
+ private int count;
+
+ /**
+ * Constructor of atomic habit class
+ * Will initialise private description to the input parameter
+ * Assigns count to 1 when a new habit is added
+ *
+ * @param description Description of this new atomic habit provided by the user
+ */
+ public AtomicHabit(String description) {
+ this.description = description;
+ this.count = 0;
+ }
+
+ /**
+ * Constructor of atomic habit class.
+ * Will initialise private description and count to the input parameter.
+ *
+ * @param description Description of atomic habit.
+ * @param count Number of habit to be initialized.
+ */
+ public AtomicHabit(String description, int count) {
+ this.description = description;
+ this.count = count;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void increaseCount(int increment) {
+ count += increment;
+ }
+
+ public void decreaseCount(int decrement) {
+ count -= decrement;
+ }
+
+ @Override
+ public String toString() {
+ return getDescription();
+ }
+}
+
diff --git a/src/main/java/wellnus/atomichabit/feature/AtomicHabitList.java b/src/main/java/wellnus/atomichabit/feature/AtomicHabitList.java
new file mode 100644
index 0000000000..1e9de99fc0
--- /dev/null
+++ b/src/main/java/wellnus/atomichabit/feature/AtomicHabitList.java
@@ -0,0 +1,119 @@
+package wellnus.atomichabit.feature;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.common.WellNusLogger;
+import wellnus.exception.StorageException;
+import wellnus.exception.TokenizerException;
+import wellnus.storage.AtomicHabitTokenizer;
+import wellnus.storage.Storage;
+import wellnus.ui.TextUi;
+
+/**
+ * Class to represent a container that will contain all unique AtomicHabit objects in an arraylist
+ */
+public class AtomicHabitList {
+
+ private static final String TOKENIZER_ERROR = "Previous atomic habit data will not be restored.";
+ private static final String STORAGE_ERROR = "The data cannot be stored properly!!";
+ private static final Logger LOGGER = WellNusLogger.getLogger("AtomicHabitListLogger");
+ private static final AtomicHabitTokenizer atomicHabitTokenizer = new AtomicHabitTokenizer();
+ private ArrayList
+ *
+ * Then continuously read commands from the user and execute those that are supported.
+ */
+ @Override
+ public void runEventDriver() {
+ greet();
+ runCommands();
+ }
+
+ /**
+ * Method to test for exception handling of invalid command using JUnit
+ *
+ * @param userCommand Command identified after parsing through userInput
+ * @return Command according to userInput
+ * @throws AtomicHabitException For every invalid command being tested below
+ */
+ public Command testInvalidCommand(String userCommand) throws AtomicHabitException {
+ String descriptionTest = "testing";
+ String exitCommand = "hb exit";
+ String listCommand = "hb list";
+ String indexTest = "1";
+ String invalidCommandErrorMessage = "Invalid command issued!!";
+ HashMap
+ *
+ *
+ *
+ * hb add
command, toString() will output
+ * hb [add] [--name] <habit name>
+ *
+ * @return String Representation of this Command that includes all given arguments
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(this.getFeatureKeyword());
+ builder.append(Command.DELIMITER_FOR_WORDS);
+ builder.append(this.getCommandKeyword());
+ for (Map.Entry
+ *
+ * The validation logic and strictness is up to the implementer.
+ *
+ * As a guideline, isValidCommand
should minimally:
+ *
+ * Additionally, payload value cleanup (such as trimming) is also possible.
+ * As Java is pass (object reference) by value, any changes made to commandMap
+ * will persist out of the function call.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws BadCommandException If the arguments have any issues
+ */
+ public abstract void validateCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ public abstract String getCommandUsage();
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ public abstract String getCommandDescription();
+
+}
diff --git a/src/main/java/wellnus/command/CommandParser.java b/src/main/java/wellnus/command/CommandParser.java
new file mode 100644
index 0000000000..895f908c8d
--- /dev/null
+++ b/src/main/java/wellnus/command/CommandParser.java
@@ -0,0 +1,187 @@
+package wellnus.command;
+
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.common.WellNusLogger;
+import wellnus.exception.BadCommandException;
+
+//@@author nichyjt
+
+/**
+ * A CommandParser processes user input from a defined format
+ * Each user input via console consists of:
+ *
+ *
+ *
+ *
+ *
+ *
+ * Further, we define the FIRST command to be the MAIN command of any given user input.
+ * So, "deadline work on CS2113 --by Sunday"
has "deadline work on CS2113"
+ * as the main command
+ * Each command (argument-payload pair) except for the main command MUST
+ * be delimited by " --"
(whitespace intentional)
+ *
+ * For example, a given user input: "deadline work on CS2113 --by Sunday"
+ *
+ */
+public class CommandParser {
+
+ private static final String ARGUMENT_DELIMITER = " --";
+ private static final String UNPADDED_DELIMITER = "--";
+ private static final String PAYLOAD_DELIMITER = " ";
+
+ // Message string constants for errors and ui
+ private static final String ERROR_EMPTY_COMMAND = "Invalid command issued, command cannot be empty!";
+ private static final String ERROR_EMPTY_ARGUMENT = "Invalid arguments given, command is missing an argument!";
+ private static final String ERROR_REPEATED_ARGUMENT = "Invalid arguments given, command cannot have "
+ + "repeated arguments!";
+ private static final Logger LOGGER = WellNusLogger.getLogger("CommandParserLogger");
+ private static final String LOG_STR_EMPTY_INPUT = "Input string is empty. This should be properly handled";
+ private static final String LOG_EMPTY_ARG = "Argument is empty. This should be properly handled";
+
+ /**
+ * Constructs an instance of CommandParser.
+ * [Argument,Payload]
pairs
+ */
+ public CommandParser() {
+
+ }
+
+ /**
+ * Split a userInput by the standardized delimiter.
+ *
+ * Split renders it as ["cmd payload", " payload1", ""]
+ * " payload1" will cause issues with rendering
+ * So, check for empty commands and whitespace prefix.
+ *
+ * Split renders this as ["--argument payload"]
+ * So, check for "--" prefix.
+ *
+ * @param fullCommandString Raw user input from stdin in string form
+ * @return String array of command substrings
+ * @throws BadCommandException when command is empty or is problematic
+ */
+ private String[] splitIntoCommands(String fullCommandString) throws BadCommandException {
+ assert fullCommandString != null : "fullCommandString should not be null";
+ // Perform a string length sanity check
+ fullCommandString = fullCommandString.strip();
+ if (fullCommandString.length() == 0) {
+ LOGGER.log(Level.INFO, LOG_STR_EMPTY_INPUT);
+ throw new BadCommandException(ERROR_EMPTY_COMMAND);
+ }
+ int noLimit = -1;
+ String[] rawCommands = fullCommandString.split(ARGUMENT_DELIMITER, noLimit);
+ String[] cleanCommands = new String[rawCommands.length];
+ for (int i = 0; i < rawCommands.length; ++i) {
+ String currentCommand = rawCommands[i];
+ // Case 1 check
+ if (currentCommand.startsWith(" ") || currentCommand.length() == 0) {
+ LOGGER.log(Level.INFO, LOG_EMPTY_ARG);
+ throw new BadCommandException(ERROR_EMPTY_ARGUMENT);
+ }
+ // Strip command of whitespace to clean input
+ currentCommand = currentCommand.strip();
+ // Case 2 check
+ if (currentCommand.startsWith(UNPADDED_DELIMITER)) {
+ LOGGER.log(Level.INFO, LOG_EMPTY_ARG);
+ throw new BadCommandException(ERROR_EMPTY_COMMAND);
+ }
+ cleanCommands[i] = currentCommand;
+ }
+ return cleanCommands;
+ }
+
+ private String getArgumentFromCommand(String commandString) throws BadCommandException {
+ assert commandString != null : "commandString should not be null";
+
+ String[] words = commandString.split(PAYLOAD_DELIMITER);
+ // Bad input checks
+ if (words.length == 0) {
+ LOGGER.log(Level.INFO, LOG_STR_EMPTY_INPUT);
+ throw new BadCommandException(ERROR_EMPTY_ARGUMENT);
+ }
+ return words[0].toLowerCase().strip();
+ }
+
+ private String getPayloadFromCommand(String commandString) {
+ assert commandString != null : "commandString should not be null";
+
+ String[] words = commandString.split(PAYLOAD_DELIMITER);
+ String payload = "";
+ // Ignore the first word (Main Command), so start from 1
+ for (int i = 1; i < words.length; ++i) {
+ payload = payload.concat(words[i]);
+ if (i != words.length - 1) {
+ payload = payload.concat(PAYLOAD_DELIMITER);
+ }
+ }
+ // No checks for payload length is done as payload CAN be empty
+ return payload.strip();
+ }
+
+ /**
+ * Takes in raw user input and splits it into Argument-Payload pairs
+ *
+ * @param userInput Raw user input from stdin in string form
+ * @return HashMap mapping a Argument (key) to a Payload (value)
+ * @throws BadCommandException when command is empty or is problematic
+ */
+ public HashMap
+ * Practically, this is the First argument of any command string.
+ * For example, "hb add --name foobar"
+ * Has main argument "hb"
+ *
+ * @param userInput Any string input representing a command
+ * @return the inferred Main Argument, converted to lowercase
+ * @throws BadCommandException when String is empty
+ */
+ public String getMainArgument(String userInput) throws BadCommandException {
+ assert userInput != null : "userInput should not be null";
+
+ userInput = userInput.strip();
+ if (userInput.length() == 0) {
+ LOGGER.log(Level.INFO, LOG_STR_EMPTY_INPUT);
+ throw new BadCommandException(ERROR_EMPTY_COMMAND);
+ }
+ String[] parameters = userInput.split(" ");
+ return parameters[0].toLowerCase();
+ }
+}
diff --git a/src/main/java/wellnus/command/ExitCommand.java b/src/main/java/wellnus/command/ExitCommand.java
new file mode 100644
index 0000000000..a47910b2fc
--- /dev/null
+++ b/src/main/java/wellnus/command/ExitCommand.java
@@ -0,0 +1,113 @@
+package wellnus.command;
+
+import java.util.HashMap;
+
+import wellnus.exception.BadCommandException;
+import wellnus.exception.WellNusException;
+import wellnus.ui.TextUi;
+
+/**
+ * Provides the exit command of the WellNUS++ app.
+ */
+public class ExitCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "exit - Close WellNUS++ and return to your terminal.";
+ public static final String COMMAND_USAGE = "usage: exit";
+ public static final String COMMAND_KEYWORD = "exit";
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'exit'!";
+ private static final String EXTRA_PAYLOAD_MESSAGE = "Invalid payload given to 'exit'!";
+ private static final String FEATURE_KEYWORD = "";
+ private static final int NUM_OF_ARGUMENTS = 1;
+ private static final String INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'exit'";
+ private final TextUi textUi;
+
+ /**
+ * Initialises an ExitCommand Object using the arguments issued by the user.
+ *
+ * @param arguments Command arguments issued by the user
+ * @see ExitCommand#validateCommand(HashMap)
+ */
+ public ExitCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/command/HelpCommand.java b/src/main/java/wellnus/command/HelpCommand.java
new file mode 100644
index 0000000000..d03d377261
--- /dev/null
+++ b/src/main/java/wellnus/command/HelpCommand.java
@@ -0,0 +1,204 @@
+package wellnus.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.common.MainManager;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.gamification.GamificationManager;
+import wellnus.reflection.feature.ReflectionManager;
+import wellnus.ui.TextUi;
+
+//@@author nichyjt
+
+/**
+ * Implementation of WellNus' help
command. Explains to the user what commands are supported
+ * by WellNus and how to use each command.
+ */
+public class HelpCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "help - Get help on what commands can be used in WellNUS++.";
+ public static final String COMMAND_USAGE = "usage: help [command-to-check]";
+ private static final String COMMAND_KEYWORD = "help";
+ private static final String BAD_ARGUMENTS_MESSAGE = "Invalid arguments given to 'help'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'help'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "help command " + COMMAND_USAGE;
+ private static final String NO_FEATURE_KEYWORD = "";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String HELP_PREAMBLE = "Input `help` to see all available commands." + LINE_SEPARATOR
+ + "Input `help [command-to-check]` to get usage help for a specific command." + LINE_SEPARATOR
+ + "Here are all the commands available for you!";
+ private static final String USAGE_HABIT = "\tusage: hb";
+ private static final String USAGE_REFLECT = "\tusage: reflect";
+ private static final String USAGE_FOCUS = "\tusage: ft";
+ private static final String USAGE_GAMIFICATION = "\tusage: gamif";
+ private static final String PADDING = " ";
+ private static final String DOT = ".";
+ private static final int ONE_OFFSET = 1;
+ private static final int EXPECTED_PAYLOAD_SIZE = 1;
+ private final TextUi textUi;
+
+ /**
+ * Initialises a HelpCommand Object using the command arguments issued by the user.
+ *
+ * @param arguments Command arguments issued by the user
+ */
+ public HelpCommand(HashMap
+ * If the commandToSearch does not exist, help will print an unknown command
+ * error message.
+ */
+ public void printSpecificHelpMessage(String commandToSearch) {
+ switch (commandToSearch) {
+ case AtomicHabitManager.FEATURE_NAME:
+ printUsageMessage(AtomicHabitManager.FEATURE_HELP_DESCRIPTION, USAGE_HABIT);
+ break;
+ case ReflectionManager.FEATURE_NAME:
+ printUsageMessage(ReflectionManager.FEATURE_HELP_DESCRIPTION, USAGE_REFLECT);
+ break;
+ case FocusManager.FEATURE_NAME:
+ printUsageMessage(FocusManager.FEATURE_HELP_DESCRIPTION, USAGE_FOCUS);
+ break;
+ case GamificationManager.FEATURE_NAME:
+ printUsageMessage(GamificationManager.FEATURE_HELP_DESCRIPTION, USAGE_GAMIFICATION);
+ break;
+ case HelpCommand.COMMAND_KEYWORD:
+ printUsageMessage(HelpCommand.COMMAND_DESCRIPTION, HelpCommand.COMMAND_USAGE);
+ break;
+ case ExitCommand.COMMAND_KEYWORD:
+ printUsageMessage(ExitCommand.COMMAND_DESCRIPTION, ExitCommand.COMMAND_USAGE);
+ break;
+ default:
+ BadCommandException unknownCommand = new BadCommandException(COMMAND_INVALID_PAYLOAD);
+ textUi.printErrorFor(unknownCommand, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+
+ private void printUsageMessage(String commandDescription, String usageString) {
+ String message = commandDescription + System.lineSeparator() + usageString;
+ textUi.printOutputMessage(message);
+ }
+
+ @Override
+ protected String getCommandKeyword() {
+ return HelpCommand.COMMAND_KEYWORD;
+ }
+
+ @Override
+ protected String getFeatureKeyword() {
+ return HelpCommand.NO_FEATURE_KEYWORD;
+ }
+
+ /**
+ * Executes the issued help command.
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/common/MainManager.java b/src/main/java/wellnus/common/MainManager.java
new file mode 100644
index 0000000000..6fccf9244b
--- /dev/null
+++ b/src/main/java/wellnus/common/MainManager.java
@@ -0,0 +1,236 @@
+package wellnus.common;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+import wellnus.atomichabit.feature.AtomicHabitManager;
+import wellnus.command.Command;
+import wellnus.command.CommandParser;
+import wellnus.command.ExitCommand;
+import wellnus.command.HelpCommand;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.WellNusException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.gamification.GamificationManager;
+import wellnus.manager.Manager;
+import wellnus.reflection.feature.ReflectionManager;
+import wellnus.ui.TextUi;
+
+/**
+ * MainManager is the primary event driver for WellNUS++
+ *
+ * MainManager creates and stores exactly one instance of each feature's Manager in WellNUS++.
+ *
+ * Instantiates boilerplate utilities like TextUi
+ * and populates featureManagers with exactly one instance to be executed on user selection.
+ */
+ public MainManager() {
+ super();
+ this.featureManagers = new ArrayList<>();
+ this.textUi = new TextUi();
+ this.textUi.setCursorName(FEATURE_NAME);
+ }
+
+ /**
+ * Parses the given command String issued by the user and returns the corresponding
+ * Command object that can execute it.
+ *
+ * @param command Command issued by the user
+ * @return Command object that can execute the user's command
+ * @throws BadCommandException If command issued is not supported or invalid
+ */
+ protected Command getMainCommandFor(String command) throws BadCommandException {
+ String commandKeyword = getCommandParser().getMainArgument(command);
+ HashMap
+ * help
) as well as any feature-specific
+ * commands, which are delegated to the corresponding features' Managers.
+ *
+ * This method will keep reading the user's command until the exit command is given.
+ */
+ @Override
+ public void runEventDriver() {
+ this.greet();
+ this.executeCommands();
+ }
+
+ /**
+ * Returns a list of features supported by WellNUS++.
+ *
+ * Suggested implementation:
+ * this.supportedManagers.add([mgr1, mgr2, ...]);
+ */
+ protected void setSupportedFeatureManagers() {
+ GamificationManager gamificationManager = new GamificationManager();
+ this.getSupportedFeatureManagers().add(gamificationManager);
+ this.getSupportedFeatureManagers().add(
+ new AtomicHabitManager(gamificationManager.getGamificationData()));
+ this.getSupportedFeatureManagers().add(new ReflectionManager());
+ this.getSupportedFeatureManagers().add(new FocusManager());
+ }
+
+}
diff --git a/src/main/java/wellnus/common/WellNusLogger.java b/src/main/java/wellnus/common/WellNusLogger.java
new file mode 100644
index 0000000000..c857ab0a27
--- /dev/null
+++ b/src/main/java/wellnus/common/WellNusLogger.java
@@ -0,0 +1,131 @@
+package wellnus.common;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.logging.FileHandler;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+import wellnus.exception.StorageException;
+import wellnus.ui.TextUi;
+
+/**
+ * Wrapper class for java.util.logging.Logger
that redirects logging
+ * to a specific log file instead of printing it on the user's screen.
+ * @see Logger
+ */
+public class WellNusLogger {
+ private static final String CREATE_LOG_FILE_IO_EXCEPTION_MESSAGE = "Failed to create log file.";
+ private static final String EXCEPTION_NOTE_MESSAGE = "Logging will not be saved to a log file during this app "
+ + "session.";
+ private static final int FIVE_MEGABYTES = 5 * 1024 * 1024;
+ private static final String INVALID_LOG_PATH_MESSAGE = "Invalid log path, cannot create log file.";
+ private static final boolean IS_LOG_FILE_APPEND_MODE = true;
+ private static final String LOG_DIR_PATH = "log/";
+ private static final String LOG_FILE_NAME = "wellnus.log";
+ private static final String LOG_PATH_BLANK_MESSAGE = "Blank log path passed to WellNusLogger.checkLogPath().";
+ private static final String LOG_PATH_NULL_MESSAGE = "null log path passed to WellNusLogger.checkLogPath().";
+ private static final String LOGGER_NAME_BLANK_MESSAGE = "Name parameter given to WellNusLogger.getLogger() cannot "
+ + "be blank";
+ private static final String LOGGER_NAME_NULL_MESSAGE = "Name parameter given to WellNusLogger.getLogger() cannot "
+ + "be null";
+ private static final String IO_EXCEPTION_MESSAGE = "Failed to load log file.";
+ private static final int MAX_LOG_FILE_SIZE_MEGA_BYTES = 5;
+ private static final int MEGABYTE_DIVISOR = 1024 * 1024;
+ private static final int NUM_LOG_FILE = 1;
+ private static final String SECURITY_EXCEPTION_MESSAGE = "Unable to create log file due to security policies.";
+ private static final String UNKNOWN_ERROR_MESSAGE = "Unable to create log file due to unknown error.";
+ private static Optional
+ *
+ *
+ * Tokenizer
interface and its operations/subclasses.
+ * @see wellnus.storage.Tokenizer
+ */
+public class TokenizerException extends WellNusException {
+ /**
+ * Initializes an instance of TokenizerException with the given error message.
+ * @param errorMessage Error message to show to the user
+ */
+ public TokenizerException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/wellnus/exception/WellNusException.java b/src/main/java/wellnus/exception/WellNusException.java
new file mode 100644
index 0000000000..3bcd536dc0
--- /dev/null
+++ b/src/main/java/wellnus/exception/WellNusException.java
@@ -0,0 +1,13 @@
+package wellnus.exception;
+
+/**
+ * WellNusException is thrown when a conceptual/logical error occurs in WellNUS++
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/ConfigCommand.java b/src/main/java/wellnus/focus/command/ConfigCommand.java
new file mode 100644
index 0000000000..e42cf2e08e
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/ConfigCommand.java
@@ -0,0 +1,361 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.command.Command;
+import wellnus.common.WellNusLogger;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.FocusException;
+import wellnus.exception.WellNusException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+//@@author nichyjt
+
+/**
+ * ConfigCommand sets the configuration for a Session's parameters.
+ *
+ *
+ *
+ * The validation logic and strictness is up to the implementer.
+ *
+ * As a guideline, isValidCommand
should minimally:
+ *
+ * Additionally, payload value cleanup (such as trimming) is also possible.
+ * As Java is pass (object reference) by value, any changes made to commandMap
+ * will persist out of the function call.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @throws wellnus.exception.BadCommandException If the arguments have any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/HelpCommand.java b/src/main/java/wellnus/focus/command/HelpCommand.java
new file mode 100644
index 0000000000..1c55055f45
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/HelpCommand.java
@@ -0,0 +1,210 @@
+package wellnus.focus.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.ui.TextUi;
+
+/**
+ * Implementation of Focus Timer WellNus' help
command. Explains to the user what commands are supported
+ * by Focus Timer and how to use each command.
+ */
+public class HelpCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "help - Get help on what commands can be used "
+ + "in Focus Timer WellNUS++";
+ public static final String COMMAND_USAGE = "usage: help [command-to-check]";
+ private static final String BAD_ARGUMENTS_MESSAGE = "Invalid arguments given to 'help'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'help'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "help command " + COMMAND_USAGE;
+ private static final String COMMAND_KEYWORD = "help";
+ private static final String NO_FEATURE_KEYWORD = "";
+ private static final String HELP_PREAMBLE = "Input `help` to see all available commands."
+ + System.lineSeparator()
+ + "Input `help [command-to-check]` to get usage help for a specific command."
+ + System.lineSeparator()
+ + "Here are all the commands available for you!";
+ private static final String PADDING = " ";
+ private static final String DOT = ".";
+ private static final int ONE_OFFSET = 1;
+ private static final int EXPECTED_PAYLOAD_SIZE = 1;
+ private final FocusUi focusUi;
+
+ /**
+ * Initialises a HelpCommand Object using the command arguments issued by the user.
+ *
+ * @param arguments Command arguments issued by the user
+ */
+ public HelpCommand(HashMap
+ * If the commandToSearch does not exist, help will print an unknown command
+ * error message.
+ */
+ public void printSpecificHelpMessage(String commandToSearch) {
+ switch (commandToSearch) {
+ case CheckCommand.COMMAND_KEYWORD:
+ printUsageMessage(CheckCommand.COMMAND_DESCRIPTION, CheckCommand.COMMAND_USAGE);
+ break;
+ case ConfigCommand.COMMAND_KEYWORD:
+ printUsageMessage(ConfigCommand.COMMAND_DESCRIPTION, ConfigCommand.COMMAND_USAGE);
+ break;
+ case HelpCommand.COMMAND_KEYWORD:
+ printUsageMessage(HelpCommand.COMMAND_DESCRIPTION, HelpCommand.COMMAND_USAGE);
+ break;
+ case HomeCommand.COMMAND_KEYWORD:
+ printUsageMessage(HomeCommand.COMMAND_DESCRIPTION, HomeCommand.COMMAND_USAGE);
+ break;
+ case NextCommand.COMMAND_KEYWORD:
+ printUsageMessage(NextCommand.COMMAND_DESCRIPTION, NextCommand.COMMAND_USAGE);
+ break;
+ case PauseCommand.COMMAND_KEYWORD:
+ printUsageMessage(PauseCommand.COMMAND_DESCRIPTION, PauseCommand.COMMAND_USAGE);
+ break;
+ case ResumeCommand.COMMAND_KEYWORD:
+ printUsageMessage(ResumeCommand.COMMAND_DESCRIPTION, ResumeCommand.COMMAND_USAGE);
+ break;
+ case StartCommand.COMMAND_KEYWORD:
+ printUsageMessage(StartCommand.COMMAND_DESCRIPTION, StartCommand.COMMAND_USAGE);
+ break;
+ case StopCommand.COMMAND_KEYWORD:
+ printUsageMessage(StopCommand.COMMAND_DESCRIPTION, StopCommand.COMMAND_USAGE);
+ break;
+ default:
+ BadCommandException unknownCommand = new BadCommandException(COMMAND_INVALID_PAYLOAD);
+ focusUi.printErrorFor(unknownCommand, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+
+ private void printUsageMessage(String commandDescription, String usageString) {
+ String message = commandDescription + System.lineSeparator() + usageString;
+ focusUi.printOutputMessage(message);
+ }
+
+ @Override
+ protected String getCommandKeyword() {
+ return HelpCommand.COMMAND_KEYWORD;
+ }
+
+ @Override
+ protected String getFeatureKeyword() {
+ return HelpCommand.NO_FEATURE_KEYWORD;
+ }
+
+ /**
+ * Executes the issued help command.
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/HomeCommand.java b/src/main/java/wellnus/focus/command/HomeCommand.java
new file mode 100644
index 0000000000..1da88c8b65
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/HomeCommand.java
@@ -0,0 +1,134 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.WellNusException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * The HomeCommand class is a command class that returns user back to the main WellNUS++ program.
+ */
+public class HomeCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "home - Stop the session and go back to WellNUS++.";
+ public static final String COMMAND_USAGE = "usage: home";
+ public static final String COMMAND_KEYWORD = "home";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'home'!";
+ private static final String COMMAND_INVALID_ARGUMENTS = "Invalid arguments given to 'home'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'home'!";
+ private static final String HOME_MESSAGE = "Thank you for using focus timer. Keep up the productivity!";
+ private final FocusUi focusUi;
+ private final Session session;
+
+ /**
+ * Constructor for HomeCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload map generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public HomeCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/NextCommand.java b/src/main/java/wellnus/focus/command/NextCommand.java
new file mode 100644
index 0000000000..7dd5cadcd8
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/NextCommand.java
@@ -0,0 +1,137 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * Represents a class to start the next countdown in the session.
+ */
+public class NextCommand extends Command {
+
+ public static final String COMMAND_DESCRIPTION = "next - Move on to the next countdown. "
+ + "Can only be used when a countdown timer has ended.";
+ public static final String COMMAND_USAGE = "usage: next";
+ public static final String COMMAND_KEYWORD = "next";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'next'!";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'next'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'next'!";
+ private static final String ERROR_SESSION_NOT_STARTED = "A focus session has not started yet, "
+ + "try `start`ing one first!";
+ private static final String ERROR_COUNTDOWN_RUNNING = "Oops, your timer for this session is still ticking!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "next command " + COMMAND_USAGE;
+ private final Session session;
+ private final FocusUi focusUi;
+
+ /**
+ * Constructor for NextCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public NextCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
+
diff --git a/src/main/java/wellnus/focus/command/PauseCommand.java b/src/main/java/wellnus/focus/command/PauseCommand.java
new file mode 100644
index 0000000000..7d391e3cb1
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/PauseCommand.java
@@ -0,0 +1,138 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * Represents a class to pause the current countdown in the session.
+ */
+public class PauseCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "pause - Pause the session! "
+ + "Can only be used when a countdown is ticking.";
+ public static final String COMMAND_USAGE = "usage: pause";
+ public static final String COMMAND_KEYWORD = "pause";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'pause'!";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'pause'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'pause'!";
+ private static final String PAUSE_OUTPUT = "Timer paused at: ";
+ private static final String ERROR_COUNTDOWN_NOT_RUNNING = "Nothing to pause - the timer has not started yet!";
+ private static final String ERROR_IS_PAUSED = "Nothing to pause - you have already paused the timer!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "pause command " + COMMAND_USAGE;
+ private final Session session;
+ private final FocusUi focusUi;
+
+ /**
+ * Constructor for PauseCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public PauseCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/ResumeCommand.java b/src/main/java/wellnus/focus/command/ResumeCommand.java
new file mode 100644
index 0000000000..83d946c261
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/ResumeCommand.java
@@ -0,0 +1,130 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * Represents a command to resume the countdown timer in the current session.
+ */
+public class ResumeCommand extends Command {
+
+ public static final String COMMAND_DESCRIPTION = "resume - Continue the countdown. "
+ + "Can only be used when a countdown is paused.";
+ public static final String COMMAND_USAGE = "usage: home";
+ public static final String COMMAND_KEYWORD = "resume";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'resume'!";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'resume'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'resume'!";
+ private static final String COMMAND_KEYWORD_ASSERTION = "The key should be resume.";
+ private static final String RESUME_OUTPUT = "Timer resumed at: ";
+ private static final String ERROR_NOT_PAUSED = "You don't seem to be paused. Ignoring the command!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "resume command " + COMMAND_USAGE;
+ private final Session session;
+ private final FocusUi focusUi;
+
+ /**
+ * Constructs a ResumeCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public ResumeCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/StartCommand.java b/src/main/java/wellnus/focus/command/StartCommand.java
new file mode 100644
index 0000000000..42babe2960
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/StartCommand.java
@@ -0,0 +1,133 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * Represents a command to start the current session.
+ * Also used to start different countdowns timers in the session.
+ */
+public class StartCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "start - Start your focus session!";
+ public static final String COMMAND_USAGE = "usage: start";
+ public static final String COMMAND_KEYWORD = "start";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final int FIRST_COUNTDOWN_INDEX = 0;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'start'!";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'start'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'start'!";
+ private static final String START_MESSAGE = "Your session has started. All the best!";
+ private static final String ERROR_NOT_READY = "Nothing to start - your session has started!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "start command " + COMMAND_USAGE;
+ private final Session session;
+ private final FocusUi focusUi;
+
+ /**
+ * Constructor for StartCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public StartCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/focus/command/StopCommand.java b/src/main/java/wellnus/focus/command/StopCommand.java
new file mode 100644
index 0000000000..73705f65aa
--- /dev/null
+++ b/src/main/java/wellnus/focus/command/StopCommand.java
@@ -0,0 +1,137 @@
+package wellnus.focus.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.focus.feature.FocusManager;
+import wellnus.focus.feature.FocusUi;
+import wellnus.focus.feature.Session;
+
+/**
+ * Represents a command to stop the current session.
+ */
+public class StopCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "stop - Stop the session. You will have to `start` "
+ + "your focus session again!";
+ public static final String COMMAND_USAGE = "usage: stop";
+ public static final String COMMAND_KEYWORD = "stop";
+ private static final int COMMAND_NUM_OF_ARGUMENTS = 1;
+ private static final String COMMAND_INVALID_COMMAND_MESSAGE = "Invalid command issued, expected 'stop'!";
+ private static final String COMMAND_INVALID_ARGUMENTS_MESSAGE = "Invalid arguments given to 'stop'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'stop'!";
+ private static final String STOP_MESSAGE = "Your focus session has ended."
+ + System.lineSeparator()
+ + "To start a new session, `start` it up!"
+ + System.lineSeparator()
+ + "You can also configure the session to your liking with `config`!";
+ private static final String ERROR_NOT_STARTED = "Nothing to stop - the timer has not started yet!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "stop command " + COMMAND_USAGE;
+ private final Session session;
+ private final FocusUi focusUi;
+
+ /**
+ * Constructs a StopCommand object.
+ * Session in FocusManager is passed into this class.
+ *
+ * @param arguments Argument-Payload Hashmap generated by CommandParser
+ * @param session Session object which is an arraylist of Countdowns
+ */
+ public StopCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
+
diff --git a/src/main/java/wellnus/focus/feature/Countdown.java b/src/main/java/wellnus/focus/feature/Countdown.java
new file mode 100644
index 0000000000..ea49699225
--- /dev/null
+++ b/src/main/java/wellnus/focus/feature/Countdown.java
@@ -0,0 +1,233 @@
+package wellnus.focus.feature;
+
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+/**
+ * Class to represent a timer counting down given a specific minutes.
+ * This class contains a Timer class which is used to simulate a clock counting down.
+ * Atomic data type is used to communicate with the timer background thread.
+ */
+public class Countdown {
+ private static final int ONE_SECOND = 1000;
+ private static final int DELAY_TIME = 0;
+ private static final int DEFAULT_STOP_TIME = 0;
+ private static final int DEFAULT_SECONDS = 59;
+ private static final int INITIAL_SECONDS = 0;
+ private static final int COUNTDOWN_PRINT_START_TIME = 10;
+ private static final String MINUTES_INPUT_ASSERTION = "Minutes should be greater than 0";
+ private static final String STOP_BEFORE_START_ASSERTION = "Timer should be started before trying to stop it";
+ private static final String TIMER_NOT_RUNNING_ASSERTION = "Timer should not be running";
+ private static final String TIMER_COMPLETE_MESSAGE = "Type 'next' to begin the next countdown";
+ private static final String TIMER_COMPLETE_MESSAGE_LAST = "Congrats! That's a session done and dusted!"
+ + System.lineSeparator()
+ + "Type `start` to start a new session, or `config` to change the session settings.";
+ private static final String FEATURE_NAME = "ft";
+ private FocusUi focusUi;
+ private Timer timer;
+ private int minutes;
+ private int inputMinutes;
+ private int seconds;
+ private final String description;
+ private boolean isLast;
+ // Convenience attribute to signify that this countdown object is the rollover countdown
+ private boolean isReady = false;
+ private AtomicBoolean isCompletedCountdown;
+ private AtomicBoolean isRunClock;
+
+ /**
+ * Constructor of Countdown.
+ * This will initialise the private attributes of Countdown object.
+ *
+ * @param minutes the number of minutes to countdown
+ * @param description description of the current task user is focusing on
+ */
+ public Countdown(int minutes, String description, boolean isLast) {
+ assert minutes > 0 : MINUTES_INPUT_ASSERTION;
+ this.minutes = minutes;
+ this.inputMinutes = minutes;
+ this.seconds = INITIAL_SECONDS;
+ this.isCompletedCountdown = new AtomicBoolean(false);
+ this.isRunClock = new AtomicBoolean(false);
+ this.description = description;
+ this.focusUi = new FocusUi();
+ focusUi.setCursorName(FEATURE_NAME);
+ this.isLast = isLast;
+ }
+
+ /**
+ * This method will execute the actions necessary when a countdown completes.
+ * The timer will be stopped and a beep sound will be played.
+ * A message will be printed to the user to notify them that the countdown has completed.
+ */
+ private void timerComplete() {
+ setStop();
+ java.awt.Toolkit.getDefaultToolkit().beep();
+ if (isLast) {
+ focusUi.printOutputMessage(TIMER_COMPLETE_MESSAGE_LAST);
+ } else {
+ focusUi.printOutputMessage(TIMER_COMPLETE_MESSAGE);
+ }
+ this.minutes = inputMinutes;
+ this.isCompletedCountdown.set(true);
+ if (isLast) {
+ setIsReady(true);
+ }
+ focusUi.printCursor();
+ }
+
+ /**
+ * This method will decrement the minutes by 1;
+ */
+ private void decrementMinutes() {
+ minutes--;
+ }
+
+ /**
+ * This method will decrement the seconds by 1;
+ */
+ private void decrementSeconds() {
+ seconds--;
+ }
+
+ /**
+ * This method will start the countdown timer.
+ * Timer will notify user when the countdown timer has completed by playing a beep sound.
+ * The timer will run in the background and will be stopped when the countdown minutes and seconds reaches 0.
+ */
+ public void start() {
+ assert isRunClock.get() == false : TIMER_NOT_RUNNING_ASSERTION;
+ timer = new Timer();
+ TimerTask countdownTask = new TimerTask() {
+ @Override
+ public void run() {
+ setIsReady(false);
+ if (!isRunClock.get()) {
+ return;
+ }
+ if (minutes == DEFAULT_STOP_TIME && seconds == COUNTDOWN_PRINT_START_TIME) {
+ focusUi.printNewline();
+ }
+ if (isCountdownPrinting()) {
+ focusUi.printOutputMessage(seconds + " seconds left");
+ }
+ if (seconds == DEFAULT_STOP_TIME && minutes == DEFAULT_STOP_TIME) {
+ timerComplete();
+ } else if (seconds == DEFAULT_STOP_TIME) {
+ seconds = DEFAULT_SECONDS;
+ decrementMinutes();
+ } else {
+ decrementSeconds();
+ }
+ }
+ };
+ timer.scheduleAtFixedRate(countdownTask, DELAY_TIME, ONE_SECOND);
+ }
+
+ /**
+ * Utility method to check if the countdown is in its printing phase.
+ *
+ *
+ * The last timer holds a special `isReady` attribute to help Session determine if `start` and `config` is usable.
+ */
+public class Session {
+ private static final int INCREMENT = 1;
+ private static final boolean IS_LAST_COUNTDOWN = true;
+ private String workDescription = "Task Cycle: Do your task now!";
+ private final ArrayList
+ *
+ * It is calls the relevant methods to present the user with the gamification feature's interface
+ * and manage the user's commands.
+ */
+ @Override
+ public void runEventDriver() {
+ GamificationUi.printLogo();
+ boolean isExit = false;
+ while (!isExit) {
+ try {
+ String commandString = gamificationUi.getCommand();
+ Command command = getCommandFor(commandString);
+ command.execute();
+ isExit = HomeCommand.isHome(command);
+ } catch (WellNusException exception) {
+ String errorMessage = exception.getMessage();
+ if (errorMessage.contains(COMMAND_STATS)) {
+ gamificationUi.printErrorFor(exception, STATS_USAGE);
+ } else if (errorMessage.contains(COMMAND_HOME)) {
+ gamificationUi.printErrorFor(exception, HOME_USAGE);
+ } else {
+ gamificationUi.printErrorFor(exception, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/wellnus/gamification/command/HelpCommand.java b/src/main/java/wellnus/gamification/command/HelpCommand.java
new file mode 100644
index 0000000000..a7119dfdb8
--- /dev/null
+++ b/src/main/java/wellnus/gamification/command/HelpCommand.java
@@ -0,0 +1,180 @@
+package wellnus.gamification.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.gamification.GamificationManager;
+import wellnus.gamification.util.GamificationUi;
+
+/**
+ * Implementation of Gamification WellNus' help
command. Explains to the user what commands are supported
+ * by Gamification Feature and how to use each command.
+ */
+public class HelpCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "help - Get help on what commands can be used "
+ + "in WellNUS++ Gamification Feature";
+ public static final String COMMAND_USAGE = "usage: help [command-to-check]";
+ private static final String BAD_ARGUMENTS_MESSAGE = "Invalid arguments given to 'help'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'help'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "help command " + COMMAND_USAGE;
+ private static final String COMMAND_KEYWORD = "help";
+ private static final String NO_FEATURE_KEYWORD = "";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String HELP_PREAMBLE = "Input `help` to see all available commands." + LINE_SEPARATOR
+ + "Input `help [command-to-check]` to get usage help for a specific command." + LINE_SEPARATOR
+ + "Here are all the commands available for you!";
+ private static final String PADDING = " ";
+ private static final String DOT = ".";
+ private static final int ONE_OFFSET = 1;
+ private static final int EXPECTED_PAYLOAD_SIZE = 1;
+ private final GamificationUi gamificationUi;
+
+ /**
+ * Initialises a HelpCommand Object using the command arguments issued by the user.
+ *
+ * @param arguments Command arguments issued by the user
+ */
+ public HelpCommand(HashMap
+ * If the commandToSearch does not exist, help will print an unknown command
+ * error message.
+ */
+ public void printSpecificHelpMessage(String commandToSearch) {
+ switch (commandToSearch) {
+ case HelpCommand.COMMAND_KEYWORD:
+ printUsageMessage(HelpCommand.COMMAND_DESCRIPTION, HelpCommand.COMMAND_USAGE);
+ break;
+ case HomeCommand.COMMAND_KEYWORD:
+ printUsageMessage(HomeCommand.COMMAND_DESCRIPTION, HomeCommand.COMMAND_USAGE);
+ break;
+ case StatsCommand.COMMAND_KEYWORD:
+ printUsageMessage(StatsCommand.COMMAND_DESCRIPTION, StatsCommand.COMMAND_USAGE);
+ break;
+ default:
+ BadCommandException unknownCommand = new BadCommandException(COMMAND_INVALID_PAYLOAD);
+ gamificationUi.printErrorFor(unknownCommand, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+
+ private void printUsageMessage(String commandDescription, String usageString) {
+ String message = commandDescription + System.lineSeparator() + usageString;
+ gamificationUi.printOutputMessage(message);
+ }
+
+ @Override
+ protected String getCommandKeyword() {
+ return HelpCommand.COMMAND_KEYWORD;
+ }
+
+ @Override
+ protected String getFeatureKeyword() {
+ return HelpCommand.NO_FEATURE_KEYWORD;
+ }
+
+ /**
+ * Executes the issued help command.
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/gamification/command/HomeCommand.java b/src/main/java/wellnus/gamification/command/HomeCommand.java
new file mode 100644
index 0000000000..62d277d421
--- /dev/null
+++ b/src/main/java/wellnus/gamification/command/HomeCommand.java
@@ -0,0 +1,106 @@
+package wellnus.gamification.command;
+
+import java.util.HashMap;
+
+import wellnus.command.Command;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.WellNusException;
+import wellnus.gamification.util.GamificationUi;
+
+/**
+ * Provides the 'home' command for the gamification feature.
+ */
+public class HomeCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "home - Returns the user to the main WellNus++ session";
+ public static final String COMMAND_KEYWORD = "home";
+ public static final String COMMAND_USAGE = "usage: home";
+ public static final String FEATURE_NAME = "gamif";
+ private static final int NUM_OF_ARGUMENTS = 1;
+ private static final String WRONG_COMMAND_KEYWORD_MESSAGE = "Invalid command issued, expected 'home'!";
+ private static final String WRONG_ARGUMENTS_MESSAGE = "Invalid arguments given to 'home'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'home'!";
+
+ /**
+ * Initialises a Command Object to handle the 'home' command from the user
+ *
+ * @param arguments Arguments issued by the user
+ */
+ public HomeCommand(HashMap
+ *
+ *
+ * @param arguments Arguments given by the user
+ * @throws BadCommandException If the arguments have any issues
+ */
+ @Override
+ public void validateCommand(HashMap
+ *
+ * Each manager is in charge of 'managing' exactly one feature.
+ * For example, hb and reflect.
+ *
+ * MainCommands
,
+ * stored in supportedCommands
+ *
+ * Manager
(event drivers),
+ * stored in supportedManagers
+ *
+ *
+ *
+ * Internally, it sets up the following for convenience:
+ *
+ *
+ * This should be the part that contains the infinite loop and switch cases,
+ * but it is up to the implementer.
+ * Its implementation should include the following:
+ *
+ * Pass in a questionList object from ReflectionManager to access the list of favorite questions.
+ *
+ * @param arguments Argument-payload pairs from users
+ * @param questionList Object that contains the data about questions
+ */
+ public FavoriteCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+ /**
+ * Entry point to this command.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException invalidCommand) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(invalidCommand, INVALID_COMMAND_NOTES);
+ return;
+ }
+ if (!questionList.hasFavQuestions()) {
+ UI.printOutputMessage(EMPTY_FAV_LIST);
+ return;
+ }
+ try {
+ String outputString = questionList.getFavQuestions();
+ UI.printOutputMessage(outputString);
+ } catch (IndexOutOfBoundsException indexOutOfBoundsException) {
+ LOGGER.log(Level.WARNING, INDEX_OUT_OF_BOUND_MSG);
+ UI.printErrorFor(indexOutOfBoundsException, INVALID_COMMAND_NOTES);
+ }
+ }
+
+ /**
+ * Validate the command.
+ *
+ * Conditions for command to be valid:
+ *
+ * Pass in a questionList object from ReflectionManager to access the list of questions.
+ *
+ * @param arguments Argument-payload pairs from users
+ * @param questionList Object that contains the data about questions
+ */
+ public GetCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+ /**
+ * Entry point to this command.
+ * Trigger the generation of five random questions and print to users.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException invalidCommand) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(invalidCommand, INVALID_COMMAND_NOTES);
+ return;
+ }
+ try {
+ String outputString = convertQuestionsToString();
+ UI.printOutputMessage(outputString);
+ } catch (StorageException storageException) {
+ LOGGER.log(Level.WARNING, STORAGE_ERROR);
+ UI.printErrorFor(storageException, STORAGE_ERROR);
+ }
+ }
+
+ /**
+ * Validate the command.
+ *
+ * Conditions for command to be valid:
+ *
+ * Each number num: num >= 0 and num <= (maxSize - 1)
+ *
+ * @return The selected sets of random questions
+ */
+ public ArrayListhelp
command. Explains to the user what commands are supported
+ * by Reflection and how to use each command.
+ */
+
+public class HelpCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "help - Get help on what commands can be used "
+ + "in Reflection WellNUS++";
+ public static final String COMMAND_USAGE = "usage: help [command-to-check]";
+ private static final String BAD_ARGUMENTS_MESSAGE = "Invalid arguments given to 'help'!";
+ private static final String COMMAND_INVALID_PAYLOAD = "Invalid payload given to 'help'!";
+ private static final String COMMAND_INVALID_COMMAND_NOTE = "help command " + COMMAND_USAGE;
+ private static final String COMMAND_KEYWORD = "help";
+ private static final String NO_FEATURE_KEYWORD = "";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String HELP_PREAMBLE = "Input `help` to see all available commands." + LINE_SEPARATOR
+ + "Input `help [command-to-check] to get usage help for a specific command." + LINE_SEPARATOR
+ + "Here are all the commands available for you!";
+ private static final String PADDING = " ";
+ private static final String DOT = ".";
+ private static final int ONE_OFFSET = 1;
+ private static final int EXPECTED_PAYLOAD_SIZE = 1;
+ private final ReflectUi reflectUi;
+
+ /**
+ * Initialises a HelpCommand Object using the command arguments issued by the user.
+ *
+ * @param arguments Command arguments issued by the user
+ */
+ public HelpCommand(HashMap
+ * If the commandToSearch does not exist, help will print an unknown command
+ * error message.
+ */
+ public void printSpecificHelpMessage(String commandToSearch) {
+ switch (commandToSearch) {
+ case FavoriteCommand.COMMAND_KEYWORD:
+ printUsageMessage(FavoriteCommand.COMMAND_DESCRIPTION, FavoriteCommand.COMMAND_USAGE);
+ break;
+ case GetCommand.COMMAND_KEYWORD:
+ printUsageMessage(GetCommand.COMMAND_DESCRIPTION, GetCommand.COMMAND_USAGE);
+ break;
+ case HelpCommand.COMMAND_KEYWORD:
+ printUsageMessage(HelpCommand.COMMAND_DESCRIPTION, HelpCommand.COMMAND_USAGE);
+ break;
+ case HomeCommand.COMMAND_KEYWORD:
+ printUsageMessage(HomeCommand.COMMAND_DESCRIPTION, HomeCommand.COMMAND_USAGE);
+ break;
+ case LikeCommand.COMMAND_KEYWORD:
+ printUsageMessage(LikeCommand.COMMAND_DESCRIPTION, LikeCommand.COMMAND_USAGE);
+ break;
+ case UnlikeCommand.COMMAND_KEYWORD:
+ printUsageMessage(UnlikeCommand.COMMAND_DESCRIPTION, UnlikeCommand.COMMAND_USAGE);
+ break;
+ case PrevCommand.COMMAND_KEYWORD:
+ printUsageMessage(PrevCommand.COMMAND_DESCRIPTION, PrevCommand.COMMAND_USAGE);
+ break;
+ default:
+ BadCommandException unknownCommand = new BadCommandException(COMMAND_INVALID_PAYLOAD);
+ reflectUi.printErrorFor(unknownCommand, COMMAND_INVALID_COMMAND_NOTE);
+ }
+ }
+
+ private void printUsageMessage(String commandDescription, String usageString) {
+ String message = commandDescription + System.lineSeparator() + usageString;
+ reflectUi.printOutputMessage(message);
+ }
+
+ @Override
+ protected String getCommandKeyword() {
+ return HelpCommand.COMMAND_KEYWORD;
+ }
+
+ @Override
+ protected String getFeatureKeyword() {
+ return HelpCommand.NO_FEATURE_KEYWORD;
+ }
+
+ /**
+ * Executes the issued help command.
+ *
+ *
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+}
diff --git a/src/main/java/wellnus/reflection/command/HomeCommand.java b/src/main/java/wellnus/reflection/command/HomeCommand.java
new file mode 100644
index 0000000000..1ee809bbb7
--- /dev/null
+++ b/src/main/java/wellnus/reflection/command/HomeCommand.java
@@ -0,0 +1,140 @@
+package wellnus.reflection.command;
+
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.command.Command;
+import wellnus.common.WellNusLogger;
+import wellnus.exception.BadCommandException;
+import wellnus.reflection.feature.QuestionList;
+import wellnus.reflection.feature.ReflectUi;
+import wellnus.reflection.feature.ReflectionManager;
+
+//@@author wenxin-c
+/**
+ * Home command to return back to WellNUS++ main interface.
+ */
+public class HomeCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "home - Return back to the main menu of WellNUS++.";
+ public static final String COMMAND_USAGE = "usage: home";
+ public static final String COMMAND_KEYWORD = "home";
+ private static final Logger LOGGER = WellNusLogger.getLogger("ReflectHomeCommandLogger");
+ private static final String FEATURE_NAME = "reflect";
+ private static final String PAYLOAD = "";
+ private static final String INVALID_COMMAND_MSG = "Invalid command issued, expected 'home'!";
+ private static final String INVALID_ARGUMENT_MSG = "Invalid arguments given to 'home'!";
+ private static final String INVALID_PAYLOAD = "Invalid payload given to 'home'!";
+ private static final String INVALID_COMMAND_NOTES = "home command " + COMMAND_USAGE;
+ private static final String COMMAND_PAYLOAD_ASSERTION = "The payload should be empty.";
+ private static final String HOME_MESSAGE = "How do you feel after reflecting on yourself?"
+ + System.lineSeparator() + "Hope you have gotten some takeaways from self reflection, see you again!!";
+ private static final int ARGUMENT_PAYLOAD_SIZE = 1;
+ private static final ReflectUi UI = new ReflectUi();
+ private QuestionList questionList;
+
+ /**
+ * Set up the argument-payload pairs for this command.
+ * Pass in a questionList object from ReflectionManager to manipulate history data.
+ *
+ * @param arguments Argument-payload pairs from users
+ * @param questionList Object that contains the data about questions
+ */
+ public HomeCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+ /**
+ * Main entry point of this command.
+ * Return back to WellNUS++ main interface and clear the questionList history data.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException invalidCommand) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(invalidCommand, INVALID_COMMAND_NOTES);
+ return;
+ }
+ UI.printOutputMessage(HOME_MESSAGE);
+ if (!questionList.getRandomQuestionIndexes().isEmpty()) {
+ questionList.clearRandomQuestionIndexes();
+ }
+ ReflectionManager.setIsExit(true);
+ }
+
+ /**
+ * Validate the command.
+ *
+ * Conditions for command to be valid:
+ *
+ * Pass in a questionList object from ReflectionManager to access the indexes of the previous set of questions.
+ *
+ * @param arguments Argument-payload pairs from users
+ * @param questionList Object that contains the data about questions
+ */
+ public LikeCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+ /**
+ * Validate the command.
+ *
+ * Conditions for command to be valid:
+ *
+ * Check the validity of commands and add into favorite list.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException badCommandException) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(badCommandException, INVALID_COMMAND_NOTES);
+ return;
+ }
+ try {
+ addFavQuestion(getArguments().get(COMMAND_KEYWORD));
+ } catch (BadCommandException badCommandException) {
+ LOGGER.log(Level.INFO, MISSING_SET_QUESTIONS);
+ UI.printErrorFor(badCommandException, INVALID_COMMAND_NOTES);
+ } catch (TokenizerException tokenizerException) {
+ LOGGER.log(Level.WARNING, TOKENIZER_ERROR);
+ UI.printErrorFor(tokenizerException, TOKENIZER_ERROR);
+ } catch (StorageException storageException) {
+ LOGGER.log(Level.WARNING, STORAGE_ERROR);
+ UI.printErrorFor(storageException, STORAGE_ERROR);
+ } catch (NumberFormatException numberFormatException) {
+ LOGGER.log(Level.INFO, WRONG_INDEX_MSG);
+ BadCommandException exception = new BadCommandException(WRONG_INDEX_MSG);
+ UI.printErrorFor(exception, INVALID_COMMAND_NOTES);
+ } catch (ReflectionException reflectionException) {
+ UI.printErrorFor(reflectionException, INVALID_COMMAND_NOTES);
+ }
+ }
+
+ /**
+ * Add this index to favorite list and print the question to be added.
+ *
+ * A valid index will only be added(i.e. passed validateCommand()) if there is a set of questions gotten previously
+ *
+ * @param questionIndex User input of the index of question to be added to favorite list.
+ * @throws BadCommandException If there is not a set of question generated yet.
+ * @throws TokenizerException If there is error in tokenization of index
+ * @throws StorageException If there is error in storing the data
+ */
+ public void addFavQuestion(String questionIndex) throws BadCommandException, TokenizerException, StorageException,
+ NumberFormatException, ReflectionException {
+ int questionIndexInt = Integer.parseInt(questionIndex);
+ if (questionIndexInt > UPPER_BOUND || questionIndexInt < LOWER_BOUND) {
+ throw new ReflectionException(WRONG_INDEX_OUT_BOUND);
+ }
+ if (!questionList.hasRandomQuestionIndexes()) {
+ UI.printOutputMessage(MISSING_SET_QUESTIONS);
+ return;
+ }
+ IndexMapper indexMapper = new IndexMapper(this.randomQuestionIndexes);
+ HashMap
+ * Pass in a questionList object from ReflectionManager to access the indexes of the previous set of questions.
+ *
+ * @param arguments Argument-payload pairs from users
+ * @param questionList Object that contains the data about questions
+ */
+ public PrevCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+ /**
+ * Validate the command.
+ *
+ * Conditions for command to be valid:
+ *
+ * Check the validity of commands and add into favorite list.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException badCommandException) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(badCommandException, INVALID_COMMAND_NOTES);
+ return;
+ }
+ try {
+ getPrevSetQuestions();
+ } catch (IndexOutOfBoundsException indexOutOfBoundsException) {
+ LOGGER.log(Level.WARNING, INDEX_OUT_OF_BOUND_MSG);
+ UI.printErrorFor(indexOutOfBoundsException, INVALID_COMMAND_NOTES);
+ } catch (BadCommandException badCommandException) {
+ LOGGER.log(Level.WARNING, MISSING_SET_QUESTIONS);
+ UI.printErrorFor(badCommandException, INVALID_COMMAND_NOTES);
+ }
+ }
+
+ /**
+ * Get and print the previous set of question generated.
+ */
+ public void getPrevSetQuestions() throws BadCommandException {
+ if (!questionList.hasRandomQuestionIndexes()) {
+ UI.printOutputMessage(MISSING_SET_QUESTIONS);
+ return;
+ }
+ String prevSetQuestions = this.questionList.getPrevSetQuestions();
+ UI.printOutputMessage(prevSetQuestions);
+ }
+}
diff --git a/src/main/java/wellnus/reflection/command/UnlikeCommand.java b/src/main/java/wellnus/reflection/command/UnlikeCommand.java
new file mode 100644
index 0000000000..dc59bbfc3a
--- /dev/null
+++ b/src/main/java/wellnus/reflection/command/UnlikeCommand.java
@@ -0,0 +1,186 @@
+package wellnus.reflection.command;
+
+import java.util.HashMap;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import wellnus.command.Command;
+import wellnus.common.WellNusLogger;
+import wellnus.exception.BadCommandException;
+import wellnus.exception.ReflectionException;
+import wellnus.exception.StorageException;
+import wellnus.exception.TokenizerException;
+import wellnus.reflection.feature.IndexMapper;
+import wellnus.reflection.feature.QuestionList;
+import wellnus.reflection.feature.ReflectUi;
+
+//@@author wenxin-c
+/**
+ * Unlike command to remove reflection questions from favorite questions.
+ */
+public class UnlikeCommand extends Command {
+ public static final String COMMAND_DESCRIPTION = "unlike - Remove a particular question "
+ + "from favorite list.";
+ public static final String COMMAND_USAGE = "usage: unlike (index)";
+ public static final String COMMAND_KEYWORD = "unlike";
+ private static final String FEATURE_NAME = "reflect";
+ private static final String INVALID_COMMAND_MSG = "Invalid command issued, expected 'unlike'!";
+ private static final String INVALID_ARGUMENT_MSG = "Invalid arguments given to 'unlike'!";
+ private static final String INVALID_COMMAND_NOTES = "unlike command " + COMMAND_USAGE;
+ private static final String WRONG_INDEX_MSG = "Invalid index payload given to 'unlike', expected a valid integer!";
+ private static final String WRONG_INDEX_OUT_BOUND = "Invalid index payload given to 'unlike', "
+ + "index is out of range!";
+ private static final String EMPTY_FAV_LIST_MSG = "The favorite list is empty, there is nothing to be removed.";
+ private static final String TOKENIZER_ERROR = "Error tokenizing data!";
+ private static final String STORAGE_ERROR = "Error saving to storage!";
+ private static final int INDEX_ZERO = 0;
+ private static final int ARGUMENT_PAYLOAD_SIZE = 1;
+ private static final int LOWER_BOUND = 1;
+ private static final int EMPTY_LIST = 0;
+ private static final Logger LOGGER = WellNusLogger.getLogger("ReflectUnlikeCommandLogger");
+ private static final ReflectUi UI = new ReflectUi();
+ private Set
+ * Pass in a questionList object from ReflectionManager to access the indexes of the liked questions.
+ *
+ * @param arguments Argument-payload pairs from users
+ * @param questionList Object that contains the data about questions
+ */
+ public UnlikeCommand(HashMap
+ * "usage: add --name (name of habit)"
+ *
+ * @return String of the proper usage of the habit
+ */
+ @Override
+ public String getCommandUsage() {
+ return COMMAND_USAGE;
+ }
+
+ /**
+ * Method to ensure that developers add in a description for the command.
+ *
+ * "add - add a habit to your list"
+ *
+ * @return String of the description of what the command does
+ */
+ @Override
+ public String getCommandDescription() {
+ return COMMAND_DESCRIPTION;
+ }
+
+ /**
+ * Validate the command.
+ *
+ * Conditions for command to be valid:
+ *
+ * Check the validity of commands and add into favorite list.
+ */
+ @Override
+ public void execute() {
+ try {
+ validateCommand(getArguments());
+ } catch (BadCommandException badCommandException) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(badCommandException, INVALID_COMMAND_NOTES);
+ return;
+ }
+ try {
+ removeFavQuestion(getArguments().get(COMMAND_KEYWORD));
+ } catch (TokenizerException tokenizerException) {
+ LOGGER.log(Level.WARNING, TOKENIZER_ERROR);
+ UI.printErrorFor(tokenizerException, TOKENIZER_ERROR);
+ } catch (StorageException storageException) {
+ LOGGER.log(Level.WARNING, STORAGE_ERROR);
+ UI.printErrorFor(storageException, STORAGE_ERROR);
+ } catch (NumberFormatException numberFormatException) {
+ LOGGER.log(Level.INFO, WRONG_INDEX_MSG);
+ BadCommandException exception = new BadCommandException(WRONG_INDEX_MSG);
+ UI.printErrorFor(exception, INVALID_COMMAND_NOTES);
+ } catch (ReflectionException reflectionException) {
+ UI.printErrorFor(reflectionException, INVALID_COMMAND_NOTES);
+ } catch (BadCommandException badCommandException) {
+ LOGGER.log(Level.INFO, INVALID_COMMAND_MSG);
+ UI.printErrorFor(badCommandException, INVALID_COMMAND_NOTES);
+ }
+ }
+
+ /**
+ * Remove the user input index from favorite list and print the question to be removed.
+ *
+ * A valid index will only be removed(i.e. passed validateCommand()) if the favorite list in not empty.
+ *
+ * @param questionIndex User input of the index of question to be removed from favorite list.
+ * @throws TokenizerException If there is error in tokenization of index
+ * @throws StorageException If there is error in storing the data
+ * @throws NumberFormatException If invalid input is given, expected a valid integer
+ * @throws ReflectionException If fav list is empty
+ * @throws BadCommandException If an invalid command is given
+ */
+ public void removeFavQuestion(String questionIndex) throws TokenizerException, StorageException,
+ NumberFormatException, ReflectionException, BadCommandException {
+ int questionIndexInt = Integer.parseInt(questionIndex);
+ if (this.favQuestionIndexes.size() == EMPTY_LIST) {
+ UI.printOutputMessage(EMPTY_FAV_LIST_MSG);
+ return;
+ }
+ if (questionIndexInt > this.favQuestionIndexes.size() || questionIndexInt < LOWER_BOUND) {
+ throw new ReflectionException(WRONG_INDEX_OUT_BOUND);
+ }
+ IndexMapper indexMapper = new IndexMapper(this.favQuestionIndexes);
+ HashMap
+ * This function maps display index to each integer in the set.
+ *
+ * @return indexMap The hashmap with display index as key and real integer as value.
+ */
+ public HashMap
+ *
+ * This class calls methods to load the list of indexes of favorite questions from data file,
+ * and save the updated data into data file.
+ *
+ * It also stores the indexes of the previous set of questions(i.e. set of 5 random indexes)
+ * which will then be used for other commands.
+ */
+public class QuestionList {
+
+ // Questions are adopted from website: https://www.usa.edu/blog/self-discovery-questions/
+ private static final String[] QUESTIONS = {
+ "What are three of my most cherished personal values?",
+ "What is my purpose in life?",
+ "What is my personality type?",
+ "Did I make time for myself this week?",
+ "Am I making time for my social life?",
+ "What scares me the most right now?",
+ "What is something I find inspiring?",
+ "What is something that brings me joy?",
+ "When is the last time I gave back to others?",
+ "What matters to me most right now?"
+ };
+ private static final int TOTAL_NUM_QUESTIONS = 10;
+ private static final int RANDOM_NUMBER_UPPERBOUND = 10;
+ private static final int INDEX_ZERO = 0;
+ private static final int INDEX_ONE = 1;
+ private static final int INCREMENT_ONE = 1;
+ private static final String TOTAL_NUM_QUESTION_ASSERTIONS = "The total number of questions is 10.";
+ private static final String ADD_FAV_SUCCESS_ONE = "You have added question: ";
+ private static final String ADD_FAV_SUCCESS_TWO = " Into favorite list!!";
+ private static final String REMOVE_FAV_SUCCESS_ONE = "You have removed question: ";
+ private static final String REMOVE_FAV_SUCCESS_TWO = " From favorite list!!";
+ private static final String DUPLICATE_LIKE = " Is already in the favorite list!";
+ private static final String STORAGE_ERROR = "Error storing data!";
+ private static final String TOKENIZER_ERROR = "Previous reflect data will not be restored.";
+ private static final String DOT = ".";
+ private static final String EMPTY_STRING = "";
+ private static final String FILE_NAME = "reflect";
+ private static final String QUOTE = "\"";
+ private static final RandomNumberGenerator RANDOM_NUMBER_GENERATOR =
+ new RandomNumberGenerator(RANDOM_NUMBER_UPPERBOUND);
+ private static final Logger LOGGER = WellNusLogger.getLogger("ReflectQuestionListLogger");
+ private static final ReflectionTokenizer reflectionTokenizer = new ReflectionTokenizer();
+ private static final ReflectUi UI = new ReflectUi();
+ private static final boolean HAS_RANDOM_QUESTIONS = true;
+ private static final boolean NOT_HAS_RANDOM_QUESTIONS = false;
+ private static final boolean HAS_FAV_QUESTIONS = true;
+ private static final boolean NOT_HAS_FAV_QUESTIONS = false;
+ private ArrayList
+ *
+ * A valid index will only be added(i.e. passed validateCommand())
+ * if the question is not yet in the favorite list.
+ * Indexes of all favorite questions will be stored in data file every time a question is liked.
+ *
+ * @param indexToAdd The index of the question liked by user
+ * @throws StorageException If data fails to be stored properly.
+ */
+ public void addFavListIndex(int indexToAdd) throws StorageException {
+ if (this.dataIndex.get(INDEX_ZERO).contains(indexToAdd)) {
+ UI.printOutputMessage(QUOTE + questions.get(indexToAdd).toString() + QUOTE + DUPLICATE_LIKE);
+ return;
+ }
+ this.dataIndex.get(INDEX_ZERO).add(indexToAdd);
+ this.storeQuestionData();
+ UI.printOutputMessage(ADD_FAV_SUCCESS_ONE + QUOTE + this.questions.get(indexToAdd).toString() + QUOTE
+ + ADD_FAV_SUCCESS_TWO);
+ }
+
+ /**
+ * Remove the index of a liked question from the fav list.
+ *
+ * Indexes of all favorite questions will be stored in data file every time a question is removed.
+ *
+ * @param indexToRemove The index of question to be removed from fav list.
+ * @throws StorageException If data fails to be stored properly.
+ */
+ public void removeFavListIndex(int indexToRemove) throws StorageException {
+ this.dataIndex.get(INDEX_ZERO).remove(indexToRemove);
+ this.storeQuestionData();
+ UI.printOutputMessage(REMOVE_FAV_SUCCESS_ONE + QUOTE + this.questions.get(indexToRemove).toString() + QUOTE
+ + REMOVE_FAV_SUCCESS_TWO);
+ }
+
+ /**
+ * Check whether a set of random question has been generated by checking the size of the set of question indexes.
+ *
+ * @return True for non-empty set and false for empty set
+ */
+ public boolean hasRandomQuestionIndexes() {
+ if (this.randomQuestionIndexes.isEmpty()) {
+ return NOT_HAS_RANDOM_QUESTIONS;
+ } else {
+ return HAS_RANDOM_QUESTIONS;
+ }
+ }
+
+ /**
+ * Check whether there is a set of favorite questions by checking the size of the set of favorite question indexes.
+ *
+ * @return True for non-empty set and false for empty set
+ */
+ public boolean hasFavQuestions() {
+ if (this.dataIndex.get(INDEX_ZERO).isEmpty()) {
+ return NOT_HAS_FAV_QUESTIONS;
+ } else {
+ return HAS_FAV_QUESTIONS;
+ }
+ }
+
+ /**
+ * Get a string of all favorite questions based on the favorite question indexes.
+ *
+ * @return String of favorite questions
+ */
+ public String getFavQuestions() throws IndexOutOfBoundsException {
+ String questionString = EMPTY_STRING;
+ int displayIndex = INDEX_ONE;
+ for (int questionIndex : this.dataIndex.get(INDEX_ZERO)) {
+ questionString += (displayIndex + DOT + this.questions.get(questionIndex).toString()
+ + System.lineSeparator());
+ displayIndex += INCREMENT_ONE;
+ }
+ return questionString;
+ }
+
+ /**
+ * Get the previously generated set of questions. *
+ * @return String of previously generated questions */
+ public String getPrevSetQuestions() throws IndexOutOfBoundsException {
+ String questionString = EMPTY_STRING;
+ int displayIndex = INDEX_ONE;
+ for (int questionIndex : this.dataIndex.get(INDEX_ONE)) {
+ questionString += (displayIndex + DOT + this.questions.get(questionIndex).toString()
+ + System.lineSeparator());
+ displayIndex += INCREMENT_ONE;
+ }
+ return questionString;
+ }
+ //@@author
+}
+
diff --git a/src/main/java/wellnus/reflection/feature/RandomNumberGenerator.java b/src/main/java/wellnus/reflection/feature/RandomNumberGenerator.java
new file mode 100644
index 0000000000..8f84231c8a
--- /dev/null
+++ b/src/main/java/wellnus/reflection/feature/RandomNumberGenerator.java
@@ -0,0 +1,46 @@
+package wellnus.reflection.feature;
+
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+//@@author wenxin-c
+/**
+ * Generate a set of 5 distinct random integers ranging from 0 to 9
+ *
+ * This set of random numbers will be used as indexes to get a set of random questions.
+ */
+public class RandomNumberGenerator {
+ private static final String NUM_SELECTED_QUESTIONS_ASSERTION = "The number of selected questions should be 5.";
+ private static final int LOWER_BOUND = 0;
+ private static final int ONE_OFFSET = 1;
+ private static final int NUM_OF_RANDOM_NUMBERS = 5;
+ private int upperBound;
+
+ /**
+ * Constructor with the upper limit of the random number as an argument.
+ *
+ * @param upperBound The max value of the random number is (upperBound - 1)
+ */
+ public RandomNumberGenerator(int upperBound) {
+ this.upperBound = upperBound;
+ }
+
+ /**
+ * Generate a set of 5 random numbers.
+ *
+ * Each number num: num >= 0 and num <= (maxSize - 1)
+ *
+ * @return Set of 5 random numbers
+ */
+ public Set
+ * This class oversees the command execution for self reflection section.
+ */
+public class ReflectionManager extends Manager {
+ public static final String FEATURE_HELP_DESCRIPTION = "reflect - Self Reflection - Take some time to pause "
+ + "and reflect with our specially curated list of questions and reflection management tools.";
+ public static final String FEATURE_NAME = "reflect";
+ private static final Logger LOGGER = WellNusLogger.getLogger("ReflectionManagerLogger");
+ private static final String GET_COMMAND = "get";
+ private static final String HOME_COMMAND = "home";
+ private static final String HELP_COMMAND = "help";
+ private static final String LIKE_COMMAND = "like";
+ private static final String UNLIKE_COMMAND = "unlike";
+ private static final String FAV_COMMAND = "fav";
+ private static final String PREV_COMMAND = "prev";
+ private static final String NO_ELEMENT_MESSAGE = "There is no new line of input, please key in inputs!";
+ private static final String INVALID_COMMAND_MESSAGE = "Invalid command issued!";
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+ private static final String INVALID_COMMAND_NOTES =
+ "Supported commands in Self Reflection: " + LINE_SEPARATOR
+ + "get command " + GetCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "like command " + LikeCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "unlike command " + UnlikeCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "fav command " + FavoriteCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "prev command " + PrevCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "help command " + HelpCommand.COMMAND_USAGE + LINE_SEPARATOR
+ + "home command " + HomeCommand.COMMAND_USAGE;
+ private static final String COMMAND_TYPE_ASSERTION = "Command type should have length greater than 0";
+ private static final String ARGUMENT_PAYLOAD_ASSERTION = "Argument-payload pairs cannot be empty";
+ private static final String LOGO =
+ " ##### ###### \n"
+ + " # # ###### # ###### # # ###### ###### # ###### #### ##### \n"
+ + " # # # # # # # # # # # # # \n"
+ + " ##### ##### # ##### ###### ##### ##### # ##### # # \n"
+ + " # # # # # # # # # # # # \n"
+ + " # # # # # # # # # # # # # # \n"
+ + " ##### ###### ###### # # # ###### # ###### ###### #### # \n";
+ private static final String GREETING_MESSAGE = "Welcome to WellNUS++ Self Reflection section :D"
+ + System.lineSeparator() + "Feel very occupied and cannot find time to self reflect?"
+ + System.lineSeparator() + "No worries, this section will give you the opportunity to reflect "
+ + "and improve on yourself!!";
+ private static final int EMPTY_COMMAND = 0;
+ private static final boolean IS_EXIT_INITIAL = false;
+ private static final ReflectUi UI = new ReflectUi();
+ private static boolean isExit;
+ private String commandType;
+ private HashMap
+ * This is to be used to generate command.
+ *
+ * @param inputCommand Read from user input
+ * @throws BadCommandException If an invalid command was given
+ */
+ public void setArgumentPayload(String inputCommand) throws BadCommandException {
+ argumentPayload = commandParser.parseUserInput(inputCommand);
+ assert argumentPayload.size() > EMPTY_COMMAND : ARGUMENT_PAYLOAD_ASSERTION;
+ }
+
+ /**
+ * Set the main command type to determine which command to create.
+ *
+ * @param inputCommand Read from user input
+ * @throws BadCommandException If an invalid command was given
+ */
+ public void setCommandType(String inputCommand) throws BadCommandException {
+ commandType = commandParser.getMainArgument(inputCommand);
+ assert commandType.length() > EMPTY_COMMAND : COMMAND_TYPE_ASSERTION;
+ }
+
+ //@@author wenxin-c
+
+ /**
+ * Print greeting logo and message.
+ */
+ public void greet() {
+ UI.printLogoWithSeparator(LOGO);
+ UI.printOutputMessage(GREETING_MESSAGE);
+ }
+ //@@author
+
+ //@@author wenxin-c
+
+ /**
+ * Main entry point of self reflection section.
+ *
+ * It prints out greeting messages, listen to and execute user commands.
+ */
+ @Override
+ public void runEventDriver() {
+ setIsExit(false);
+ this.greet();
+ while (!isExit) {
+ try {
+ String inputCommand = UI.getCommand();
+ setCommandType(inputCommand);
+ setArgumentPayload(inputCommand);
+ executeCommands();
+ } catch (NoSuchElementException noSuchElement) {
+ LOGGER.log(Level.INFO, NO_ELEMENT_MESSAGE);
+ UI.printErrorFor(noSuchElement, NO_ELEMENT_MESSAGE);
+ } catch (BadCommandException badCommand) {
+ LOGGER.log(Level.INFO, badCommand.getMessage());
+ UI.printErrorFor(badCommand, INVALID_COMMAND_NOTES);
+ }
+ }
+ }
+ //@@author
+
+ /**
+ * Decide which command to create based on command type.
+ *
+ * Commands available at this moment are:
+ *
+ *
+ *
+ * @throws BadCommandException If an invalid command was given
+ */
+ public void executeCommands() throws BadCommandException {
+ assert commandType.length() > EMPTY_COMMAND : COMMAND_TYPE_ASSERTION;
+ switch (commandType) {
+ case GET_COMMAND:
+ GetCommand getQuestionsCmd = new GetCommand(argumentPayload, questionList);
+ getQuestionsCmd.execute();
+ break;
+ case HELP_COMMAND:
+ HelpCommand helpCmd = new HelpCommand(argumentPayload);
+ helpCmd.execute();
+ break;
+ case HOME_COMMAND:
+ HomeCommand returnCmd = new HomeCommand(argumentPayload, questionList);
+ returnCmd.execute();
+ break;
+ case LIKE_COMMAND:
+ LikeCommand likeCmd = new LikeCommand(argumentPayload, questionList);
+ likeCmd.execute();
+ break;
+ case UNLIKE_COMMAND:
+ UnlikeCommand unlikeCmd = new UnlikeCommand(argumentPayload, questionList);
+ unlikeCmd.execute();
+ break;
+ case FAV_COMMAND:
+ FavoriteCommand favCmd = new FavoriteCommand(argumentPayload, questionList);
+ favCmd.execute();
+ break;
+ case PREV_COMMAND:
+ PrevCommand prevCmd = new PrevCommand(argumentPayload, questionList);
+ prevCmd.execute();
+ break;
+ default:
+ throw new BadCommandException(INVALID_COMMAND_MESSAGE);
+ }
+ }
+}
+
diff --git a/src/main/java/wellnus/reflection/feature/ReflectionQuestion.java b/src/main/java/wellnus/reflection/feature/ReflectionQuestion.java
new file mode 100644
index 0000000000..fb7d1d4ea3
--- /dev/null
+++ b/src/main/java/wellnus/reflection/feature/ReflectionQuestion.java
@@ -0,0 +1,32 @@
+package wellnus.reflection.feature;
+
+//@@author wenxin-c
+/**
+ * ReflectQuestion class is used to create reflect question objects.
+ */
+public class ReflectionQuestion {
+ private static final int EMPTY_QUESTION = 0;
+ private static final String EMPTY_QUESTION_MSG = "Question description cannot be empty.";
+ private String questionDescription;
+
+ /**
+ * Constructor to create a ReflectionQuestion object and initialise question description.
+ *
+ * @param questionDescription The reflection question description
+ */
+ public ReflectionQuestion(String questionDescription) {
+ assert questionDescription.length() > EMPTY_QUESTION : EMPTY_QUESTION_MSG;
+ this.questionDescription = questionDescription;
+ }
+
+ /**
+ * Convert each reflect question to a string to be printed.
+ *
+ * @return Question description with its status
+ */
+ @Override
+ public String toString() {
+ return questionDescription;
+ }
+}
+
diff --git a/src/main/java/wellnus/storage/AtomicHabitTokenizer.java b/src/main/java/wellnus/storage/AtomicHabitTokenizer.java
new file mode 100644
index 0000000000..1b92dc1990
--- /dev/null
+++ b/src/main/java/wellnus/storage/AtomicHabitTokenizer.java
@@ -0,0 +1,126 @@
+package wellnus.storage;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+import wellnus.atomichabit.feature.AtomicHabit;
+import wellnus.exception.TokenizerException;
+
+/**
+ * Class to tokenize and detokenize the AtomicHabit list.
+ */
+public class AtomicHabitTokenizer implements Tokenizer
+ * Each habit will be tokenized with the following format:
+ * --description [description of habit] --count [count of habit].
+ *
+ * @param habitsToTokenize List of atomic habits to be tokenized as ArrayList of strings.
+ * @return ArrayList of Strings representing the tokenized habits that we can write to storage.
+ */
+ public ArrayList
+ * This method can be called in the constructor of AtomicHabitManager to detokenize.
+ * ArrayList of atomic habits from storage.
+ *
+ * @param tokenizedAtomicHabits List of tokenized atomic habit strings from the storage.
+ * @return ArrayList containing all the atomic habit saved in the storage.
+ * @throws TokenizerException When the data can't be detokenized.
+ */
+ public ArrayListGamificationManager
into a String representation to be
+ * saved to storage.
+ *
+ * @param dataObjects List of GamificationData Objects we want to convert into a String representation
+ * @return ArrayList of Strings representing the GamificationData objects that we can write to storage
+ */
+ @Override
+ public ArrayListGamificationManager
's state back into an
+ * ArrayList
of GamificationData that can be used to restore the gamification feature's
+ * previous state.
+ *
+ * @param tokenizedDataObjects String representation of the GamificationData Objects whose state we want to restore
+ * @return ArrayList containing all the gamification data from the gamification feature's previously saved state
+ * @throws TokenizerException If detokenizing fails and stored gamification statistics cannot be restored
+ */
+ @Override
+ public ArrayList
+ */
+public class ReflectionTokenizer implements Tokenizer