diff --git a/.gitignore b/.gitignore index 2873e189e..1c6cf4efc 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT +src/META-INF/MANIFEST.MF diff --git a/data/lia.txt b/data/lia.txt new file mode 100644 index 000000000..e69de29bb diff --git a/docs/README.md b/docs/README.md index 47b9f984f..f52d162a3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,58 @@ -# Duke User Guide +# πŸŽ‰ Welcome to Lia, Your Task-Handling Superstar! πŸŽ‰ -// Update the title above to match the actual product name +Hey there, fellow task wrangler! Meet **Lia**, your trusty chatbot sidekick here to help you conquer the chaos of your to-do list with a sprinkle of fun! Whether you're drowning in deadlines or just need a friendly reminder, Lia's got your back. Let’s dive into how you can make the most of your new digital friend! -// Product screenshot goes here +## Getting Started πŸš€ -// Product intro goes here +1. **Installation:** + - Ensure that you have Java installed on your machine. β˜•οΈ + - Download the JAR file from GitHub. πŸ™ + - Navigate to the project directory. πŸ“‚ -## Adding deadlines +2. **Running the Chatbot:** + - Open a command window in the project folder. πŸ’» + - Run the command: `java -jar Lia.jar` (ensure the JAR file is correctly built and available). πŸ“¦ -// Describe the action and its outcome. +3. **Interacting with Lia:** + - Once Lia is running, you can enter commands as outlined in the features section. πŸ’¬ + - Lia will respond accordingly and keep track of your tasks. πŸ“‹ -// Give examples of usage -Example: `keyword (optional arguments)` +## πŸš€ Features -// A description of the expected outcome goes here +### 1. **Say Hello! πŸ‘‹** +When you first meet Lia, she’ll greet you with enthusiasm and charm. Just type anything to get the conversation started, and she’ll respond with her signature flair! -``` -expected output -``` +### 2. **Task Management Made Easy! πŸ—‚οΈ** +With Lia, you can effortlessly manage your tasks: +- **Add a To-Do:** + Just type `todo ` (e.g., `todo Read a book`) and watch Lia add it to your list! + +- **Set a Deadline:** + Need to remember to finish something by a certain date? Type `deadline /by ` (e.g., `deadline Return book /by 2023-12-01`) and Lia will save it with a deadline! -## Feature ABC +- **Plan an Event:** + Planning something special? Use `event /from /to ` (e.g., `event Project meeting /from 2023-12-01 10:00 /to 2023-12-01 11:00`) to keep your calendar organized. -// Feature details +### 3. **Check Your List! πŸ“** +Wanna see all your tasks? Just type `list`, and Lia will present you with a beautifully formatted list of everything you’ve got going on! +### 4. **Mark Tasks as Done! βœ”οΈ** +Finished something? Simply type `mark ` to give yourself a well-deserved pat on the back! If you change your mind, you can unmark it with `unmark `. Lia will cheer you on either way! -## Feature XYZ +### 5. **Delete Tasks with Style! πŸ—‘οΈ** +Got a task that’s no longer relevant? Type `delete `, and Lia will handle the rest, reminding you of what you’ve let go! -// Feature details \ No newline at end of file +### 6. **Find Your Tasks! πŸ”** +Looking for something specific? Type `find ` (e.g., `find book`), and Lia will fetch all related tasks for you. It's like a treasure hunt, but for your to-do list! + +### 7. **Say Goodbye! πŸ‘‹** +When you’re done, just type `bye`, and Lia will bid you farewell with a warm goodbye message. She’s always here when you need her! + +## 🌟 Conclusion + +Lia isn’t just a chatbot; she’s your new task management best friend. With her help, you can tackle anything life throws your way, one task at a time. Remember, the key to success is staying organizedβ€”and Lia is here to help you every step of the way! + +--- + +Now get out there and start conquering those tasks! 🎊 diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/Lia.java b/src/main/java/Lia.java new file mode 100644 index 000000000..9235d365f --- /dev/null +++ b/src/main/java/Lia.java @@ -0,0 +1,65 @@ +import exception.LiaException; +import UI.Ui; +import storage.Storage; +import task.TaskList; +import commands.Command; +import parser.Parser; + +/** + * Represents the main application for the Lia chatbot. + * This application manages tasks and facilitates user interactions + * through a command-line interface. + */ +public class Lia { + private Storage storage; + private TaskList tasks; + private Ui ui; + + /** + * Constructs a Lia chatbot instance with the specified file path for storage. + * + * @param filePath The path to the file where tasks will be loaded from or saved to. + */ + public Lia(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (LiaException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } + } + + /** + * Runs the chatbot, displaying the welcome message and continuously + * accepting user input until an exit command is given. + */ + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.printLine(); // show the divider line + Command command = Parser.parse(fullCommand); + command.execute(tasks, ui, storage); + storage.save(tasks.getTasks()); + isExit = command.isExit(); + } catch (LiaException e) { + ui.showError(e.getMessage()); + } finally { + ui.printLine(); + } + } + } + + /** + * The main method that starts the Lia chatbot application. + * + * @param args Command line arguments (not used). + */ + public static void main(String[] args) { + new Lia("./data/lia.txt").run(); + } +} diff --git a/src/main/java/UI/Ui.java b/src/main/java/UI/Ui.java new file mode 100644 index 000000000..1d0517a04 --- /dev/null +++ b/src/main/java/UI/Ui.java @@ -0,0 +1,135 @@ +package UI; + +import java.util.Scanner; +import task.Task; +import task.TaskList; + +/** + * Handles interactions with the user. + *

