diff --git a/src/main/java/duke/command/ByeCommand.java b/src/main/java/duke/command/ByeCommand.java index 8e9e71a942..0acdbdf44f 100644 --- a/src/main/java/duke/command/ByeCommand.java +++ b/src/main/java/duke/command/ByeCommand.java @@ -2,17 +2,23 @@ import java.io.IOException; +import duke.exceptions.UnknownCommandException; import duke.inputoutput.DukeCliSettings; import duke.inputoutput.DukeIo; +import duke.util.ParsedData; import duke.util.Storage; import duke.util.TaskList; /** * Command class that exit the program. When bye is entered */ -public class ByeCommand implements Command { +public class ByeCommand extends NoParamCommand { private static final String OUTRO = "Pff.. Not like I want to see you again"; + public ByeCommand(ParsedData data) { + super(data); + } + /** * Returns true when asked if program should exit. * @@ -27,9 +33,11 @@ public boolean isExit() { * {@inheritDoc} Prints goodbye message and exits program. * * @throws IOException raised if an error occured when saving + * @throws UnknownCommandException when extra parameters is included */ @Override - public void execute(TaskList tasks, DukeIo io, Storage storage) throws IOException { + public void execute(TaskList tasks, DukeIo io, Storage storage) throws IOException, UnknownCommandException { + checkSingleArgumentGuard(); io.printTask(OUTRO, DukeCliSettings.WRAP_INDENT); storage.saveTasks(tasks); } diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java index c25c380ed6..4ef6bdf583 100644 --- a/src/main/java/duke/command/ListCommand.java +++ b/src/main/java/duke/command/ListCommand.java @@ -1,13 +1,19 @@ package duke.command; +import duke.exceptions.UnknownCommandException; import duke.inputoutput.DukeIo; +import duke.util.ParsedData; import duke.util.Storage; import duke.util.TaskList; /** * Command to list out all the current tasks. */ -public class ListCommand implements Command { +public class ListCommand extends NoParamCommand { + + public ListCommand(ParsedData data) { + super(data); + } /** * {@inheritDoc} List command does not exit @@ -19,9 +25,12 @@ public boolean isExit() { /** * Prints out all the current tasks added. + * + * @throws UnknownCommandException when extra parameters is included */ @Override - public void execute(TaskList tasks, DukeIo io, Storage storage) { + public void execute(TaskList tasks, DukeIo io, Storage storage) throws UnknownCommandException { + checkSingleArgumentGuard(); io.printNumberedList(tasks.getTasks()); } diff --git a/src/main/java/duke/command/NoParamCommand.java b/src/main/java/duke/command/NoParamCommand.java new file mode 100644 index 0000000000..104540371d --- /dev/null +++ b/src/main/java/duke/command/NoParamCommand.java @@ -0,0 +1,29 @@ +package duke.command; + +import duke.exceptions.UnknownCommandException; +import duke.util.ParsedData; + +/** + * Abstract class to handle commands that requires no argument + */ +abstract class NoParamCommand implements Command { + + private boolean invalid; + + NoParamCommand(ParsedData data) { + if (data.description.isEmpty()) { + invalid = false; + } else { + invalid = true; + } + } + + /** + * Guards to ensure commands contains no extra parameters + */ + protected void checkSingleArgumentGuard() throws UnknownCommandException { + if (invalid) { + throw new UnknownCommandException(); + } + } +} diff --git a/src/main/java/duke/command/ResetAliasCommand.java b/src/main/java/duke/command/ResetAliasCommand.java index a795b8f919..9e70370955 100644 --- a/src/main/java/duke/command/ResetAliasCommand.java +++ b/src/main/java/duke/command/ResetAliasCommand.java @@ -2,16 +2,23 @@ import java.io.IOException; +import duke.exceptions.UnknownCommandException; import duke.inputoutput.DukeIo; +import duke.util.ParsedData; import duke.util.Storage; import duke.util.TaskList; /** * Command class that resets set aliases */ -public class ResetAliasCommand implements Command { +public class ResetAliasCommand extends NoParamCommand { + private static final String OUTRO = "Back to beginning!"; + public ResetAliasCommand(ParsedData data) { + super(data); + } + /** * Returns true when asked if program should exit. * @@ -26,9 +33,11 @@ public boolean isExit() { * {@inheritDoc} Resets all added aliases. * * @throws IOException raised if an error occured when saving + * @throws UnknownCommandException when extra parameters is included */ @Override - public void execute(TaskList tasks, DukeIo io, Storage storage) throws IOException { + public void execute(TaskList tasks, DukeIo io, Storage storage) throws IOException, UnknownCommandException { + checkSingleArgumentGuard(); io.printTask(OUTRO); CommandSelector.reset(); } diff --git a/src/main/java/duke/command/SwapFaceCommand.java b/src/main/java/duke/command/SwapFaceCommand.java index 7ff94d86fe..f1188dd0c3 100644 --- a/src/main/java/duke/command/SwapFaceCommand.java +++ b/src/main/java/duke/command/SwapFaceCommand.java @@ -6,9 +6,11 @@ import duke.exceptions.GuiOnlyException; import duke.exceptions.ImageDownloadFailedException; import duke.exceptions.OperatonIsStillRunningException; +import duke.exceptions.UnknownCommandException; import duke.gui.GuiDataController; import duke.inputoutput.DukeGuiIo; import duke.inputoutput.DukeIo; +import duke.util.ParsedData; import duke.util.Storage; import duke.util.TaskList; import javafx.concurrent.Task; @@ -17,7 +19,7 @@ /** * Command to list out all the current tasks. */ -public class SwapFaceCommand implements Command { +public class SwapFaceCommand extends NoParamCommand { private static final String RESPONSE = "I'm gonna replace us! We are not real after all! Goodbye!"; private static final String SUCCESS = "Nice to meet you, I am duke."; private static final String ANIME_FAKE_IMAGE = "https://www.thiswaifudoesnotexist.net/example-%d.jpg"; @@ -25,6 +27,10 @@ public class SwapFaceCommand implements Command { private static boolean isRunning = false; + public SwapFaceCommand(ParsedData data) { + super(data); + } + /** * {@inheritDoc} List command does not exit */ @@ -35,10 +41,16 @@ public boolean isExit() { /** * Prints out all the current tasks added. + * + * @throws GuiOnlyException when called via CLI mode + * @throws OperatonIsStillRunningException when another swap face command is running + * @throws UnknownCommandException when extra parameters is included */ @Override public void execute(TaskList tasks, DukeIo io, Storage storage) - throws GuiOnlyException, OperatonIsStillRunningException { + throws GuiOnlyException, OperatonIsStillRunningException, UnknownCommandException { + + checkSingleArgumentGuard(); if (!(io instanceof DukeGuiIo)) { throw new GuiOnlyException(); } diff --git a/src/main/java/duke/inputoutput/DukeAbstractIo.java b/src/main/java/duke/inputoutput/DukeAbstractIo.java index 1b50f085aa..9827feeb30 100644 --- a/src/main/java/duke/inputoutput/DukeAbstractIo.java +++ b/src/main/java/duke/inputoutput/DukeAbstractIo.java @@ -104,6 +104,11 @@ public void printError(Exception e) { printTask(String.format("🙄 OOPS!!! %s", e.getMessage())); } + @Override + public void printError(String msg) { + printError(new Exception(msg)); + } + protected boolean isBitFlag(int bitsValue, DukeCliSettings flagEnum) { return (bitsValue & flagEnum.value) == flagEnum.value; } diff --git a/src/main/java/duke/inputoutput/DukeIo.java b/src/main/java/duke/inputoutput/DukeIo.java index b432ff55a8..7f0dc9c054 100644 --- a/src/main/java/duke/inputoutput/DukeIo.java +++ b/src/main/java/duke/inputoutput/DukeIo.java @@ -44,6 +44,13 @@ public interface DukeIo { */ void printError(Exception e); + /** + * Prints the message in the format of an exception + * + * @param msg message to print + */ + void printError(String msg); + /** * Prints Text with selected features * diff --git a/src/main/java/duke/main/Duke.java b/src/main/java/duke/main/Duke.java index a1ccf1b7c7..38eb5945cf 100644 --- a/src/main/java/duke/main/Duke.java +++ b/src/main/java/duke/main/Duke.java @@ -16,8 +16,8 @@ */ public class Duke { private static final String LOGO = - "Welcome to\n" + " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n" + " Chatbot!\n"; + "Welcome to\n" + " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n" + " Chatbot!\n"; private static final String INTRO = "Hey hey hey! I'm Duke\n" + "What can I do for you?"; @@ -48,9 +48,11 @@ public boolean handleInput(String txt) { c.execute(tasks, userInputOutput, dukeData); } catch (DukeException e) { userInputOutput.printError(e); + return true; } catch (IOException e) { userInputOutput.printError(e); - return true; + userInputOutput.printError(FATAL_EXIT); + return false; } return !c.isExit(); @@ -84,18 +86,7 @@ public static Duke createApplication() { * @return returns an instance of Duke */ public static Duke createApplication(DukeIo userIo) { - Storage dukeData; - TaskList tasks; - try { - dukeData = Storage.createStorage(); - tasks = new TaskList(dukeData.readFile()); - } catch (IOException e) { - userIo.printError(e); - userIo.printTask(FATAL_EXIT); - return null; - } - - return new Duke(tasks, dukeData, userIo); + return createApplication(userIo, ""); } /** @@ -110,10 +101,10 @@ public static Duke createApplication(DukeIo userIo, String filePath) { TaskList tasks; try { dukeData = Storage.createStorage(filePath); - tasks = new TaskList(dukeData.readFile()); + tasks = new TaskList(dukeData.readFile(userIo)); } catch (IOException e) { userIo.printError(e); - userIo.printTask(FATAL_EXIT); + userIo.printError(FATAL_EXIT); return null; } diff --git a/src/main/java/duke/util/DataParser.java b/src/main/java/duke/util/DataParser.java index 374c0ae686..bdb4a4eddf 100644 --- a/src/main/java/duke/util/DataParser.java +++ b/src/main/java/duke/util/DataParser.java @@ -71,9 +71,13 @@ public static Command dataToCommand(ParsedData data) { switch (CommandSelector.getCs().getCommand(data.command)) { case BYE: - return new ByeCommand(); + return new ByeCommand(data); case LIST: - return new ListCommand(); + return new ListCommand(data); + case SWAP: + return new SwapFaceCommand(data); + case RESETALIAS: + return new ResetAliasCommand(data); case MARK: return new MarkCommand(data); case UNMARK: @@ -94,10 +98,6 @@ public static Command dataToCommand(ParsedData data) { return new AliasCommand(data); case DELETECOMMAND: return new DeleteAliasCommand(data); - case SWAP: - return new SwapFaceCommand(); - case RESETALIAS: - return new ResetAliasCommand(); case INVALID: default: return new InvalidCommand(); diff --git a/src/main/java/duke/util/Storage.java b/src/main/java/duke/util/Storage.java index 728f4a7d7f..5398b18455 100644 --- a/src/main/java/duke/util/Storage.java +++ b/src/main/java/duke/util/Storage.java @@ -9,6 +9,7 @@ import java.util.Scanner; import duke.exceptions.CorruptedLineException; +import duke.inputoutput.DukeIo; import duke.task.Task; /** @@ -17,6 +18,7 @@ public class Storage { private static final String DEFAULT_SAVE_PATH = "data/SavedData.duke"; + private static final String PARSE_FAIL = "I was unable to parse line %s of the save file!"; private File file; private Storage(File file) { @@ -31,12 +33,11 @@ private Storage(File file) { * @throws IOException Throws if pathing cannot exist. */ public static Storage createStorage(String path) throws IOException { - File newFile = new File(path); - File parentFolder = newFile.getParentFile(); - if (parentFolder != null) { - parentFolder.mkdir(); + if (path.trim().isEmpty()) { + path = DEFAULT_SAVE_PATH; } - newFile.createNewFile(); + File newFile = new File(path); + ensureExistance(newFile); return new Storage(newFile); } @@ -50,13 +51,24 @@ public static Storage createStorage() throws IOException { return createStorage(DEFAULT_SAVE_PATH); } + private static void ensureExistance(File file) throws IOException { + if (file.exists()) { + return; + } + File parentFolder = file.getParentFile(); + if (parentFolder != null) { + parentFolder.mkdirs(); + } + file.createNewFile(); + } + /** * Read the save file and convert it to a list of Task. * * @return List of Tasks * @throws FileNotFoundException Throws when save file does not exist */ - public List readFile() throws FileNotFoundException { + public List readFile(DukeIo io) throws FileNotFoundException { List ret = new ArrayList<>(); List corruptedLines = new ArrayList<>(); @@ -75,6 +87,16 @@ public List readFile() throws FileNotFoundException { } } sc.close(); + if (corruptedLines.size() == 0) { + return ret; + } + StringBuilder joinedList = corruptedLines.stream().collect(StringBuilder::new, (sb, num) -> { + sb.append(num); + sb.append(", "); + }, StringBuilder::append); + // removes the ", " of the last string + joinedList.setLength(joinedList.length() - 2); + io.printError(String.format(PARSE_FAIL, joinedList.toString())); return ret; } @@ -85,9 +107,7 @@ public List readFile() throws FileNotFoundException { * @throws IOException Throws when save file doesn't exist */ public void saveData(ParsedData[] dataList) throws IOException { - if (!file.exists()) { - file.createNewFile(); - } + ensureExistance(file); assert file.exists(); StringBuilder sb = new StringBuilder(); @@ -117,9 +137,7 @@ public void saveTasks(TaskList tl) throws IOException { * @throws IOException Throws when save file is missing */ public void saveTask(Task task) throws IOException { - if (!file.exists()) { - file.createNewFile(); - } + ensureExistance(file); assert file.exists(); FileWriter fw = new FileWriter(file, true);