+ * This class provides methods to display messages to the user, read user input, and format the output for a better user experience. + */ +public class Ui { + private static final String INDENTATION = " "; + + /** + * Displays a welcome message and the logo of the chatbot. + */ + public void showWelcome() { + String LOGO = """ + β–ˆβ–ˆβ–“ β–ˆβ–ˆβ–“ β–„β–„β–„ + β–“β–ˆβ–ˆβ–’ β–“β–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–„ + β–’β–ˆβ–ˆβ–‘ β–’β–ˆβ–ˆβ–’β–’β–ˆβ–ˆ β–€β–ˆβ–„ + β–’β–ˆβ–ˆβ–‘ β–‘β–ˆβ–ˆβ–‘β–‘β–ˆβ–ˆβ–„β–„β–„β–„β–ˆβ–ˆ + β–‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–‘β–ˆβ–ˆβ–‘ β–“β–ˆ β–“β–ˆβ–ˆβ–’ + β–‘ β–’β–‘β–“ β–‘β–‘β–“ β–’β–’ β–“β–’β–ˆβ–‘ + β–‘ β–‘ β–’ β–‘ β–’ β–‘ β–’ β–’β–’ β–‘ + β–‘ β–‘ β–’ β–‘ β–‘ β–’ + β–‘ β–‘ β–‘ β–‘ β–‘ + """; + System.out.println(INDENTATION + "Hello there! I'm Lia, your cheerful task management buddy!\n" + LOGO); + System.out.println(INDENTATION + "What exciting tasks can I help you with today?"); + printLine(); + } + + /** + * Reads a command from the user. + * + * @return The command input by the user as a trimmed string. + */ + public String readCommand() { + Scanner scanner = new Scanner(System.in); + return scanner.nextLine().trim(); + } + + /** + * Prints a line separator for better readability. + */ + public void printLine() { + System.out.println(INDENTATION + "___________________________________________________________"); + } + + /** + * Displays a message indicating that a task has been added. + * + * @param task The task that was added. + * @param taskCount The current number of tasks in the list. + */ + public void addTaskAndPrint(Task task, int taskCount) { + System.out.println(INDENTATION + "Hooray! I've added this fabulous task:"); + System.out.println(INDENTATION + task.toString()); + System.out.println(INDENTATION + "Now you have " + taskCount + " tasks in your super organized list!"); + } + + /** + * Displays a message indicating that a task has been deleted. + * + * @param task The task that was removed. + * @param taskCount The current number of tasks in the list. + */ + public void showDeleteTask(Task task, int taskCount) { + System.out.println(INDENTATION + "Oops! I've removed this task for you:"); + System.out.println(INDENTATION + task.toString()); + System.out.println(INDENTATION + "Now you have " + taskCount + " tasks left to conquer!"); + } + + + /** + * Prints all tasks in the provided TaskList. + * + * @param tasks The TaskList containing tasks to be printed. + */ + public void printTasks(TaskList tasks) { + if (tasks.isEmpty()) { + System.out.println(INDENTATION + "Oops! I couldn't find any tasks matching your search."); + } else { + System.out.println(INDENTATION + "Here are the tasks in your list:"); + for (int i = 0; i < tasks.size(); i++) { + System.out.println(INDENTATION + (i + 1) + "." + tasks.get(i).toString()); + } + } + } + + /** + * Displays a message indicating that a task has been marked as done. + * + * @param task The task that was marked as done. + */ + public void showMarkDone(Task task) { + System.out.println(INDENTATION + "Good Job! I've marked this task as done:"); + System.out.println(INDENTATION + task.toString()); + } + + /** + * Displays a message indicating that a task has been unmarked. + * + * @param task The task that was unmarked. + */ + public void showUnmarkDone(Task task) { + System.out.println(INDENTATION + "No worries! I've unmarked this task:"); + System.out.println(INDENTATION + task.toString()); + } + + /** + * Displays a farewell message when the application is exiting. + */ + public void showFarewell() { + System.out.println(INDENTATION + "Bye for now! Hope to see you again soon, superstar!"); + } + + /** + * Displays an error message. + * + * @param message The error message to display. + */ + public void showError(String message) { + System.out.println(INDENTATION + message); + } + + /** + * Displays a loading error message when tasks cannot be loaded from the file. + */ + public void showLoadingError() { + System.out.println(INDENTATION + "Error loading tasks from file."); + } +} diff --git a/src/main/java/commands/AddCommand.java b/src/main/java/commands/AddCommand.java new file mode 100644 index 000000000..b2ca1de4a --- /dev/null +++ b/src/main/java/commands/AddCommand.java @@ -0,0 +1,75 @@ +package commands; + +import task.TaskList; +import task.Task; +import task.ToDo; +import task.Deadline; +import task.Event; +import UI.Ui; +import storage.Storage; +import exception.LiaException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Command to add a task. + */ +public class AddCommand extends Command { + private final String taskDetails; + + /** + * Constructs an AddCommand with the specified task details. + * + * @param taskDetails The details of the task to be added. + */ + public AddCommand(String taskDetails) { + this.taskDetails = taskDetails; + } + + /** + * Executes the command to add a task to the task list. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + * @throws LiaException if an error occurs during task creation. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws LiaException { + Task newTask; + + if (taskDetails.startsWith("todo")) { + newTask = new ToDo(taskDetails.substring(5).trim()); + } else if (taskDetails.startsWith("deadline")) { + String[] details = taskDetails.split(" /by "); + LocalDateTime by = parseDateTime(details[1].trim()); + newTask = new Deadline(details[0].substring(9).trim(), by); + } else if (taskDetails.startsWith("event")) { + String[] details = taskDetails.split(" /from "); + String[] timeDetails = details[1].split(" /to "); + LocalDateTime start = parseDateTime(timeDetails[0].trim()); + LocalDateTime end = parseDateTime(timeDetails[1].trim()); + newTask = new Event(details[0].substring(6).trim(), start, end); + } else { + throw new LiaException("Invalid task type."); + } + + tasks.add(newTask); + ui.addTaskAndPrint(newTask, tasks.size()); + } + + /** + * Parses a date and time string into a LocalDateTime object. + * + * @param dateTimeString The date and time string to parse. + * @return The corresponding LocalDateTime object. + * @throws LiaException if the date/time format is invalid. + */ + private LocalDateTime parseDateTime(String dateTimeString) throws LiaException { + try { + return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm")); + } catch (Exception e) { + throw new LiaException("Invalid date format. Please use yyyy-MM-dd HHmm format."); + } + } +} diff --git a/src/main/java/commands/Command.java b/src/main/java/commands/Command.java new file mode 100644 index 000000000..4d3f3ee1e --- /dev/null +++ b/src/main/java/commands/Command.java @@ -0,0 +1,30 @@ +package commands; + +import task.TaskList; +import UI.Ui; +import storage.Storage; +import exception.LiaException; + +/** + * Represents a command that can be executed. + */ +public abstract class Command { + /** + * Executes the command. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + * @throws LiaException if an error occurs during execution. + */ + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws LiaException; + + /** + * Checks if the command is an exit command. + * + * @return True if the command is an exit command, false otherwise. + */ + public boolean isExit() { + return false; // Default implementation; can be overridden. + } +} diff --git a/src/main/java/commands/DeleteCommand.java b/src/main/java/commands/DeleteCommand.java new file mode 100644 index 000000000..0f2955288 --- /dev/null +++ b/src/main/java/commands/DeleteCommand.java @@ -0,0 +1,43 @@ +package commands; + +import task.TaskList; +import task.Task; +import UI.Ui; +import storage.Storage; +import exception.LiaException; + +/** + * Command to delete a task. + */ +public class DeleteCommand extends Command { + private final String taskNumberStr; + + /** + * Constructs a DeleteCommand with the specified task number string. + * + * @param taskNumberStr The task number of the task to be deleted. + */ + public DeleteCommand(String taskNumberStr) { + this.taskNumberStr = taskNumberStr; + } + + /** + * Executes the command to delete a task from the task list. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + * @throws LiaException if the task number is invalid. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws LiaException { + int taskIndex = Integer.parseInt(taskNumberStr) - 1; + + if (taskIndex < 0 || taskIndex >= tasks.size()) { + throw new LiaException("Oops! Task number " + taskNumberStr + " does not exist."); + } + + Task task = tasks.remove(taskIndex); + ui.showDeleteTask(task, tasks.size()); + } +} diff --git a/src/main/java/commands/ExitCommand.java b/src/main/java/commands/ExitCommand.java new file mode 100644 index 000000000..45af418c6 --- /dev/null +++ b/src/main/java/commands/ExitCommand.java @@ -0,0 +1,32 @@ +package commands; + +import task.TaskList; +import UI.Ui; +import storage.Storage; + +/** + * Command to exit the application. + */ +public class ExitCommand extends Command { + /** + * Executes the command to exit the application. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.showFarewell(); + } + + /** + * Checks if the command is an exit command. + * + * @return True, as this is an exit command. + */ + @Override + public boolean isExit() { + return true; // This is an exit command. + } +} diff --git a/src/main/java/commands/FindCommand.java b/src/main/java/commands/FindCommand.java new file mode 100644 index 000000000..8e4e3a79d --- /dev/null +++ b/src/main/java/commands/FindCommand.java @@ -0,0 +1,41 @@ +package commands; + +import task.Task; +import task.TaskList; +import UI.Ui; +import storage.Storage; + + +/** + * Command to find tasks based on a keyword. + */ +public class FindCommand extends Command { + private final String keyword; + + /** + * Constructs a FindCommand with the specified keyword. + * + * @param keyword The keyword to search for in tasks. + */ + public FindCommand(String keyword) { + this.keyword = keyword.toLowerCase(); // Normalize to lowercase for case-insensitive search + } + + /** + * Executes the command to find tasks containing the keyword. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + TaskList matchingTasks = new TaskList(); + for (Task task : tasks.getTasks()) { + if (task.toString().toLowerCase().contains(keyword)) { + matchingTasks.add(task); + } + } + ui.printTasks(matchingTasks); + } +} diff --git a/src/main/java/commands/ListCommand.java b/src/main/java/commands/ListCommand.java new file mode 100644 index 000000000..a1e44c6db --- /dev/null +++ b/src/main/java/commands/ListCommand.java @@ -0,0 +1,23 @@ +package commands; + +import task.TaskList; +import UI.Ui; +import storage.Storage; + +/** + * Command to list all tasks. + */ +public class ListCommand extends Command { + + /** + * Executes the command to list all tasks. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.printTasks(tasks); + } +} diff --git a/src/main/java/commands/MarkCommand.java b/src/main/java/commands/MarkCommand.java new file mode 100644 index 000000000..8d8ac72f4 --- /dev/null +++ b/src/main/java/commands/MarkCommand.java @@ -0,0 +1,52 @@ +package commands; + +import task.TaskList; +import task.Task; +import UI.Ui; +import storage.Storage; +import exception.LiaException; + +/** + * Command to mark or unmark a task. + */ +public class MarkCommand extends Command { + private final String taskNumberStr; + private final boolean markDone; + + /** + * Constructs a MarkCommand with the specified task number string and mark status. + * + * @param taskNumberStr The task number of the task to be marked/unmarked. + * @param markDone True to mark the task as done, false to unmark it. + */ + public MarkCommand(String taskNumberStr, boolean markDone) { + this.taskNumberStr = taskNumberStr; + this.markDone = markDone; + } + + /** + * Executes the command to mark or unmark a task. + * + * @param tasks The list of tasks to manage. + * @param ui The user interface for interactions. + * @param storage The storage for saving and loading tasks. + * @throws LiaException if the task number is invalid. + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws LiaException { + int taskIndex = Integer.parseInt(taskNumberStr) - 1; + + if (taskIndex < 0 || taskIndex >= tasks.size()) { + throw new LiaException("Oops! Task number " + taskNumberStr + " does not exist."); + } + + Task task = tasks.get(taskIndex); + if (markDone) { + task.markAsDone(); + ui.showMarkDone(task); + } else { + task.markAsNotDone(); + ui.showUnmarkDone(task); + } + } +} diff --git a/src/main/java/exception/LiaException.java b/src/main/java/exception/LiaException.java new file mode 100644 index 000000000..e5ed92034 --- /dev/null +++ b/src/main/java/exception/LiaException.java @@ -0,0 +1,16 @@ +package exception; + +/** + * Represents exceptions specific to the Lia chatbot. + */ +public class LiaException extends Exception { + + /** + * Creates a new LiaException with a specified error message. + * + * @param message The error message to be displayed. + */ + public LiaException(String message) { + super(message); + } +} diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java new file mode 100644 index 000000000..86dff0854 --- /dev/null +++ b/src/main/java/parser/Parser.java @@ -0,0 +1,72 @@ +package parser; + +import exception.LiaException; +import commands.Command; +import commands.AddCommand; +import commands.DeleteCommand; +import commands.MarkCommand; +import commands.ExitCommand; +import commands.ListCommand; +import commands.FindCommand; + +/** + * Parses user input commands and creates corresponding command objects. + */ +public class Parser { + + /** + * Parses the given command string and returns the appropriate command object. + * + * @param commandString The user input command as a string. + * @return A Command object corresponding to the user input. + * @throws LiaException if the command is invalid or improperly formatted. + */ + public static Command parse(String commandString) throws LiaException { + String[] parts = commandString.split(" ", 2); + String command = parts[0]; + + switch (command.toLowerCase()) { + case "todo": + if (parts.length < 2 || parts[1].isBlank()) { + throw new LiaException("Oops! The description of a todo cannot be empty."); + } + return new AddCommand(commandString); + case "deadline": + if (parts.length < 2 || !parts[1].contains("/by")) { + throw new LiaException("Oops! The deadline command requires a description and '/by' followed by a date."); + } + return new AddCommand(commandString); + case "event": + if (parts.length < 2 || !parts[1].contains("/from") || !parts[1].contains("/to")) { + throw new LiaException("Oops! The event command requires a description, '/from', and '/to' followed by times."); + } + return new AddCommand(commandString); + case "mark": + if (parts.length < 2) { + throw new LiaException("Oops! You must specify a task number to mark."); + } + return new MarkCommand(parts[1], true); + case "unmark": + if (parts.length < 2) { + throw new LiaException("Oops! You must specify a task number to unmark."); + } + return new MarkCommand(parts[1], false); + case "delete": + if (parts.length < 2) { + throw new LiaException("Oops! You must specify a task number to delete."); + } + return new DeleteCommand(parts[1]); + case "find": + if (parts.length < 2) { + throw new LiaException("Oops! You must specify a keyword to find."); + } + return new FindCommand(parts[1]); + case "list": + return new ListCommand(); + case "bye": + return new ExitCommand(); + default: + throw new LiaException("Oops! I don't recognize that command."); + } + } +} diff --git a/src/main/java/storage/Storage.java b/src/main/java/storage/Storage.java new file mode 100644 index 000000000..ea145bf3b --- /dev/null +++ b/src/main/java/storage/Storage.java @@ -0,0 +1,118 @@ +package storage; + +import task.Task; +import task.Event; +import task.ToDo; +import task.Deadline; +import exception.LiaException; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.io.FileWriter; +import java.io.IOException; +import java.io.BufferedWriter; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Handles loading and saving tasks from/to a file. + */ +public class Storage { + private final String filePath; + + /** + * Constructs a Storage object with the specified file path. + * + * @param filePath The path to the file where tasks are stored. + */ + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Loads tasks from the file and returns them as a list. + * + * @return An ArrayList of tasks loaded from the file. + * @throws LiaException if there is an error loading tasks or if the file format is invalid. + */ + public ArrayList load() throws LiaException { + ArrayList tasks = new ArrayList<>(); + Path path = Paths.get(filePath); + + try { + if (!Files.exists(path)) { + // Create directories and file if it does not exist + Files.createDirectories(path.getParent()); + Files.createFile(path); + } else { + List lines = Files.readAllLines(path); + for (String line : lines) { + try { + String[] data = line.split(" \\| "); + switch (data[0]) { + case "T": + if (data.length != 3) throw new LiaException("Invalid ToDo format."); + ToDo todo = new ToDo(data[2]); + if (data[1].equals("1")) todo.markAsDone(); + tasks.add(todo); + break; + case "D": + if (data.length != 4) throw new LiaException("Invalid Deadline format."); + Deadline deadline = new Deadline(data[2], parseDateTime(data[3])); + if (data[1].equals("1")) deadline.markAsDone(); + tasks.add(deadline); + break; + case "E": + if (data.length != 5) throw new LiaException("Invalid Event format."); + Event event = new Event(data[2], parseDateTime(data[3]), parseDateTime(data[4])); + if (data[1].equals("1")) event.markAsDone(); + tasks.add(event); + break; + default: + System.out.println("Warning: Unrecognized task type in file. Skipping line."); + } + } catch (Exception e) { + System.out.println("Warning: Corrupted data in file. Skipping line: " + line); + } + } + } + } catch (Exception e) { + throw new LiaException("Error loading tasks from file."); + } + return tasks; + } + + /** + * Parses a date and time string into a LocalDateTime object. + * + * @param dateTimeString The date and time string to parse. + * @return The corresponding LocalDateTime object. + * @throws LiaException if the date/time format is invalid. + */ + private LocalDateTime parseDateTime(String dateTimeString) throws LiaException { + try { + return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm")); + } catch (Exception e) { + throw new LiaException("Invalid date format. Please use yyyy-MM-dd HHmm format."); + } + } + + /** + * Saves the current tasks to the file. + * + * @param tasks The list of tasks to save. + */ + public void save(ArrayList tasks) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) { + for (Task task : tasks) { + writer.write(task.toFileFormat()); + writer.newLine(); + } + } catch (IOException e) { + System.out.println("Error saving tasks to file: " + e.getMessage()); + } + } +} diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java new file mode 100644 index 000000000..c8afad77b --- /dev/null +++ b/src/main/java/task/Deadline.java @@ -0,0 +1,44 @@ +package task; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Represents a Deadline task, which is a task that needs to be done + * before a specific date/time. + */ +public class Deadline extends Task { + protected LocalDateTime by; + + /** + * Creates a new Deadline task with the given description and deadline. + * + * @param description A description of the task. + * @param by The deadline by which the task must be completed. + */ + public Deadline(String description, LocalDateTime by) { + super(description); + this.by = by; + } + + /** + * Returns the task information formatted for saving to a file. + * + * @return The Deadline task as a formatted string suitable for saving to a file. + */ + @Override + public String toFileFormat() { + return "D | " + (isDone ? "1" : "0") + " | " + description + " | " + by.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm")); + } + + /** + * Returns a string representation of the Deadline task. + * + * @return A string representing the Deadline task, including its status, + * description, and deadline in a readable format. + */ + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + "[" + by.format(DateTimeFormatter.ofPattern("MMM d yyyy HH:mm")) + "]" + ")"; + } +} diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java new file mode 100644 index 000000000..8f0cb90b8 --- /dev/null +++ b/src/main/java/task/Event.java @@ -0,0 +1,53 @@ +package task; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Represents an Event task, which is a task that starts and ends + * at specific dates/times. + */ +public class Event extends Task { + protected LocalDateTime start; + protected LocalDateTime end; + + /** + * Creates a new Event task with the given description, start, and end times. + * + * @param description A description of the event. + * @param start The start time of the event. + * @param end The end time of the event. + */ + public Event(String description, LocalDateTime start, LocalDateTime end) { + super(description); + this.start = start; + this.end = end; + } + + /** + * Returns the task information formatted for saving to a file. + * + * @return The Event task as a formatted string suitable for saving to a file, + * including its status, description, start time, and end time. + */ + @Override + public String toFileFormat() { + return "E | " + (isDone ? "1" : "0") + " | " + description + " | " + + start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm")) + " | " + + end.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm")); + } + + /** + * Returns a string representation of the Event task. + * + * @return A string representing the Event task, including its status, + * description, start time, and end time in a readable format. + */ + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + + "[" + start.format(DateTimeFormatter.ofPattern("MMM d yyyy HH:mm")) + "]" + + " to: " + + "[" + end.format(DateTimeFormatter.ofPattern("MMM d yyyy HH:mm")) + "]" + ")"; + } +} diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java new file mode 100644 index 000000000..f7b657eb8 --- /dev/null +++ b/src/main/java/task/Task.java @@ -0,0 +1,65 @@ +package task; + +/** + * Represents a task with a description and completion status (done or not done). + */ +public class Task { + protected String description; + protected boolean isDone; + + /** + * Creates a new Task with the given description. + * By default, the task is marked as not done. + * + * @param description A description of the task. + */ + public Task(String description) { + this.description = description; + this.isDone = false; // By default, a task is not done when created + } + + /** + * Returns the status icon of the task. + * "X" indicates that the task is done, and " " indicates that it is not done. + * + * @return A string representing the status icon of the task. + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); // "X" for done, " " for not done + } + + /** + * Marks the task as done. + */ + public void markAsDone() { + this.isDone = true; + } + + /** + * Returns the task information formatted for saving to a file. + * + * @return A string representation of the task formatted for file storage, + * or null if the implementation is not provided. + */ + public String toFileFormat() { + return null; // Override in subclasses for specific formatting + } + + /** + * Marks the task as not done. + */ + public void markAsNotDone() { + this.isDone = false; + } + + /** + * Returns a string representation of the task, + * including its status and description. + * + * @return A string representing the task in the format "[status_icon] description". + */ + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + description; + } +} \ No newline at end of file diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java new file mode 100644 index 000000000..738c3edd9 --- /dev/null +++ b/src/main/java/task/TaskList.java @@ -0,0 +1,84 @@ +package task; + +import java.util.ArrayList; + +/** + * Contains a list of tasks and methods to manipulate the list. + */ +public class TaskList { + private final ArrayList tasks; + + /** + * Initializes a new empty TaskList. + */ + public TaskList() { + this.tasks = new ArrayList<>(); + } + + /** + * Initializes a TaskList with the given tasks. + * + * @param tasks An ArrayList of Task objects to initialize the TaskList. + */ + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + /** + * Adds a task to the task list. + * + * @param task The Task object to be added to the list. + */ + public void add(Task task) { + tasks.add(task); + } + + /** + * Removes a task from the task list by index. + * + * @param index The index of the task to remove. + * @return The Task object that was removed from the list. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= size()). + */ + public Task remove(int index) { + return tasks.remove(index); + } + + /** + * Retrieves a task from the task list by index. + * + * @param index The index of the task to retrieve. + * @return The Task object at the specified index. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= size()). + */ + public Task get(int index) { + return tasks.get(index); + } + + /** + * Returns the number of tasks in the task list. + * + * @return The size of the task list. + */ + public int size() { + return tasks.size(); + } + + /** + * Checks if the task list is empty. + * + * @return True if the task list contains no tasks, false otherwise. + */ + public boolean isEmpty() { + return tasks.isEmpty(); + } + + /** + * Returns the list of tasks in the task list. + * + * @return An ArrayList containing the Task objects in the task list. + */ + public ArrayList getTasks() { + return tasks; + } +} diff --git a/src/main/java/task/ToDo.java b/src/main/java/task/ToDo.java new file mode 100644 index 000000000..0cd791a6a --- /dev/null +++ b/src/main/java/task/ToDo.java @@ -0,0 +1,41 @@ +package task; + +/** + * Represents a Todo task which is a task without any specific date or time. + */ +public class ToDo extends Task { + + /** + * Creates a new ToDo task with the specified description. + * + * @param description A description of the ToDo task. + */ + public ToDo(String description) { + super(description); + } + + /** + * Returns the task information formatted for saving to a file. + *

+ * This format is suitable for storage in a persistent format. + * + * @return A formatted string representing the ToDo task for saving to a file. + */ + @Override + public String toFileFormat() { + return "T | " + (isDone ? "1" : "0") + " | " + description; + } + + /** + * Returns a string representation of the ToDo task. + *

+ * This representation includes the task's status and description, formatted + * for user visibility. + * + * @return A string representing the ToDo task, including its status. + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } +} \ No newline at end of file diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..23ef35626 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,67 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| + ___________________________________________________________ + Hello! I'm + β–ˆβ–ˆβ–“ β–ˆβ–ˆβ–“ β–„β–„β–„ + β–“β–ˆβ–ˆβ–’ β–“β–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–„ + β–’β–ˆβ–ˆβ–‘ β–’β–ˆβ–ˆβ–’β–’β–ˆβ–ˆ β–€β–ˆβ–„ + β–’β–ˆβ–ˆβ–‘ β–‘β–ˆβ–ˆβ–‘β–‘β–ˆβ–ˆβ–„β–„β–„β–„β–ˆβ–ˆ + β–‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–‘β–ˆβ–ˆβ–‘ β–“β–ˆ β–“β–ˆβ–ˆβ–’ + β–‘ β–’β–‘β–“ β–‘β–‘β–“ β–’β–’ β–“β–’β–ˆβ–‘ + β–‘ β–‘ β–’ β–‘ β–’ β–‘ β–’ β–’β–’ β–‘ + β–‘ β–‘ β–’ β–‘ β–‘ β–’ + β–‘ β–‘ β–‘ β–‘ β–‘ + What can I do for you? + ___________________________________________________________ + ___________________________________________________________ + Got it. I've added this task: + [T][ ] read book + Now you have 1 tasks in the list. + ___________________________________________________________ + ___________________________________________________________ + Here are the tasks in your list: + 1.[T][ ] read book + ___________________________________________________________ + ___________________________________________________________ + Got it. I've added this task: + [D][ ] return book (by: Sunday) + Now you have 2 tasks in the list. + ___________________________________________________________ + ___________________________________________________________ + Here are the tasks in your list: + 1.[T][ ] read book + 2.[D][ ] return book (by: Sunday) + ___________________________________________________________ + ___________________________________________________________ + Got it. I've added this task: + [E][ ] project meeting (from: Mon 2pm to: 4pm) + Now you have 3 tasks in the list. + ___________________________________________________________ + ___________________________________________________________ + Here are the tasks in your list: + 1.[T][ ] read book + 2.[D][ ] return book (by: Sunday) + 3.[E][ ] project meeting (from: Mon 2pm to: 4pm) + ___________________________________________________________ + ___________________________________________________________ + Nice! I've marked this task as done: + [T][X] read book + ___________________________________________________________ + ___________________________________________________________ + Here are the tasks in your list: + 1.[T][X] read book + 2.[D][ ] return book (by: Sunday) + 3.[E][ ] project meeting (from: Mon 2pm to: 4pm) + ___________________________________________________________ + ___________________________________________________________ + OK, I've marked this task as not done yet: + [T][ ] read book + ___________________________________________________________ + ___________________________________________________________ + Here are the tasks in your list: + 1.[T][ ] read book + 2.[D][ ] return book (by: Sunday) + 3.[E][ ] project meeting (from: Mon 2pm to: 4pm) + ___________________________________________________________ + ___________________________________________________________ + Bye. Hope to see you again soon! + ___________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..77bed956a 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,11 @@ +todo read book +list +deadline return book /by Sunday +list +event project meeting /from Mon 2pm /to 4pm +list +mark 1 +list +unmark 1 +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..1fb20a1f0 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin Lia < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh deleted file mode 100644 index c9ec87003..000000000 --- a/text-ui-test/runtest.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -# create bin directory if it doesn't exist -if [ ! -d "../bin" ] -then - mkdir ../bin -fi - -# delete output from previous run -if [ -e "./ACTUAL.TXT" ] -then - rm ACTUAL.TXT -fi - -# compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java -then - echo "********** BUILD FAILURE **********" - exit 1 -fi - -# run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT - -# convert to UNIX format -cp EXPECTED.TXT EXPECTED-UNIX.TXT -dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT - -# compare the output to the expected output -diff ACTUAL.TXT EXPECTED-UNIX.TXT -if [ $? -eq 0 ] -then - echo "Test result: PASSED" - exit 0 -else - echo "Test result: FAILED" - exit 1 -fi \ No newline at end of file